class Utopia::Static::LocalFile

Represents a local file on disk which can be served directly, or passed upstream to sendfile.

Constants

CONTENT_LENGTH
CONTENT_RANGE

Attributes

etag[R]
path[R]
range[R]
root[R]

Public Class Methods

new(root, path) click to toggle source
# File lib/utopia/static/local_file.rb, line 31
def initialize(root, path)
        @root = root
        @path = path
        @etag = Digest::SHA1.hexdigest("#{File.size(full_path)}#{mtime_date}")

        @range = nil
end

Public Instance Methods

bytesize() click to toggle source
# File lib/utopia/static/local_file.rb, line 57
def bytesize
        File.size(full_path)
end
Also aliased as: size
each() { |part| ... } click to toggle source
# File lib/utopia/static/local_file.rb, line 68
def each
        File.open(full_path, "rb") do |file|
                file.seek(@range.begin)
                remaining = @range.end - @range.begin+1

                while remaining > 0
                        break unless part = file.read([8192, remaining].min)

                        remaining -= part.length

                        yield part
                end
        end
end
empty?() click to toggle source

This reflects whether calling each would yield anything.

# File lib/utopia/static/local_file.rb, line 62
def empty?
        bytesize == 0
end
full_path() click to toggle source
# File lib/utopia/static/local_file.rb, line 49
def full_path
        File.join(@root, @path.components)
end
modified?(env) click to toggle source
# File lib/utopia/static/local_file.rb, line 83
def modified?(env)
        if modified_since = env['HTTP_IF_MODIFIED_SINCE']
                return false if File.mtime(full_path) <= Time.parse(modified_since)
        end

        if etags = env['HTTP_IF_NONE_MATCH']
                etags = etags.split(/\s*,\s*/)
                return false if etags.include?(etag) || etags.include?('*')
        end

        return true
end
mtime_date() click to toggle source
# File lib/utopia/static/local_file.rb, line 53
def mtime_date
        File.mtime(full_path).httpdate
end
serve(env, response_headers) click to toggle source
# File lib/utopia/static/local_file.rb, line 99
def serve(env, response_headers)
        ranges = Rack::Utils.get_byte_ranges(env['HTTP_RANGE'], size)
        response = [200, response_headers, self]

        # puts "Requesting ranges: #{ranges.inspect} (#{size})"

        if ranges == nil or ranges.size != 1
                # No ranges, or multiple ranges (which we don't support).
                # TODO: Support multiple byte-ranges, for now just send entire file:
                response[0] = 200
                response[1][CONTENT_LENGTH] = size.to_s
                @range = 0...size
        else
                # Partial content:
                @range = ranges[0]
                partial_size = @range.size
                
                response[0] = 206
                response[1][CONTENT_LENGTH] = partial_size.to_s
                response[1][CONTENT_RANGE] = "bytes #{@range.min}-#{@range.max}/#{size}"
        end
        
        return response
end
size()
Alias for: bytesize
to_path() click to toggle source

Fit in with Rack::Sendfile

# File lib/utopia/static/local_file.rb, line 45
def to_path
        full_path
end