예제 #1
0
def TestOneInput(data):
  fdp = atheris.FuzzedDataProvider(data)
  whttp.parse_content_range_header(fdp.ConsumeUnicode(100))
  whttp.parse_range_header(fdp.ConsumeUnicode(100))
  whttp.parse_set_header(fdp.ConsumeUnicode(100))
  whttp.parse_etags(fdp.ConsumeUnicode(100))
  whttp.parse_if_range_header(fdp.ConsumeUnicode(100))
  whttp.parse_dict_header(fdp.ConsumeUnicode(100))
예제 #2
0
    def database_item(database_id, item_id, suffix, session_id):
        """
        """

        range_header = request.headers.get("Range", None)

        if range_header:
            begin, end = http.parse_range_header(range_header).ranges[0]
            data, mimetype, total_length = provider.get_item(
                session_id, database_id, item_id, byte_range=(begin, end))
            begin, end = (begin or 0), (end or total_length)

            # Setup response
            response = Response(
                data, 206, mimetype=mimetype,
                direct_passthrough=not isinstance(data, basestring))
            response.headers["Content-Range"] = "bytes %d-%d/%d" % (
                begin, end - 1, total_length)
            response.headers["Content-Length"] = end - begin
        else:
            data, mimetype, total_length = provider.get_item(
                session_id, database_id, item_id)

            # Setup response
            response = Response(
                data, 200, mimetype=mimetype,
                direct_passthrough=not isinstance(data, basestring))
            response.headers["Content-Length"] = total_length

        return response
예제 #3
0
def read_range(request, read_data: Callable[[int, int], bytes]) -> None:
    """
    Read an optional ``Range`` header, reads data appropriately via the given
    callable, writes the data to the request.

    Only parses a subset of ``Range`` headers that we support: must be set,
    bytes only, only a single range, the end must be explicitly specified.
    Raises a ``_HTTPError(http.REQUESTED_RANGE_NOT_SATISFIABLE)`` if parsing is
    not possible or the header isn't set.

    Takes a function that will do the actual reading given the start offset and
    a length to read.

    The resulting data is written to the request.
    """
    if request.getHeader("range") is None:
        # Return the whole thing.
        start = 0
        while True:
            # TODO should probably yield to event loop occasionally...
            # https://tahoe-lafs.org/trac/tahoe-lafs/ticket/3872
            data = read_data(start, start + 65536)
            if not data:
                request.finish()
                return
            request.write(data)
            start += len(data)

    range_header = parse_range_header(request.getHeader("range"))
    if (
        range_header is None  # failed to parse
        or range_header.units != "bytes"
        or len(range_header.ranges) > 1  # more than one range
        or range_header.ranges[0][1] is None  # range without end
    ):
        raise _HTTPError(http.REQUESTED_RANGE_NOT_SATISFIABLE)

    offset, end = range_header.ranges[0]

    # TODO limit memory usage
    # https://tahoe-lafs.org/trac/tahoe-lafs/ticket/3872
    data = read_data(offset, end - offset)

    request.setResponseCode(http.PARTIAL_CONTENT)
    if len(data):
        # For empty bodies the content-range header makes no sense since
        # the end of the range is inclusive.
        request.setHeader(
            "content-range",
            ContentRange("bytes", offset, offset + len(data)).to_header(),
        )
    request.write(data)
    request.finish()
예제 #4
0
    def test_range_parsing():
        rv = http.parse_range_header("bytes=52")
        assert rv is None

        rv = http.parse_range_header("bytes=52-")
        assert rv.units == "bytes"
        assert rv.ranges == [(52, None)]
        assert rv.to_header() == "bytes=52-"

        rv = http.parse_range_header("bytes=52-99")
        assert rv.units == "bytes"
        assert rv.ranges == [(52, 100)]
        assert rv.to_header() == "bytes=52-99"

        rv = http.parse_range_header("bytes=52-99,-1000")
        assert rv.units == "bytes"
        assert rv.ranges == [(52, 100), (-1000, None)]
        assert rv.to_header() == "bytes=52-99,-1000"

        rv = http.parse_range_header("bytes = 1 - 100")
        assert rv.units == "bytes"
        assert rv.ranges == [(1, 101)]
        assert rv.to_header() == "bytes=1-100"

        rv = http.parse_range_header("AWesomes=0-999")
        assert rv.units == "awesomes"
        assert rv.ranges == [(0, 1000)]
        assert rv.to_header() == "awesomes=0-999"
