mirror of
https://gitea.invidious.io/iv-org/shard-kemal.git
synced 2024-08-15 00:53:36 +00:00
gzip static files options dir listing and etags
format
This commit is contained in:
parent
2ff5991c92
commit
a8cc4f4177
7 changed files with 224 additions and 10 deletions
|
@ -11,13 +11,13 @@ module Kemal
|
|||
@ssl : OpenSSL::SSL::Context::Server?
|
||||
|
||||
property host_binding, ssl, port, env, public_folder, logging,
|
||||
always_rescue, serve_static, server, extra_options
|
||||
always_rescue, serve_static : (Bool | Hash(String, Bool)), server, extra_options
|
||||
|
||||
def initialize
|
||||
@host_binding = "0.0.0.0"
|
||||
@port = 3000
|
||||
@env = "development"
|
||||
@serve_static = true
|
||||
@serve_static = {"dir_listing" => false, "gzip" => true}
|
||||
@public_folder = "./public"
|
||||
@logging = true
|
||||
@logger = nil
|
||||
|
@ -89,7 +89,7 @@ module Kemal
|
|||
end
|
||||
|
||||
private def setup_static_file_handler
|
||||
HANDLERS.insert(3, Kemal::StaticFileHandler.new(@public_folder)) if @serve_static
|
||||
HANDLERS.insert(3, Kemal::StaticFileHandler.new(@public_folder)) if @serve_static.is_a?(Hash)
|
||||
end
|
||||
end
|
||||
|
||||
|
|
|
@ -32,7 +32,7 @@ def logger(logger)
|
|||
end
|
||||
|
||||
# Enables / Disables static file serving.
|
||||
def serve_static(status)
|
||||
def serve_static(status : (Bool | Hash))
|
||||
Kemal.config.serve_static = status
|
||||
end
|
||||
|
||||
|
|
|
@ -1,13 +1,92 @@
|
|||
{% if !flag?(:without_zlib) %}
|
||||
require "zlib"
|
||||
{% end %}
|
||||
|
||||
module Kemal
|
||||
# Kemal::StaticFileHandler is used to serve static files(.js/.css/.png e.g).
|
||||
# This handler is on by default and you can disable it like.
|
||||
#
|
||||
# serve_static false
|
||||
#
|
||||
class StaticFileHandler < HTTP::StaticFileHandler
|
||||
def call(context)
|
||||
return call_next(context) if context.request.path.not_nil! == "/"
|
||||
super
|
||||
|
||||
unless context.request.method == "GET" || context.request.method == "HEAD"
|
||||
if @fallthrough
|
||||
call_next(context)
|
||||
else
|
||||
context.response.status_code = 405
|
||||
context.response.headers.add("Allow", "GET, HEAD")
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
config = Kemal.config.serve_static
|
||||
original_path = context.request.path.not_nil!
|
||||
is_dir_path = original_path.ends_with? "/"
|
||||
request_path = URI.unescape(original_path)
|
||||
|
||||
# File path cannot contains '\0' (NUL) because all filesystem I know
|
||||
# don't accept '\0' character as file name.
|
||||
if request_path.includes? '\0'
|
||||
context.response.status_code = 400
|
||||
return
|
||||
end
|
||||
|
||||
expanded_path = File.expand_path(request_path, "/")
|
||||
if is_dir_path && !expanded_path.ends_with? "/"
|
||||
expanded_path = "#{expanded_path}/"
|
||||
end
|
||||
is_dir_path = expanded_path.ends_with? "/"
|
||||
|
||||
file_path = File.join(@public_dir, expanded_path)
|
||||
is_dir = Dir.exists? file_path
|
||||
|
||||
if request_path != expanded_path || is_dir && !is_dir_path
|
||||
redirect_to context, "#{expanded_path}#{is_dir && !is_dir_path ? "/" : ""}"
|
||||
end
|
||||
|
||||
if Dir.exists?(file_path)
|
||||
if config.is_a?(Hash) && config["dir_listing"] == true
|
||||
context.response.content_type = "text/html"
|
||||
directory_listing(context.response, request_path, file_path)
|
||||
else
|
||||
return call_next(context)
|
||||
end
|
||||
elsif File.exists?(file_path)
|
||||
return if self.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 = mime_type(file_path)
|
||||
request_headers = context.request.headers
|
||||
filesize = File.size(file_path)
|
||||
File.open(file_path) do |file|
|
||||
if request_headers.includes_word?("Accept-Encoding", "gzip") && config.is_a?(Hash) && config["gzip"] == true && filesize > minsize && self.zip_types(file_path)
|
||||
context.response.headers["Content-Encoding"] = "gzip"
|
||||
Zlib::Deflate.gzip(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 && self.zip_types(file_path)
|
||||
context.response.headers["Content-Encoding"] = "deflate"
|
||||
Zlib::Deflate.new(context.response) do |deflate|
|
||||
IO.copy(file, deflate)
|
||||
end
|
||||
else
|
||||
context.response.content_length = filesize
|
||||
IO.copy(file, context.response)
|
||||
end
|
||||
end
|
||||
else
|
||||
call_next(context)
|
||||
end
|
||||
end
|
||||
|
||||
def etag(context, file_path)
|
||||
etag = %{W/"#{File.lstat(file_path).mtime.epoch.to_s}"}
|
||||
context.response.headers["ETag"] = etag
|
||||
return false if !context.request.headers["If-None-Match"]? || context.request.headers["If-None-Match"] != etag
|
||||
context.response.content_length = 0
|
||||
context.response.status_code = 304 # not modified
|
||||
return true
|
||||
end
|
||||
|
||||
def zip_types(path) # https://github.com/h5bp/server-configs-nginx/blob/master/nginx.conf
|
||||
[".htm", ".html", ".txt", ".css", ".js", ".svg", ".json", ".xml", ".otf", ".ttf", ".woff", ".woff2"].includes? File.extname(path)
|
||||
end
|
||||
|
||||
def mime_type(path)
|
||||
|
@ -20,6 +99,11 @@ module Kemal
|
|||
when ".jpg", ".jpeg" then "image/jpeg"
|
||||
when ".gif" then "image/gif"
|
||||
when ".svg" then "image/svg+xml"
|
||||
when ".xml" then "application/xml"
|
||||
when ".json" then "application/json"
|
||||
when ".otf", ".ttf" then "application/font-sfnt"
|
||||
when ".woff" then "application/font-woff"
|
||||
when ".woff2" then "font/woff2"
|
||||
else "application/octet-stream"
|
||||
end
|
||||
end
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue