class Racknga::Middleware::Range
This is a middleware that provides HTTP range request (partial request) support. For example, HTTP range request is used for playing a video on the way.
Usage:
require "racknga" use Racknga::Middleware::Range run YourApplication
Public Class Methods
new(application)
click to toggle source
# File lib/racknga/middleware/range.rb, line 31 def initialize(application) @application = application end
Public Instance Methods
call(environment)
click to toggle source
For Rack.
# File lib/racknga/middleware/range.rb, line 36 def call(environment) status, headers, body = @application.call(environment) return [status, headers, body] if status != 200 headers = Rack::Utils::HeaderHash.new(headers) headers["Accept-Ranges"] = "bytes" request = Rack::Request.new(environment) range = request.env["HTTP_RANGE"] if range and /\Abytes=(\d*)-(\d*)\z/ =~ range first_byte, last_byte = $1, $2 status, headers, body = apply_range(status, headers, body, request, first_byte, last_byte) end [status, headers.to_hash, body] end
Private Instance Methods
apply_range(status, headers, body, request, first_byte, last_byte)
click to toggle source
# File lib/racknga/middleware/range.rb, line 53 def apply_range(status, headers, body, request, first_byte, last_byte) unless use_range?(request, headers) return [status, headers, body] end length = guess_length(headers, body) return [status, headers.to_hash, body] if length.nil? if first_byte.empty? and last_byte.empty? headers["Content-Length"] = "0" return [Rack::Utils.status_code(:requested_range_not_satisfiable), headers, []] end if last_byte.empty? last_byte = length - 1 else last_byte = last_byte.to_i end if first_byte.empty? first_byte = length - last_byte last_byte = length - 1 else first_byte = first_byte.to_i end byte_range_spec = "#{first_byte}-#{last_byte}/#{length}" range_length = last_byte - first_byte + 1 headers["Content-Range"] = "bytes #{byte_range_spec}" headers["Content-Length"] = range_length.to_s stream = RangeStream.new(body, first_byte, range_length) if body.respond_to?(:to_path) def stream.to_path @body.to_path end end [Rack::Utils.status_code(:partial_content), headers, stream] end
guess_length(headers, body)
click to toggle source
# File lib/racknga/middleware/range.rb, line 113 def guess_length(headers, body) length = headers["Content-Length"] return length.to_i unless length.nil? return body.stat.size if body.respond_to?(:stat) nil end
use_range?(request, headers)
click to toggle source
# File lib/racknga/middleware/range.rb, line 94 def use_range?(request, headers) if_range = request.env["HTTP_IF_RANGE"] return true if if_range.nil? if /\A(?:Mo|Tu|We|Th|Fr|Sa|Su)/ =~ if_range last_modified = headers["Last-Modified"] return false if last_modified.nil? begin if_range = Time.httpdate(if_range) last_modified = Time.httpdate(last_modified) rescue ArgumentError return true end if_range == last_modified else if_range == headers["ETag"] end end