예제 #5
0
    def test_range_parsing():
        rv = http.parse_range_header('bytes=52')
        assert rv is None

        rv = http.parse_range_header('bytes=52-')
        assert rv.units == 'bytes'
        assert rv.ranges == [(52, None)]
        assert rv.to_header() == 'bytes=52-'

        rv = http.parse_range_header('bytes=52-99')
        assert rv.units == 'bytes'
        assert rv.ranges == [(52, 100)]
        assert rv.to_header() == 'bytes=52-99'

        rv = http.parse_range_header('bytes=52-99,-1000')
        assert rv.units == 'bytes'
        assert rv.ranges == [(52, 100), (-1000, None)]
        assert rv.to_header() == 'bytes=52-99,-1000'

        rv = http.parse_range_header('bytes = 1 - 100')
        assert rv.units == 'bytes'
        assert rv.ranges == [(1, 101)]
        assert rv.to_header() == 'bytes=1-100'

        rv = http.parse_range_header('AWesomes=0-999')
        assert rv.units == 'awesomes'
        assert rv.ranges == [(0, 1000)]
        assert rv.to_header() == 'awesomes=0-999'
예제 #6
0
    def test_range_parsing():
        rv = http.parse_range_header('bytes=52')
        assert rv is None

        rv = http.parse_range_header('bytes=52-')
        assert rv.units == 'bytes'
        assert rv.ranges == [(52, None)]
        assert rv.to_header() == 'bytes=52-'

        rv = http.parse_range_header('bytes=52-99')
        assert rv.units == 'bytes'
        assert rv.ranges == [(52, 100)]
        assert rv.to_header() == 'bytes=52-99'

        rv = http.parse_range_header('bytes=52-99,-1000')
        assert rv.units == 'bytes'
        assert rv.ranges == [(52, 100), (-1000, None)]
        assert rv.to_header() == 'bytes=52-99,-1000'

        rv = http.parse_range_header('bytes = 1 - 100')
        assert rv.units == 'bytes'
        assert rv.ranges == [(1, 101)]
        assert rv.to_header() == 'bytes=1-100'

        rv = http.parse_range_header('AWesomes=0-999')
        assert rv.units == 'awesomes'
        assert rv.ranges == [(0, 1000)]
        assert rv.to_header() == 'awesomes=0-999'
예제 #7
0
def get_download_response(payload,
                          content_length,
                          content_format,
                          filename,
                          request=None):
    """
    :param payload: File like object.
    :param content_length: Size of payload in bytes
    :param content_format: ``couchexport.models.Format`` instance
    :param filename: Name of the download
    :param request: The request. Used to determine if a range response should be given.
    :return: HTTP response
    """
    ranges = None
    if request and "HTTP_RANGE" in request.META:
        try:
            ranges = parse_range_header(request.META['HTTP_RANGE'],
                                        content_length)
        except ValueError:
            pass

    if ranges and len(ranges.ranges) != 1:
        ranges = None

    response = StreamingHttpResponse(content_type=content_format.mimetype)
    if content_format.download:
        response['Content-Disposition'] = safe_filename_header(filename)

    response["Content-Length"] = content_length
    response["Accept-Ranges"] = "bytes"

    if ranges:
        start, stop = ranges.ranges[0]
        if stop is not None and stop > content_length:
            # requested range not satisfiable
            return HttpResponse(status=416)

        response.streaming_content = RangedFileWrapper(payload,
                                                       start=start,
                                                       stop=stop
                                                       or float("inf"))
        end = stop or content_length
        response["Content-Range"] = "bytes %d-%d/%d" % (start, end - 1,
                                                        content_length)
        response["Content-Length"] = end - start
        response.status_code = 206
    else:
        response.streaming_content = FileWrapper(payload)

    return response
예제 #8
0
파일: views.py 프로젝트: sdoom/shapps
def sharedir_download(dname, rpath):
    rootpath = os.path.abspath(settings.SHAREDIR.directories.get(dname))
    if not rootpath:
        raise NotFound
    apath = os.path.abspath(os.path.join(rootpath, rpath))
    if (not apath.startswith(rootpath)) or (not os.path.isfile(apath)):
        raise NotFound

    def _opener(filename):
        return (open(filename, 'rb'),
                datetime.utcfromtimestamp(os.path.getmtime(filename)),
                int(os.path.getsize(filename)))

    guessed_type = mimetypes.guess_type(apath)
    mime_type = guessed_type[0] or 'application/octet-stream'

    headers = []
    headers.append(('Content-Type', mime_type))

    if request.range:
        range = request.range
    else:
        range = parse_range_header(request.environ.get('HTTP_RANGE'))
    #when request range,only recognize "bytes" as range units
    if range and range.units == "bytes":
        rbegin, rend = range.ranges[0]
        try:
            fsize = os.path.getsize(apath)
        except OSError as e:
            return Response("Not found", status=404)
        if (rbegin + 1) < fsize:
            if rend == None:
                rend = fsize - 1
            headers.append(('Content-Length', str(rend - rbegin + 1)))
            headers.append(
                ('Content-Range',
                 '%s %d-%d/%d' % (range.units, rbegin, rend, fsize)))
            return Response(FileIterator(apath, rbegin, rend),
                            status=206,
                            headers=headers,
                            direct_passthrough=True)

    f, mtime, file_size = _opener(apath)

    return Response(wrap_file(request.environ, f),
                    status=200,
                    headers=headers,
                    direct_passthrough=True)
예제 #9
0
    def _process_range_request(
        self,
        environ: "WSGIEnvironment",
        complete_length: t.Optional[int] = None,
        accept_ranges: t.Optional[t.Union[bool, str]] = None,
    ) -> bool:
        """Handle Range Request related headers (RFC7233).  If `Accept-Ranges`
        header is valid, and Range Request is processable, we set the headers
        as described by the RFC, and wrap the underlying response in a
        RangeWrapper.

        Returns ``True`` if Range Request can be fulfilled, ``False`` otherwise.

        :raises: :class:`~werkzeug.exceptions.RequestedRangeNotSatisfiable`
                 if `Range` header could not be parsed or satisfied.

        .. versionchanged:: 2.0
            Returns ``False`` if the length is 0.
        """
        from ..exceptions import RequestedRangeNotSatisfiable

        if (
            accept_ranges is None
            or complete_length is None
            or complete_length == 0
            or not self._is_range_request_processable(environ)
        ):
            return False

        parsed_range = parse_range_header(environ.get("HTTP_RANGE"))

        if parsed_range is None:
            raise RequestedRangeNotSatisfiable(complete_length)

        range_tuple = parsed_range.range_for_length(complete_length)
        content_range_header = parsed_range.to_content_range_header(complete_length)

        if range_tuple is None or content_range_header is None:
            raise RequestedRangeNotSatisfiable(complete_length)

        content_length = range_tuple[1] - range_tuple[0]
        self.headers["Content-Length"] = content_length
        self.headers["Accept-Ranges"] = accept_ranges
        self.content_range = content_range_header  # type: ignore
        self.status_code = 206
        self._wrap_range_response(range_tuple[0], content_length)
        return True
예제 #10
0
def sendFile(aStrFileName, aErrorStatusCode=200):
    """发送文件到客户端,支持单 Range 请求

    :aStrFileName: 文件名称
    :returns: Response

    """

    if not os.path.isfile(aStrFileName):
        log.logObject().error("找不到文件=%s" % aStrFileName)
        return buildErrorResponseData(kCmdUserError_NotResource,
                                      statusCode=aErrorStatusCode)

    try:
        httpHeaderRange = request.headers.get("Range")
        r = http.parse_range_header(httpHeaderRange)
        bHasRange = r is not None and len(
            r.ranges) == 1 and r.ranges[0][0] is not None
    except Exception as e:
        bHasRange = False

    #发送整个文件
    if not bHasRange:
        return send_file(aStrFileName)

    #发送部分文件
    nFileSize = os.path.getsize(aStrFileName)
    beginPos, stopPos = r.range_for_length(nFileSize)
    with open(aStrFileName, "rb") as f:
        f.seek(beginPos)
        byteContens = f.read(stopPos - beginPos)

    strMimeType = mimetypes.guess_type(aStrFileName)[0]
    if strMimeType is None:
        strMimeType = "application/octet-stream"
    resp = Response(byteContens,
                    206,
                    mimetype=strMimeType,
                    headers={
                        "Content-Range": r.make_content_range(nFileSize),
                        "Accept-Ranges": r.units,
                        "Etag": "%d" % nFileSize
                    })
    return resp
