Class: Utopia::Middleware::Static::LocalFile

Inherits:
Object
  • Object
show all
Defined in:
lib/utopia/middleware/static.rb

Instance Attribute Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(root, path) ⇒ LocalFile

Returns a new instance of LocalFile.



69
70
71
72
73
74
75
# File 'lib/utopia/middleware/static.rb', line 69

def initialize(root, path)
	@root = root
	@path = path
	@etag = Digest::SHA1.hexdigest("#{File.size(full_path)}#{mtime_date}")

	@range = nil
end

Instance Attribute Details

#etagObject (readonly)

Returns the value of attribute etag.



79
80
81
# File 'lib/utopia/middleware/static.rb', line 79

def etag
  @etag
end

#pathObject (readonly)

Returns the value of attribute path.



78
79
80
# File 'lib/utopia/middleware/static.rb', line 78

def path
  @path
end

#rangeObject (readonly)

Returns the value of attribute range.



80
81
82
# File 'lib/utopia/middleware/static.rb', line 80

def range
  @range
end

#rootObject (readonly)

Returns the value of attribute root.



77
78
79
# File 'lib/utopia/middleware/static.rb', line 77

def root
  @root
end

Instance Method Details

#eachObject



99
100
101
102
103
104
105
106
107
108
109
110
111
112
# File 'lib/utopia/middleware/static.rb', line 99

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

#full_pathObject



87
88
89
# File 'lib/utopia/middleware/static.rb', line 87

def full_path
	File.join(@root, @path.components)
end

#modified?(env) ⇒ Boolean

Returns:

  • (Boolean)


114
115
116
117
118
119
120
121
122
123
124
125
# File 'lib/utopia/middleware/static.rb', line 114

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_dateObject



91
92
93
# File 'lib/utopia/middleware/static.rb', line 91

def mtime_date
	File.mtime(full_path).httpdate
end

#serve(env, response_headers) ⇒ Object



127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
# File 'lib/utopia/middleware/static.rb', line 127

def serve(env, response_headers)
	ranges = Rack::Utils.byte_ranges(env, size)
	response = [200, response_headers, self]

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

	if ranges.nil? || ranges.length > 1
		# No ranges, or multiple ranges (which we don't support):
		# TODO: Support multiple byte-ranges
		response[0] = 200
		response[1]["Content-Length"] = size.to_s
		@range = 0..size-1
	elsif ranges.empty?
		# Unsatisfiable. Return error, and file size:
		response = Middleware::failure(416, "Invalid range specified.")
		response[1]["Content-Range"] = "bytes */#{size}"
		return response
	else
		# Partial content:
		@range = ranges[0]
		response[0] = 206
		response[1]["Content-Range"] = "bytes #{@range.begin}-#{@range.end}/#{size}"
		response[1]["Content-Length"] = (@range.end - @range.begin+1).to_s
		size = @range.end - @range.begin + 1
	end

	# LOG.info("Response for #{self.full_path}: #{response.inspect}")
	LOG.info "Serving file #{full_path.inspect}, range #{@range.inspect}"

	return response
end

#sizeObject



95
96
97
# File 'lib/utopia/middleware/static.rb', line 95

def size
	File.size(full_path)
end

#to_pathObject

Fit in with Rack::Sendfile



83
84
85
# File 'lib/utopia/middleware/static.rb', line 83

def to_path
	full_path
end