Add allocation optimizations (#487)
This commit is contained in:
parent
afd17fc774
commit
21ab64511a
13
src/kemal.cr
13
src/kemal.cr
|
@ -68,15 +68,12 @@ module Kemal
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.stop
|
def self.stop
|
||||||
if config.running
|
raise "Kemal is already stopped." if !config.running
|
||||||
if server = config.server
|
if server = config.server
|
||||||
server.close unless server.closed?
|
server.close unless server.closed?
|
||||||
config.running = false
|
config.running = false
|
||||||
else
|
|
||||||
raise "Kemal.config.server is not set. Please use Kemal.run to set the server."
|
|
||||||
end
|
|
||||||
else
|
else
|
||||||
raise "Kemal is already stopped."
|
raise "Kemal.config.server is not set. Please use Kemal.run to set the server."
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -41,14 +41,8 @@ module Kemal
|
||||||
private def configure_ssl
|
private def configure_ssl
|
||||||
{% if !flag?(:without_openssl) %}
|
{% if !flag?(:without_openssl) %}
|
||||||
if @ssl_enabled
|
if @ssl_enabled
|
||||||
unless @key_file
|
abort "SSL Key Not Found" if !@key_file
|
||||||
puts "SSL Key Not Found"
|
abort "SSL Certificate Not Found" if !@cert_file
|
||||||
exit
|
|
||||||
end
|
|
||||||
unless @cert_file
|
|
||||||
puts "SSL Certificate Not Found"
|
|
||||||
exit
|
|
||||||
end
|
|
||||||
ssl = Kemal::SSL.new
|
ssl = Kemal::SSL.new
|
||||||
ssl.key_file = @key_file.not_nil!
|
ssl.key_file = @key_file.not_nil!
|
||||||
ssl.cert_file = @cert_file.not_nil!
|
ssl.cert_file = @cert_file.not_nil!
|
||||||
|
@ -58,7 +52,9 @@ module Kemal
|
||||||
end
|
end
|
||||||
|
|
||||||
private def read_env
|
private def read_env
|
||||||
@config.env = ENV["KEMAL_ENV"] if ENV.has_key?("KEMAL_ENV")
|
if kemal_env = ENV["KEMAL_ENV"]?
|
||||||
|
@config.env = kemal_env
|
||||||
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -144,15 +144,10 @@ module Kemal
|
||||||
end
|
end
|
||||||
|
|
||||||
private def setup_custom_handlers
|
private def setup_custom_handlers
|
||||||
CUSTOM_HANDLERS.each do |ch|
|
CUSTOM_HANDLERS.each do |ch0, ch1|
|
||||||
position = ch[0]
|
position = ch0
|
||||||
if !position
|
HANDLERS.insert (position || @handler_position), ch1
|
||||||
HANDLERS.insert(@handler_position, ch[1])
|
@handler_position += 1
|
||||||
@handler_position += 1
|
|
||||||
else
|
|
||||||
HANDLERS.insert(position, ch[1])
|
|
||||||
@handler_position += 1
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -30,7 +30,7 @@ module Kemal
|
||||||
def _add_route_filter(verb : String, path, type, &block : HTTP::Server::Context -> _)
|
def _add_route_filter(verb : String, path, type, &block : HTTP::Server::Context -> _)
|
||||||
lookup = lookup_filters_for_path_type(verb, path, type)
|
lookup = lookup_filters_for_path_type(verb, path, type)
|
||||||
if lookup.found? && lookup.payload.is_a?(Array(FilterBlock))
|
if lookup.found? && lookup.payload.is_a?(Array(FilterBlock))
|
||||||
(lookup.payload.as(Array(FilterBlock))) << FilterBlock.new(&block)
|
lookup.payload << FilterBlock.new(&block)
|
||||||
else
|
else
|
||||||
@tree.add radix_path(verb, path, type), [FilterBlock.new(&block)]
|
@tree.add radix_path(verb, path, type), [FilterBlock.new(&block)]
|
||||||
end
|
end
|
||||||
|
@ -54,8 +54,8 @@ module Kemal
|
||||||
private def call_block_for_path_type(verb : String?, path : String, type, context : HTTP::Server::Context)
|
private def call_block_for_path_type(verb : String?, path : String, type, context : HTTP::Server::Context)
|
||||||
lookup = lookup_filters_for_path_type(verb, path, type)
|
lookup = lookup_filters_for_path_type(verb, path, type)
|
||||||
if lookup.found? && lookup.payload.is_a? Array(FilterBlock)
|
if lookup.found? && lookup.payload.is_a? Array(FilterBlock)
|
||||||
blocks = lookup.payload.as(Array(FilterBlock))
|
blocks = lookup.payload
|
||||||
blocks.each { |block| block.call(context) }
|
blocks.each &.call(context)
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -11,15 +11,17 @@ module Kemal
|
||||||
|
|
||||||
macro only(paths, method = "GET")
|
macro only(paths, method = "GET")
|
||||||
class_name = {{@type.name}}
|
class_name = {{@type.name}}
|
||||||
|
method_downcase = {{method}}.downcase
|
||||||
|
class_name_method = "#{class_name}/#{method_downcase}"
|
||||||
({{paths}}).each do |path|
|
({{paths}}).each do |path|
|
||||||
@@only_routes_tree.add "#{class_name}/#{{{method}}.downcase}#{path}", "/#{{{method}}.downcase}#{path}"
|
@@only_routes_tree.add class_name_method + path, '/' + method_downcase + path
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
macro exclude(paths, method = "GET")
|
macro exclude(paths, method = "GET")
|
||||||
class_name = {{@type.name}}
|
class_name = {{@type.name}}
|
||||||
({{paths}}).each do |path|
|
({{paths}}).each do |path|
|
||||||
@@exclude_routes_tree.add "#{class_name}/#{{{method}}.downcase}#{path}", "/#{{{method}}.downcase}#{path}"
|
@@exclude_routes_tree.add class_name_method + path, '/' + method_downcase + path
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -127,12 +127,14 @@ def send_file(env : HTTP::Server::Context, path : String, mime_type : String? =
|
||||||
if env.request.method == "GET" && env.request.headers.has_key?("Range")
|
if env.request.method == "GET" && env.request.headers.has_key?("Range")
|
||||||
next multipart(file, env)
|
next multipart(file, env)
|
||||||
end
|
end
|
||||||
if request_headers.includes_word?("Accept-Encoding", "gzip") && config.is_a?(Hash) && config["gzip"] == true && filesize > minsize && Kemal::Utils.zip_types(file_path)
|
|
||||||
|
condition = config.is_a?(Hash) && config["gzip"]? == true && filesize > minsize && Kemal::Utils.zip_types(file_path)
|
||||||
|
if condition && request_headers.includes_word?("Accept-Encoding", "gzip")
|
||||||
env.response.headers["Content-Encoding"] = "gzip"
|
env.response.headers["Content-Encoding"] = "gzip"
|
||||||
Gzip::Writer.open(env.response) do |deflate|
|
Gzip::Writer.open(env.response) do |deflate|
|
||||||
IO.copy(file, deflate)
|
IO.copy(file, deflate)
|
||||||
end
|
end
|
||||||
elsif request_headers.includes_word?("Accept-Encoding", "deflate") && config.is_a?(Hash) && config["gzip"]? == true && filesize > minsize && Kemal::Utils.zip_types(file_path)
|
elsif condition && request_headers.includes_word?("Accept-Encoding", "deflate")
|
||||||
env.response.headers["Content-Encoding"] = "deflate"
|
env.response.headers["Content-Encoding"] = "deflate"
|
||||||
Flate::Writer.open(env.response) do |deflate|
|
Flate::Writer.open(env.response) do |deflate|
|
||||||
IO.copy(file, deflate)
|
IO.copy(file, deflate)
|
||||||
|
@ -148,28 +150,16 @@ end
|
||||||
private def multipart(file, env : HTTP::Server::Context)
|
private def multipart(file, env : HTTP::Server::Context)
|
||||||
# See http://httpwg.org/specs/rfc7233.html
|
# See http://httpwg.org/specs/rfc7233.html
|
||||||
fileb = file.size
|
fileb = file.size
|
||||||
|
startb = endb = 0
|
||||||
|
|
||||||
range = env.request.headers["Range"]
|
if match = env.request.headers["Range"].match /bytes=(\d{1,})-(\d{0,})/
|
||||||
match = range.match(/bytes=(\d{1,})-(\d{0,})/)
|
startb = match[1].to_i { 0 } if match.size >= 2
|
||||||
|
endb = match[2].to_i { 0 } if match.size >= 3
|
||||||
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
|
end
|
||||||
|
|
||||||
if endb == 0
|
endb = fileb - 1 if endb == 0
|
||||||
endb = fileb - 1
|
|
||||||
end
|
|
||||||
|
|
||||||
if startb < endb && endb < fileb
|
if startb < endb < fileb
|
||||||
content_length = 1 + endb - startb
|
content_length = 1 + endb - startb
|
||||||
env.response.status_code = 206
|
env.response.status_code = 206
|
||||||
env.response.content_length = content_length
|
env.response.content_length = content_length
|
||||||
|
@ -179,12 +169,12 @@ private def multipart(file, env : HTTP::Server::Context)
|
||||||
if startb > 1024
|
if startb > 1024
|
||||||
skipped = 0
|
skipped = 0
|
||||||
# file.skip only accepts values less or equal to 1024 (buffer size, undocumented)
|
# file.skip only accepts values less or equal to 1024 (buffer size, undocumented)
|
||||||
until skipped + 1024 > startb
|
until (increase_skipped = skipped + 1024) > startb
|
||||||
file.skip(1024)
|
file.skip(1024)
|
||||||
skipped += 1024
|
skipped = increase_skipped
|
||||||
end
|
end
|
||||||
if skipped - startb > 0
|
if (skipped_minus_startb = skipped - startb) > 0
|
||||||
file.skip(skipped - startb)
|
file.skip skipped_minus_startb
|
||||||
end
|
end
|
||||||
else
|
else
|
||||||
file.skip(startb)
|
file.skip(startb)
|
||||||
|
|
|
@ -1,9 +1,9 @@
|
||||||
module Kemal
|
module Kemal
|
||||||
module Utils
|
module Utils
|
||||||
ZIP_TYPES = [".htm", ".html", ".txt", ".css", ".js", ".svg", ".json", ".xml", ".otf", ".ttf", ".woff", ".woff2"]
|
ZIP_TYPES = {".htm", ".html", ".txt", ".css", ".js", ".svg", ".json", ".xml", ".otf", ".ttf", ".woff", ".woff2"}
|
||||||
|
|
||||||
def self.path_starts_with_slash?(path : String)
|
def self.path_starts_with_slash?(path : String)
|
||||||
path.starts_with?("/")
|
path.starts_with? '/'
|
||||||
end
|
end
|
||||||
|
|
||||||
def self.zip_types(path : String) # https://github.com/h5bp/server-configs-nginx/blob/master/nginx.conf
|
def self.zip_types(path : String) # https://github.com/h5bp/server-configs-nginx/blob/master/nginx.conf
|
||||||
|
|
|
@ -8,7 +8,7 @@ module Kemal
|
||||||
time = Time.now
|
time = Time.now
|
||||||
call_next(context)
|
call_next(context)
|
||||||
elapsed_text = elapsed_text(Time.now - time)
|
elapsed_text = elapsed_text(Time.now - time)
|
||||||
@io << time << " " << context.response.status_code << " " << context.request.method << " " << context.request.resource << " " << elapsed_text << "\n"
|
@io << time << ' ' << context.response.status_code << ' ' << context.request.method << ' ' << context.request.resource << ' ' << elapsed_text << '\n'
|
||||||
context
|
context
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -21,7 +21,7 @@ module Kemal
|
||||||
end
|
end
|
||||||
|
|
||||||
private def unescape_url_param(value : String)
|
private def unescape_url_param(value : String)
|
||||||
value.size == 0 ? value : URI.unescape(value)
|
value.empty? ? value : URI.unescape(value)
|
||||||
rescue
|
rescue
|
||||||
value
|
value
|
||||||
end
|
end
|
||||||
|
@ -74,19 +74,11 @@ module Kemal
|
||||||
end
|
end
|
||||||
|
|
||||||
private def parse_part(part : IO?)
|
private def parse_part(part : IO?)
|
||||||
if part
|
HTTP::Params.parse(part ? part.gets_to_end : "")
|
||||||
HTTP::Params.parse(part.gets_to_end)
|
|
||||||
else
|
|
||||||
HTTP::Params.parse("")
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
|
|
||||||
private def parse_part(part : String?)
|
private def parse_part(part : String?)
|
||||||
if part
|
HTTP::Params.parse part.to_s
|
||||||
HTTP::Params.parse(part.to_s)
|
|
||||||
else
|
|
||||||
HTTP::Params.parse("")
|
|
||||||
end
|
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
|
|
|
@ -56,7 +56,7 @@ module Kemal
|
||||||
end
|
end
|
||||||
|
|
||||||
private def radix_path(method, path)
|
private def radix_path(method, path)
|
||||||
"/#{method.downcase}#{path}"
|
'/' + method.downcase + path
|
||||||
end
|
end
|
||||||
|
|
||||||
private def add_to_radix_tree(method, path, route)
|
private def add_to_radix_tree(method, path, route)
|
||||||
|
|
|
@ -7,7 +7,9 @@ module Kemal
|
||||||
def call(context : HTTP::Server::Context)
|
def call(context : HTTP::Server::Context)
|
||||||
return call_next(context) if context.request.path.not_nil! == "/"
|
return call_next(context) if context.request.path.not_nil! == "/"
|
||||||
|
|
||||||
unless context.request.method == "GET" || context.request.method == "HEAD"
|
case context.request.method
|
||||||
|
when "GET", "HEAD"
|
||||||
|
else
|
||||||
if @fallthrough
|
if @fallthrough
|
||||||
call_next(context)
|
call_next(context)
|
||||||
else
|
else
|
||||||
|
@ -19,7 +21,6 @@ module Kemal
|
||||||
|
|
||||||
config = Kemal.config.serve_static
|
config = Kemal.config.serve_static
|
||||||
original_path = context.request.path.not_nil!
|
original_path = context.request.path.not_nil!
|
||||||
is_dir_path = original_path.ends_with? "/"
|
|
||||||
request_path = URI.unescape(original_path)
|
request_path = URI.unescape(original_path)
|
||||||
|
|
||||||
# File path cannot contains '\0' (NUL) because all filesystem I know
|
# File path cannot contains '\0' (NUL) because all filesystem I know
|
||||||
|
@ -30,16 +31,20 @@ module Kemal
|
||||||
end
|
end
|
||||||
|
|
||||||
expanded_path = File.expand_path(request_path, "/")
|
expanded_path = File.expand_path(request_path, "/")
|
||||||
if is_dir_path && !expanded_path.ends_with? "/"
|
is_dir_path = if original_path.ends_with?('/') && !expanded_path.ends_with? '/'
|
||||||
expanded_path = "#{expanded_path}/"
|
expanded_path = expanded_path + '/'
|
||||||
end
|
true
|
||||||
is_dir_path = expanded_path.ends_with? "/"
|
else
|
||||||
|
expanded_path.ends_with? '/'
|
||||||
|
end
|
||||||
|
|
||||||
file_path = File.join(@public_dir, expanded_path)
|
file_path = File.join(@public_dir, expanded_path)
|
||||||
is_dir = Dir.exists? file_path
|
is_dir = Dir.exists? file_path
|
||||||
|
|
||||||
if request_path != expanded_path || is_dir && !is_dir_path
|
if request_path != expanded_path
|
||||||
redirect_to context, "#{expanded_path}#{is_dir && !is_dir_path ? "/" : ""}"
|
redirect_to context, expanded_path
|
||||||
|
elsif is_dir && !is_dir_path
|
||||||
|
redirect_to context, expanded_path + '/'
|
||||||
end
|
end
|
||||||
|
|
||||||
if Dir.exists?(file_path)
|
if Dir.exists?(file_path)
|
||||||
|
|
|
@ -17,7 +17,7 @@ module Kemal
|
||||||
end
|
end
|
||||||
|
|
||||||
def lookup_ws_route(path : String)
|
def lookup_ws_route(path : String)
|
||||||
@routes.find "/ws#{path}"
|
@routes.find "/ws" + path
|
||||||
end
|
end
|
||||||
|
|
||||||
def add_route(path : String, &handler : HTTP::WebSocket, HTTP::Server::Context -> Void)
|
def add_route(path : String, &handler : HTTP::WebSocket, HTTP::Server::Context -> Void)
|
||||||
|
@ -30,12 +30,12 @@ module Kemal
|
||||||
end
|
end
|
||||||
|
|
||||||
private def radix_path(method, path)
|
private def radix_path(method, path)
|
||||||
"/#{method.downcase}#{path}"
|
'/' + method.downcase + path
|
||||||
end
|
end
|
||||||
|
|
||||||
private def websocket_upgrade_request?(context)
|
private def websocket_upgrade_request?(context)
|
||||||
return false unless upgrade = context.request.headers["Upgrade"]?
|
return unless upgrade = context.request.headers["Upgrade"]?
|
||||||
return false unless upgrade.compare("websocket", case_insensitive: true) == 0
|
return unless upgrade.compare("websocket", case_insensitive: true) == 0
|
||||||
|
|
||||||
context.request.headers.includes_word?("Connection", "Upgrade")
|
context.request.headers.includes_word?("Connection", "Upgrade")
|
||||||
end
|
end
|
||||||
|
|
Loading…
Reference in New Issue