예제 #11
0
파일: views.py 프로젝트: wangjun/shapps
def sharedir_download(dname,rpath):
    rootpath = os.path.abspath(settings.SHAREDIR.directories.get(dname))
    if not rootpath:
        raise NotFound
    apath = os.path.abspath(os.path.join(rootpath,rpath))
    if (not apath.startswith(rootpath)) or (not os.path.isfile(apath)):
        raise NotFound

    def _opener(filename):
        return (
            open(filename, 'rb'),
            datetime.utcfromtimestamp(os.path.getmtime(filename)),
            int(os.path.getsize(filename))
        )

    guessed_type = mimetypes.guess_type(apath)
    mime_type = guessed_type[0] or 'application/octet-stream'

    headers = []
    headers.append(('Content-Type', mime_type))

    if request.range:
        range = request.range
    else:
        range = parse_range_header(request.environ.get('HTTP_RANGE'))
    #when request range,only recognize "bytes" as range units
    if range and range.units=="bytes":
        rbegin,rend = range.ranges[0]
        try:
            fsize = os.path.getsize(apath)
        except OSError as e:
            return Response("Not found",status=404)
        if (rbegin+1)<fsize:
            if rend == None:
                rend = fsize-1
            headers.append(('Content-Length',str(rend-rbegin+1)))
            headers.append(('Content-Range','%s %d-%d/%d' %(range.units,rbegin, rend, fsize)))
            return Response(FileIterator(apath,rbegin,rend),
                status=206, headers=headers, direct_passthrough=True)

    f, mtime, file_size = _opener(apath)

    return Response(wrap_file(request.environ, f), status=200, headers=headers,
        direct_passthrough=True)
예제 #12
0
def encode(path):
    headers = request.headers
    entry = current_app.nekumo.get_entry(path)
    encode = request.args.get('encode', 'chromecast')
    if not entry.exists():
        raise NotFound
    ffmpeg_args = FfmpegEncode(entry.gateway_path)
    ffmpeg_args.set_encode(encode)
    b = parse_range_header(headers.get('Range'))

    if b and b.ranges[0][0]:
        ffmpeg_args.set_skip_initial_bytes(b.ranges[0][0])
    p = ffmpeg_args.popen(True)

    def generate():
        for row in iter(lambda: p.stdout.read(1024 * 8), ''):
            yield row
    return Response(generate(),
                    mimetype='video/mp4' if encode == 'chromecast' else ffmpeg_args.fmt,
                    headers={'Accept-Ranges': 'bytes'})
예제 #13
0
def get_download_response(payload, content_length, content_format, filename, request=None):
    """
    :param payload: File like object.
    :param content_length: Size of payload in bytes
    :param content_format: ``couchexport.models.Format`` instance
    :param filename: Name of the download
    :param request: The request. Used to determine if a range response should be given.
    :return: HTTP response
    """
    ranges = None
    if request and "HTTP_RANGE" in request.META:
        try:
            ranges = parse_range_header(request.META['HTTP_RANGE'], content_length)
        except ValueError:
            pass

    if ranges and len(ranges.ranges) != 1:
        ranges = None

    response = StreamingHttpResponse(content_type=content_format.mimetype)
    if content_format.download:
        response['Content-Disposition'] = safe_filename_header(filename)

    response["Content-Length"] = content_length
    response["Accept-Ranges"] = "bytes"

    if ranges:
        start, stop = ranges.ranges[0]
        if stop is not None and stop > content_length:
            # requested range not satisfiable
            return HttpResponse(status=416)

        response.streaming_content = RangedFileWrapper(payload, start=start, stop=stop or float("inf"))
        end = stop or content_length
        response["Content-Range"] = "bytes %d-%d/%d" % (start, end - 1, content_length)
        response["Content-Length"] = end - start
        response.status_code = 206
    else:
        response.streaming_content = FileWrapper(payload)

    return response
예제 #14
0
파일: utils.py 프로젝트: varda/varda
 def collection_rule(*args, **kwargs):
     # Note: As an alternative, we could use the following instead of a
     #     missing Range header: `Range('items', [(0, 500)])`.
     try:
         r = parse_range_header(request.headers["Range"])
     except KeyError:
         raise ValidationError("Range required")
     if r is None or r.units != "items" or len(r.ranges) != 1:
         abort(416)
     begin, end = r.ranges[0]
     if not 0 <= begin < end:
         abort(416)
     end = min(end, begin + 500)
     kwargs.update(begin=begin, count=end - begin)
     total, response = rule(*args, **kwargs)
     if begin > total - 1:
         response.headers.add("Content-Range", ContentRange("items", None, None, total))
         abort(416)
     end = min(end, total)
     response.headers.add("Content-Range", ContentRange("items", begin, end, total))
     return response, 206
