Add allocation optimizations (#487)

This commit is contained in:
Julien Reichardt 2018-11-01 12:29:05 +01:00 committed by Serdar Dogruyol
parent afd17fc774
commit 21ab64511a
12 changed files with 59 additions and 82 deletions

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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)

View File

@ -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)

View File

@ -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