1
0
Fork 0
mirror of https://github.com/sinatra/sinatra synced 2023-03-27 23:18:01 -04:00

send_file now copes with 'Range: bytes=100-400,500-600' headers

Signed-off-by: Konstantin Haase <konstantin.mailinglists@googlemail.com>
This commit is contained in:
JP Hastings-Spital 2010-10-10 10:43:46 +02:00 committed by Konstantin Haase
parent 89af8c1167
commit 480b1e8ebe
2 changed files with 55 additions and 5 deletions

View file

@ -161,8 +161,18 @@ module Sinatra
elsif opts[:disposition] == 'inline'
response['Content-Disposition'] = 'inline'
end
halt StaticFile.open(path, 'rb')
sf = StaticFile.open(path, 'rb')
if (env['HTTP_RANGE'] =~ /^bytes=(\d+-\d+(?:,\d+-\d+)*)$/)
sf.ranges = $1.split(',').collect{|range| range.split('-').collect{|n| n.to_i}}
sf.ranges.each do |range|
halt 416 if range[1] < range[0]
end
response['Content-Range'] = "bytes #{$1}/#{response['Content-Length']}"
response['Content-Length'] = sf.ranges.dup.inject(0){|total,range| total + range[1] - range[0] + 1 }.to_s
halt 206, sf
else
halt sf
end
rescue Errno::ENOENT
not_found
end
@ -171,13 +181,28 @@ module Sinatra
# generated iteratively in 8K chunks.
class StaticFile < ::File #:nodoc:
alias_method :to_path, :path
attr_accessor :ranges
def each
if @ranges
@ranges.each do |range|
self.pos = range[0]
length = range[1] - range[0] + 1
while buf = read([8192,length.abs].min)
yield buf
length -= buf.length
break if (length -= 8192) + 8192 <= 0
end
end
else
rewind
while buf = read(8192)
yield buf
end
end
end
end
# Specify response freshness policy for HTTP caches (Cache-Control header).
# Any number of non-value directives (:public, :private, :no_cache,

View file

@ -90,4 +90,29 @@ class StaticTest < Test::Unit::TestCase
get "/../#{File.basename(__FILE__)}"
assert not_found?
end
it 'deals correctly with incompletable range requests' do
request = Rack::MockRequest.new(@app)
response = request.get("/#{File.basename(__FILE__)}", 'HTTP_RANGE' => "bytes=45-40")
assert_equal 416,response.status, "Ranges with final position < initial position should give HTTP/1.1 416 Requested Range Not Satisfiable"
end
it 'accepts and returns byte ranges correctly' do
[[[42,88]],[[1,5],[6,85]]].each do |ranges|
request = Rack::MockRequest.new(@app)
response = request.get("/#{File.basename(__FILE__)}", 'HTTP_RANGE' => "bytes=#{ranges.map{|r| r.join('-')}.join(',')}")
file = File.read(__FILE__)
should_be = ''
ranges.each do |range|
should_be += file[range[0]..range[1]]
end
assert_equal 206,response.status, "Should be HTTP/1.1 206 Partial content"
assert_equal should_be, response.body
assert_equal should_be.length.to_s, response['Content-Length'], "Length given was not the same as Content-Length reported"
assert_equal "bytes #{ranges.map{|r| r.join('-')}.join(',')}/#{File.size(__FILE__)}", response['Content-Range'],"Content-Range header was not correct"
end
end
end