예제 #15
0
    def database_item(database_id, item_id, suffix, session_id):
        """
        """

        range_header = request.headers.get("Range", None)

        if range_header:
            begin, end = http.parse_range_header(range_header).ranges[0]
            data, mimetype, total_length = provider.get_item(
                session_id, database_id, item_id, byte_range=(begin, end))
            begin, end = (begin or 0), (end or total_length)

            # Setup response
            response = Response(
                data, 206, mimetype=mimetype,
                direct_passthrough=not isinstance(data, basestring))

            # A streaming response with unknown content lenght, Range x-*
            # as per RFC2616 section 14.16
            if total_length <= 0:
                response.headers["Content-Range"] = "bytes %d-%d/*" % (
                    begin, end - 1)
            elif total_length > 0:
                response.headers["Content-Range"] = "bytes %d-%d/%d" % (
                    begin, end - 1, total_length)
                response.headers["Content-Length"] = end - begin
        else:
            data, mimetype, total_length = provider.get_item(
                session_id, database_id, item_id)

            # Setup response
            response = Response(
                data, 200, mimetype=mimetype,
                direct_passthrough=not isinstance(data, basestring))

            if total_length > 0:
                response.headers["Content-Length"] = total_length

        return response
예제 #16
0
def test_range_to_header(ranges):
    header = Range("byes", ranges).to_header()
    r = http.parse_range_header(header)
    assert r.ranges == ranges
예제 #17
0
def filedown(environ,
             filename,
             cache=True,
             cache_timeout=None,
             action=None,
             real_filename=None,
             x_sendfile=False,
             x_header_name=None,
             x_filename=None,
             fileobj=None,
             default_mimetype='application/octet-stream'):
    """
    @param filename: is used for display in download
    @param real_filename: if used for the real file location
    @param x_urlfile: is only used in x-sendfile, and be set to x-sendfile header
    @param fileobj: if provided, then returned as file content
    @type fileobj: (fobj, mtime, size)

    filedown now support web server controlled download, you should set
    xsendfile=True, and add x_header, for example:

    nginx
        ('X-Accel-Redirect', '/path/to/local_url')
    apache
        ('X-Sendfile', '/path/to/local_url')
    """
    from .common import safe_str
    from werkzeug.http import parse_range_header

    guessed_type = mimetypes.guess_type(filename)
    mime_type = guessed_type[0] or default_mimetype
    real_filename = real_filename or filename

    #make common headers
    headers = []
    headers.append(('Content-Type', mime_type))
    d_filename = _get_download_filename(environ, os.path.basename(filename))
    if action == 'download':
        headers.append(('Content-Disposition', 'attachment; %s' % d_filename))
    elif action == 'inline':
        headers.append(('Content-Disposition', 'inline; %s' % d_filename))
    if x_sendfile:
        if not x_header_name or not x_filename:
            raise Exception("x_header_name or x_filename can't be empty")
        headers.append((x_header_name, safe_str(x_filename)))
        return Response('',
                        status=200,
                        headers=headers,
                        direct_passthrough=True)
    else:
        request = environ.get('werkzeug.request')
        if request:
            range = request.range
        else:
            range = parse_range_header(environ.get('HTTP_RANGE'))
        #when request range,only recognize "bytes" as range units
        if range and range.units == "bytes":
            try:
                fsize = os.path.getsize(real_filename)
            except OSError as e:
                return Response("Not found", status=404)
            mtime = datetime.utcfromtimestamp(os.path.getmtime(real_filename))
            mtime_str = http_date(mtime)
            if cache:
                etag = _generate_etag(mtime, fsize, real_filename)
            else:
                etag = mtime_str

            if_range = environ.get('HTTP_IF_RANGE')
            if if_range:
                check_if_range_ok = (if_range.strip('"') == etag)
                #print "check_if_range_ok (%s) = (%s ==%s)"%(check_if_range_ok,if_range.strip('"'),etag)
            else:
                check_if_range_ok = True

            rbegin, rend = range.ranges[0]
            if check_if_range_ok and (rbegin + 1) < fsize:
                if rend == None:
                    rend = fsize

                headers.append(('Content-Length', str(rend - rbegin)))
                #werkzeug do not count rend with the same way of rfc7233,so -1
                headers.append(
                    ('Content-Range',
                     '%s %d-%d/%d' % (range.units, rbegin, rend - 1, fsize)))
                headers.append(('Last-Modified', mtime_str))
                if cache:
                    headers.append(('ETag', '"%s"' % etag))
                #for small file, read it to memory and return directly
                #and this can avoid some issue with google chrome
                if (rend - rbegin) < FileIterator.chunk_size:
                    s = "".join([
                        chunk
                        for chunk in FileIterator(real_filename, rbegin, rend)
                    ])
                    return Response(s,
                                    status=206,
                                    headers=headers,
                                    direct_passthrough=True)
                else:
                    return Response(FileIterator(real_filename, rbegin, rend),
                                    status=206,
                                    headers=headers,
                                    direct_passthrough=True)

        #process fileobj
        if fileobj:
            f, mtime, file_size = fileobj
        else:
            f, mtime, file_size = _opener(real_filename)
        headers.append(('Date', http_date()))

        if cache:
            etag = _generate_etag(mtime, file_size, real_filename)
            headers += [
                ('ETag', '"%s"' % etag),
            ]
            if cache_timeout:
                headers += [('Cache-Control',
                             'max-age=%d, public' % cache_timeout),
                            ('Expires', http_date(time() + cache_timeout))]
            if not is_resource_modified(environ, etag, last_modified=mtime):
                f.close()
                return Response(status=304, headers=headers)
        else:
            headers.append(('Cache-Control', 'public'))

        headers.extend(
            (('Content-Length', str(file_size)), ('Last-Modified',
                                                  http_date(mtime))))

        return Response(wrap_file(environ, f),
                        status=200,
                        headers=headers,
                        direct_passthrough=True)
