moved static file handler code to send_file (#330)

This commit is contained in:
Cris Ward 2017-03-12 12:38:47 +00:00 committed by Serdar Dogruyol
parent e276c0a278
commit 298a4b234b
2 changed files with 76 additions and 78 deletions

View file

@ -81,7 +81,8 @@ def headers(env, additional_headers)
env.response.headers.merge!(additional_headers)
end
# Send a file with given path and default `application/octet-stream` mime_type.
# Send a file with given path and base the mime-type on the file extension
# or default `application/octet-stream` mime_type.
#
# send_file env, "./path/to/file"
#
@ -89,11 +90,82 @@ end
#
# send_file env, "./path/to/file", "image/jpeg"
def send_file(env, path : String, mime_type : String? = nil)
config = Kemal.config.serve_static
file_path = File.expand_path(path, Dir.current)
mime_type ||= "application/octet-stream"
mime_type ||= Kemal::Utils.mime_type(file_path)
env.response.content_type = mime_type
env.response.content_length = File.size(file_path)
minsize = 860 # http://webmasters.stackexchange.com/questions/31750/what-is-recommended-minimum-object-size-for-gzip-performance-benefits ??
request_headers = env.request.headers
filesize = File.size(file_path)
File.open(file_path) do |file|
if env.request.method == "GET" && env.request.headers.has_key?("Range")
next multipart(file, env)
end
if request_headers.includes_word?("Accept-Encoding", "gzip") && config.is_a?(Hash) && config["gzip"] == true && filesize > minsize && Kemal::Utils.zip_types(file_path)
env.response.headers["Content-Encoding"] = "gzip"
Gzip::Writer.open(env.response) do |deflate|
IO.copy(file, deflate)
end
elsif request_headers.includes_word?("Accept-Encoding", "deflate") && config.is_a?(Hash) && config["gzip"]? == true && filesize > minsize && Kemal::Utils.zip_types(file_path)
env.response.headers["Content-Encoding"] = "deflate"
Flate::Writer.new(env.response) do |deflate|
IO.copy(file, deflate)
end
else
env.response.content_length = filesize
IO.copy(file, env.response)
end
end
end
private def multipart(file, env)
# See http://httpwg.org/specs/rfc7233.html
fileb = file.size
range = env.request.headers["Range"]
match = range.match(/bytes=(\d{1,})-(\d{0,})/)
startb = 0
endb = 0
if match
if match.size >= 2
startb = match[1].to_i { 0 }
end
if match.size >= 3
endb = match[2].to_i { 0 }
end
end
if endb == 0
endb = fileb
end
if startb < endb && endb <= fileb
env.response.status_code = 206
env.response.content_length = endb - startb
env.response.headers["Accept-Ranges"] = "bytes"
env.response.headers["Content-Range"] = "bytes #{startb}-#{endb - 1}/#{fileb}" # MUST
if startb > 1024
skipped = 0
# file.skip only accepts values less or equal to 1024 (buffer size, undocumented)
until skipped + 1024 > startb
file.skip(1024)
skipped += 1024
end
if skipped - startb > 0
file.skip(skipped - startb)
end
else
file.skip(startb)
end
IO.copy(file, env.response, endb - startb)
else
env.response.content_length = fileb
env.response.status_code = 200 # Range not satisfable, see 4.4 Note
IO.copy(file, env.response)
end
end

View file

@ -51,29 +51,7 @@ module Kemal
end
elsif File.exists?(file_path)
return if etag(context, file_path)
minsize = 860 # http://webmasters.stackexchange.com/questions/31750/what-is-recommended-minimum-object-size-for-gzip-performance-benefits ??
context.response.content_type = Utils.mime_type(file_path)
request_headers = context.request.headers
filesize = File.size(file_path)
File.open(file_path) do |file|
if context.request.method == "GET" && context.request.headers.has_key?("Range")
next multipart(file, context)
end
if request_headers.includes_word?("Accept-Encoding", "gzip") && config.is_a?(Hash) && config["gzip"] == true && filesize > minsize && Utils.zip_types(file_path)
context.response.headers["Content-Encoding"] = "gzip"
Gzip::Writer.open(context.response) do |deflate|
IO.copy(file, deflate)
end
elsif request_headers.includes_word?("Accept-Encoding", "deflate") && config.is_a?(Hash) && config["gzip"]? == true && filesize > minsize && Utils.zip_types(file_path)
context.response.headers["Content-Encoding"] = "deflate"
Flate::Writer.new(context.response) do |deflate|
IO.copy(file, deflate)
end
else
context.response.content_length = filesize
IO.copy(file, context.response)
end
end
send_file(context, file_path)
else
call_next(context)
end
@ -88,57 +66,5 @@ module Kemal
context.response.status_code = 304 # not modified
return true
end
private def multipart(file, env)
# See http://httpwg.org/specs/rfc7233.html
fileb = file.size
range = env.request.headers["Range"]
match = range.match(/bytes=(\d{1,})-(\d{0,})/)
startb = 0
endb = 0
if match
if match.size >= 2
startb = match[1].to_i { 0 }
end
if match.size >= 3
endb = match[2].to_i { 0 }
end
end
if endb == 0
endb = fileb
end
if startb < endb && endb <= fileb
env.response.status_code = 206
env.response.content_length = endb - startb
env.response.headers["Accept-Ranges"] = "bytes"
env.response.headers["Content-Range"] = "bytes #{startb}-#{endb - 1}/#{fileb}" # MUST
if startb > 1024
skipped = 0
# file.skip only accepts values less or equal to 1024 (buffer size, undocumented)
until skipped + 1024 > startb
file.skip(1024)
skipped += 1024
end
if skipped - startb > 0
file.skip(skipped - startb)
end
else
file.skip(startb)
end
IO.copy(file, env.response, endb - startb)
else
env.response.content_length = fileb
env.response.status_code = 200 # Range not satisfable, see 4.4 Note
IO.copy(file, env.response)
end
end
end
end