Remove redundant session stuff
This commit is contained in:
parent
9696e6c344
commit
ac8ec0a07b
4 changed files with 1 additions and 125 deletions
|
@ -23,14 +23,6 @@ describe "Config" do
|
||||||
config.host_binding.should eq "127.0.0.1"
|
config.host_binding.should eq "127.0.0.1"
|
||||||
end
|
end
|
||||||
|
|
||||||
it "sets session values" do
|
|
||||||
config = Kemal.config
|
|
||||||
config.session["name"] = "kemal"
|
|
||||||
config.session["expire_time"] = 1.hours
|
|
||||||
config.session["name"].as(String).should eq "kemal"
|
|
||||||
config.session["expire_time"].as(Time::Span).should eq 1.hours
|
|
||||||
end
|
|
||||||
|
|
||||||
it "adds a custom handler" do
|
it "adds a custom handler" do
|
||||||
config = Kemal.config
|
config = Kemal.config
|
||||||
config.add_handler CustomTestHandler.new
|
config.add_handler CustomTestHandler.new
|
||||||
|
|
|
@ -18,8 +18,6 @@ module Kemal
|
||||||
config.server.tls = config.ssl
|
config.server.tls = config.ssl
|
||||||
{% end %}
|
{% end %}
|
||||||
|
|
||||||
Kemal::Sessions.run_reaper!
|
|
||||||
|
|
||||||
unless Kemal.config.error_handlers.has_key?(404)
|
unless Kemal.config.error_handlers.has_key?(404)
|
||||||
error 404 do |env|
|
error 404 do |env|
|
||||||
render_404
|
render_404
|
||||||
|
|
|
@ -15,14 +15,13 @@ module Kemal
|
||||||
{% end %}
|
{% end %}
|
||||||
|
|
||||||
property host_binding, ssl, port, env, public_folder, logging, running,
|
property host_binding, ssl, port, env, public_folder, logging, running,
|
||||||
always_rescue, serve_static : (Bool | Hash(String, Bool)), server, session : Hash(String, Time::Span | String), extra_options
|
always_rescue, serve_static : (Bool | Hash(String, Bool)), server, extra_options
|
||||||
|
|
||||||
def initialize
|
def initialize
|
||||||
@host_binding = "0.0.0.0"
|
@host_binding = "0.0.0.0"
|
||||||
@port = 3000
|
@port = 3000
|
||||||
@env = "development"
|
@env = "development"
|
||||||
@serve_static = {"dir_listing" => false, "gzip" => true}
|
@serve_static = {"dir_listing" => false, "gzip" => true}
|
||||||
@session = {"name" => "kemal_session", "expire_time" => 48.hours}
|
|
||||||
@public_folder = "./public"
|
@public_folder = "./public"
|
||||||
@logging = true
|
@logging = true
|
||||||
@logger = nil
|
@logger = nil
|
||||||
|
|
|
@ -1,113 +0,0 @@
|
||||||
require "secure_random"
|
|
||||||
|
|
||||||
module Kemal
|
|
||||||
# Kemal's default session is in-memory only and holds simple String values only.
|
|
||||||
# The client-side cookie stores a random ID.
|
|
||||||
#
|
|
||||||
# Kemal handlers can access the session like so:
|
|
||||||
#
|
|
||||||
# get("/") do |env|
|
|
||||||
# env.session["abc"] = "xyz"
|
|
||||||
# uid = env.session["user_id"]?.as(Int32)
|
|
||||||
# end
|
|
||||||
#
|
|
||||||
# Note that only String values are allowed.
|
|
||||||
#
|
|
||||||
# Sessions are pruned hourly after 48 hours of inactivity.
|
|
||||||
class Sessions
|
|
||||||
# :nodoc:
|
|
||||||
alias SessionTypes = String | Int32 | Float64 | Bool
|
|
||||||
|
|
||||||
# In-memory, ephemeral datastore only.
|
|
||||||
#
|
|
||||||
# Implementing Redis or Memcached as a datastore
|
|
||||||
# is left as an exercise to another reader.
|
|
||||||
#
|
|
||||||
# Note that the only thing we store on the client-side
|
|
||||||
# is an opaque, random String. If we actually wanted to
|
|
||||||
# store any data, we'd need to implement encryption, key
|
|
||||||
# rotation, tamper-detection and that whole iceberg.
|
|
||||||
STORE = Hash(String, Session).new
|
|
||||||
|
|
||||||
class Session
|
|
||||||
getter! id : String
|
|
||||||
property! last_access_at : Int64
|
|
||||||
|
|
||||||
def initialize(@id)
|
|
||||||
@last_access_at = Time.new.epoch_ms
|
|
||||||
@store = Hash(String, SessionTypes).new
|
|
||||||
end
|
|
||||||
|
|
||||||
def [](key : String)
|
|
||||||
@last_access_at = Time.now.epoch_ms
|
|
||||||
@store[key]
|
|
||||||
end
|
|
||||||
|
|
||||||
def []?(key : String)
|
|
||||||
@last_access_at = Time.now.epoch_ms
|
|
||||||
@store[key]?
|
|
||||||
end
|
|
||||||
|
|
||||||
def []=(key : String, value : SessionTypes)
|
|
||||||
@last_access_at = Time.now.epoch_ms
|
|
||||||
@store[key] = value
|
|
||||||
end
|
|
||||||
|
|
||||||
def delete(key : String)
|
|
||||||
@last_access_at = Time.now.epoch_ms
|
|
||||||
@store.delete(key)
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
getter! id : String
|
|
||||||
|
|
||||||
def initialize(ctx : HTTP::Server::Context)
|
|
||||||
id = ctx.request.cookies[Kemal.config.session["name"].as(String)]?.try &.value
|
|
||||||
if id && id.size == 32
|
|
||||||
# valid
|
|
||||||
else
|
|
||||||
# new or invalid
|
|
||||||
id = SecureRandom.hex
|
|
||||||
end
|
|
||||||
|
|
||||||
ctx.response.cookies << HTTP::Cookie.new(name: Kemal.config.session["name"].as(String), value: id, http_only: true)
|
|
||||||
@id = id
|
|
||||||
end
|
|
||||||
|
|
||||||
def []=(key : String, value : SessionTypes)
|
|
||||||
store = STORE[id]? || begin
|
|
||||||
STORE[id] = Session.new(id)
|
|
||||||
end
|
|
||||||
store[key] = value
|
|
||||||
end
|
|
||||||
|
|
||||||
def [](key : String)
|
|
||||||
STORE[@id][key]
|
|
||||||
end
|
|
||||||
|
|
||||||
def []?(key : String)
|
|
||||||
STORE[@id]?.try &.[key]?
|
|
||||||
end
|
|
||||||
|
|
||||||
def delete(key : String)
|
|
||||||
STORE[@id]?.try &.delete(key)
|
|
||||||
end
|
|
||||||
|
|
||||||
def self.prune!(before = (Time.now - Kemal.config.session["expire_time"].as(Time::Span)).epoch_ms)
|
|
||||||
Kemal::Sessions::STORE.delete_if { |id, entry| entry.last_access_at < before }
|
|
||||||
nil
|
|
||||||
end
|
|
||||||
|
|
||||||
# This is an hourly job to prune the in-memory hash of any
|
|
||||||
# sessions which have expired due to inactivity, otherwise
|
|
||||||
# we'll have a slow memory leak and possible DDoS vector.
|
|
||||||
def self.run_reaper!
|
|
||||||
spawn do
|
|
||||||
loop do
|
|
||||||
prune!
|
|
||||||
sleep 3600
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
Loading…
Reference in a new issue