예제 #18
0
def test_range_to_header(ranges):
    header = Range("byes", ranges).to_header()
    r = http.parse_range_header(header)
    assert r.ranges == ranges
예제 #19
0
파일: filedown.py 프로젝트: 08haozi/uliweb
def filedown(environ, filename, cache=True, cache_timeout=None, 
    action=None, real_filename=None, x_sendfile=False,
    x_header_name=None, x_filename=None, fileobj=None,
    default_mimetype='application/octet-stream'):
    """
    @param filename: is used for display in download
    @param real_filename: if used for the real file location
    @param x_urlfile: is only used in x-sendfile, and be set to x-sendfile header
    @param fileobj: if provided, then returned as file content 
    @type fileobj: (fobj, mtime, size)
    
    filedown now support web server controlled download, you should set
    xsendfile=True, and add x_header, for example:
    
    nginx
        ('X-Accel-Redirect', '/path/to/local_url')
    apache
        ('X-Sendfile', '/path/to/local_url')
    """
    from werkzeug.http import parse_range_header
    
    guessed_type = mimetypes.guess_type(filename)
    mime_type = guessed_type[0] or default_mimetype
    real_filename = real_filename or filename
    
    #make common headers
    headers = []
    headers.append(('Content-Type', mime_type))
    d_filename = _get_download_filename(environ, os.path.basename(filename))
    if action == 'download':
        headers.append(('Content-Disposition', 'attachment; %s' % d_filename))
    elif action == 'inline':
        headers.append(('Content-Disposition', 'inline; %s' % d_filename))
    if x_sendfile:
        if not x_header_name or not x_filename:
            raise Exception, "x_header_name or x_filename can't be empty"
        headers.append((x_header_name, x_filename))
        return Response('', status=200, headers=headers,
            direct_passthrough=True)
    else:
        request = environ.get('werkzeug.request')
        if request:
            range = request.range
        else:
            range = parse_range_header(environ.get('HTTP_RANGE'))
        #when request range,only recognize "bytes" as range units
        if range!=None and range.units=="bytes":
            rbegin,rend = range.ranges[0]
            try:
                fsize = os.path.getsize(real_filename)
            except OSError,e:
                return Response("Not found",status=404)
            if (rbegin+1)<fsize:
                if rend == None:
                    rend = fsize-1
                headers.append(('Content-Length',str(rend-rbegin+1)))
                headers.append(('Content-Range','%s %d-%d/%d' %(range.units,rbegin, rend, fsize)))
                return Response(FileIterator(real_filename,rbegin,rend),
                    status=206, headers=headers, direct_passthrough=True)
        
        #process fileobj
        if fileobj:
            f, mtime, file_size = fileobj
        else:
            f, mtime, file_size = _opener(real_filename)
        headers.append(('Date', http_date()))
    
        if cache:
            etag = _generate_etag(mtime, file_size, real_filename)
            headers += [
                ('ETag', '"%s"' % etag),
            ]
            if cache_timeout:
                headers += [
                    ('Cache-Control', 'max-age=%d, public' % cache_timeout),
                    ('Expires', http_date(time() + cache_timeout))
                ]
            if not is_resource_modified(environ, etag, last_modified=mtime):
                f.close()
                return Response(status=304, headers=headers)
        else:
            headers.append(('Cache-Control', 'public'))
    

        headers.extend((
            ('Content-Length', str(file_size)),
            ('Last-Modified', http_date(mtime))
        ))
    
        return Response(wrap_file(environ, f), status=200, headers=headers,
            direct_passthrough=True)
예제 #20
0
def filedown(environ,
             filename,
             cache=True,
             cache_timeout=None,
             action=None,
             real_filename=None,
             x_sendfile=False,
             x_header_name=None,
             x_filename=None,
             fileobj=None,
             default_mimetype='application/octet-stream'):
    """
    @param filename: is used for display in download
    @param real_filename: if used for the real file location
    @param x_urlfile: is only used in x-sendfile, and be set to x-sendfile header
    @param fileobj: if provided, then returned as file content 
    @type fileobj: (fobj, mtime, size)
    
    filedown now support web server controlled download, you should set
    xsendfile=True, and add x_header, for example:
    
    nginx
        ('X-Accel-Redirect', '/path/to/local_url')
    apache
        ('X-Sendfile', '/path/to/local_url')
    """
    from werkzeug.http import parse_range_header

    guessed_type = mimetypes.guess_type(filename)
    mime_type = guessed_type[0] or default_mimetype
    real_filename = real_filename or filename

    #make common headers
    headers = []
    headers.append(('Content-Type', mime_type))
    d_filename = _get_download_filename(environ, os.path.basename(filename))
    if action == 'download':
        headers.append(('Content-Disposition', 'attachment; %s' % d_filename))
    elif action == 'inline':
        headers.append(('Content-Disposition', 'inline; %s' % d_filename))
    if x_sendfile:
        if not x_header_name or not x_filename:
            raise Exception, "x_header_name or x_filename can't be empty"
        headers.append((x_header_name, x_filename))
        return Response('',
                        status=200,
                        headers=headers,
                        direct_passthrough=True)
    else:
        request = environ.get('werkzeug.request')
        if request:
            range = request.range
        else:
            range = parse_range_header(environ.get('HTTP_RANGE'))
        #when request range,only recognize "bytes" as range units
        if range != None and range.units == "bytes":
            rbegin, rend = range.ranges[0]
            try:
                fsize = os.path.getsize(real_filename)
            except OSError, e:
                return Response("Not found", status=404)
            if (rbegin + 1) < fsize:
                if rend == None:
                    rend = fsize - 1
                headers.append(('Content-Length', str(rend - rbegin + 1)))
                headers.append(
                    ('Content-Range',
                     '%s %d-%d/%d' % (range.units, rbegin, rend, fsize)))
                return Response(FileIterator(real_filename, rbegin, rend),
                                status=206,
                                headers=headers,
                                direct_passthrough=True)

        #process fileobj
        if fileobj:
            f, mtime, file_size = fileobj
        else:
            f, mtime, file_size = _opener(real_filename)
        headers.append(('Date', http_date()))

        if cache:
            etag = _generate_etag(mtime, file_size, real_filename)
            headers += [
                ('ETag', '"%s"' % etag),
            ]
            if cache_timeout:
                headers += [('Cache-Control',
                             'max-age=%d, public' % cache_timeout),
                            ('Expires', http_date(time() + cache_timeout))]
            if not is_resource_modified(environ, etag, last_modified=mtime):
                f.close()
                return Response(status=304, headers=headers)
        else:
            headers.append(('Cache-Control', 'public'))

        headers.extend(
            (('Content-Length', str(file_size)), ('Last-Modified',
                                                  http_date(mtime))))

        return Response(wrap_file(environ, f),
                        status=200,
                        headers=headers,
                        direct_passthrough=True)
예제 #21
0
    def test_range_parsing(self):
        rv = http.parse_range_header("bytes=52")
        assert rv is None

        rv = http.parse_range_header("bytes=52-")
        assert rv.units == "bytes"
        assert rv.ranges == [(52, None)]
        assert rv.to_header() == "bytes=52-"

        rv = http.parse_range_header("bytes=52-99")
        assert rv.units == "bytes"
        assert rv.ranges == [(52, 100)]
        assert rv.to_header() == "bytes=52-99"

        rv = http.parse_range_header("bytes=52-99,-1000")
        assert rv.units == "bytes"
        assert rv.ranges == [(52, 100), (-1000, None)]
        assert rv.to_header() == "bytes=52-99,-1000"

        rv = http.parse_range_header("bytes = 1 - 100")
        assert rv.units == "bytes"
        assert rv.ranges == [(1, 101)]
        assert rv.to_header() == "bytes=1-100"

        rv = http.parse_range_header("AWesomes=0-999")
        assert rv.units == "awesomes"
        assert rv.ranges == [(0, 1000)]
        assert rv.to_header() == "awesomes=0-999"

        rv = http.parse_range_header("bytes=-")
        assert rv is None

        rv = http.parse_range_header("bytes=bad")
        assert rv is None

        rv = http.parse_range_header("bytes=bad-1")
        assert rv is None

        rv = http.parse_range_header("bytes=-bad")
        assert rv is None

        rv = http.parse_range_header("bytes=52-99, bad")
        assert rv is None
예제 #22
0
파일: base.py 프로젝트: ccns1/ccns11
 def range(self) -> Optional[Range]:
     return parse_range_header(self.headers.get("Range"))
예제 #23
0
파일: filedown.py 프로젝트: limodou/uliweb
def filedown(environ, filename, cache=True, cache_timeout=None,
    action=None, real_filename=None, x_sendfile=False,
    x_header_name=None, x_filename=None, fileobj=None,
    default_mimetype='application/octet-stream'):
    """
    @param filename: is used for display in download
    @param real_filename: if used for the real file location
    @param x_urlfile: is only used in x-sendfile, and be set to x-sendfile header
    @param fileobj: if provided, then returned as file content
    @type fileobj: (fobj, mtime, size)

    filedown now support web server controlled download, you should set
    xsendfile=True, and add x_header, for example:

    nginx
        ('X-Accel-Redirect', '/path/to/local_url')
    apache
        ('X-Sendfile', '/path/to/local_url')
    """
    from .common import safe_str
    from werkzeug.http import parse_range_header

    guessed_type = mimetypes.guess_type(filename)
    mime_type = guessed_type[0] or default_mimetype
    real_filename = real_filename or filename

    #make common headers
    headers = []
    headers.append(('Content-Type', mime_type))
    d_filename = _get_download_filename(environ, os.path.basename(filename))
    if action == 'download':
        headers.append(('Content-Disposition', 'attachment; %s' % d_filename))
    elif action == 'inline':
        headers.append(('Content-Disposition', 'inline; %s' % d_filename))
    if x_sendfile:
        if not x_header_name or not x_filename:
            raise Exception("x_header_name or x_filename can't be empty")
        headers.append((x_header_name, safe_str(x_filename)))
        return Response('', status=200, headers=headers,
            direct_passthrough=True)
    else:
        request = environ.get('werkzeug.request')
        if request:
            range = request.range
        else:
            range = parse_range_header(environ.get('HTTP_RANGE'))
        #when request range,only recognize "bytes" as range units
        if range and range.units=="bytes":
            try:
                fsize = os.path.getsize(real_filename)
            except OSError as e:
                return Response("Not found",status=404)
            mtime = datetime.utcfromtimestamp(os.path.getmtime(real_filename))
            mtime_str = http_date(mtime)
            if cache:
                etag = _generate_etag(mtime, fsize, real_filename)
            else:
                etag = mtime_str

            if_range = environ.get('HTTP_IF_RANGE')
            if if_range:
                check_if_range_ok = (if_range.strip('"')==etag)
                #print "check_if_range_ok (%s) = (%s ==%s)"%(check_if_range_ok,if_range.strip('"'),etag)
            else:
                check_if_range_ok = True

            rbegin,rend = range.ranges[0]
            if check_if_range_ok and (rbegin+1)<fsize:
                if rend == None:
                    rend = fsize

                headers.append(('Content-Length',str(rend-rbegin)))
                #werkzeug do not count rend with the same way of rfc7233,so -1
                headers.append(('Content-Range','%s %d-%d/%d' %(range.units,rbegin, rend-1, fsize)))
                headers.append(('Last-Modified', mtime_str))
                if cache:
                    headers.append(('ETag', '"%s"' % etag))
                #for small file, read it to memory and return directly
                #and this can avoid some issue with google chrome
                if (rend-rbegin) < FileIterator.chunk_size:
                    s = "".join([chunk for chunk in FileIterator(real_filename,rbegin,rend)])
                    return Response(s,status=206, headers=headers, direct_passthrough=True)
                else:
                    return Response(FileIterator(real_filename,rbegin,rend),
                        status=206, headers=headers, direct_passthrough=True)

        #process fileobj
        if fileobj:
            f, mtime, file_size = fileobj
        else:
            f, mtime, file_size = _opener(real_filename)
        headers.append(('Date', http_date()))

        if cache:
            etag = _generate_etag(mtime, file_size, real_filename)
            headers += [
                ('ETag', '"%s"' % etag),
            ]
            if cache_timeout:
                headers += [
                    ('Cache-Control', 'max-age=%d, public' % cache_timeout),
                    ('Expires', http_date(time() + cache_timeout))
                ]
            if not is_resource_modified(environ, etag, last_modified=mtime):
                f.close()
                return Response(status=304, headers=headers)
        else:
            headers.append(('Cache-Control', 'public'))


        headers.extend((
            ('Content-Length', str(file_size)),
            ('Last-Modified', http_date(mtime))
        ))

        return Response(wrap_file(environ, f), status=200, headers=headers,
            direct_passthrough=True)