Session.configure do |config|
config.secret = ENV["SESSION_SECRET"]
config.timeout = 24.hours
config.session_key = "_myapp_session"
config.provider = Session::MemoryStore(UserSession).provider
end
require "http/server"
server = HTTP::Server.new([
Session::SessionHandler.new(Session.provider),
MyAppHandler.new,
])
server.listen(8080)
puts "Server listening on http://localhost:8080"
class MyAppHandler
include HTTP::Handler
def call(context : HTTP::Server::Context)
provider = Session.provider
case context.request.path
when "/login"
handle_login(context, provider)
when "/profile"
handle_profile(context, provider)
when "/logout"
handle_logout(context, provider)
else
context.response.print "Hello! <a href='/login'>Login</a>"
end
end
private def handle_login(context, provider)
# Create or regenerate session
provider.regenerate_id
provider.data.user_id = 1
provider.data.username = "alice"
provider.data.role = "admin"
provider.flash["notice"] = "Welcome back!"
context.response.status = HTTP::Status::FOUND
context.response.headers["Location"] = "/profile"
end
private def handle_profile(context, provider)
if provider.data.user_id
notice = provider.flash.now["notice"]?
html = <<-HTML
#{notice ? "<p style='color:green'>#{notice}</p>" : ""}
<h1>Profile</h1>
<p>User: #{provider.data.username}</p>
<p>Role: #{provider.data.role}</p>
<a href="/logout">Logout</a>
HTML
context.response.print html
else
context.response.status = HTTP::Status::FOUND
context.response.headers["Location"] = "/login"
end
end
private def handle_logout(context, provider)
provider.delete
provider.flash["notice"] = "You have been logged out"
context.response.status = HTTP::Status::FOUND
context.response.headers["Location"] = "/"
end
end
class CustomSessionHandler
include HTTP::Handler
def initialize(@provider : Session::Provider(UserSession))
end
def call(context : HTTP::Server::Context)
# Load session from request cookies
@provider.load_from(context.request.cookies)
begin
call_next(context)
ensure
# Always set session cookie in response
@provider.set_cookies(context.response.cookies)
end
rescue Session::SessionExpiredException
handle_expired_session(context)
rescue Session::SessionCorruptionException
handle_corrupted_session(context)
end
private def handle_expired_session(context)
@provider.create
call_next(context)
@provider.set_cookies(context.response.cookies)
end
private def handle_corrupted_session(context)
@provider.create
@provider.flash["error"] = "Your session was invalid. Please log in again."
call_next(context)
@provider.set_cookies(context.response.cookies)
end
end
require "http/server"
require "session"
# Define session structure
struct UserSession
include Session::SessionData
property user_id : Int64?
property username : String?
property cart_items : Array(Int64) = [] of Int64
end
# Configure
Session.configure do |config|
config.secret = ENV.fetch("SESSION_SECRET", "dev-secret-change-in-production!")
config.timeout = 1.hour
config.sliding_expiration = true
config.provider = Session::MemoryStore(UserSession).provider
end
# Application handler
class AppHandler
include HTTP::Handler
def call(context : HTTP::Server::Context)
provider = Session.provider
case {context.request.method, context.request.path}
when {"GET", "/"}
render_home(context, provider)
when {"POST", "/cart/add"}
add_to_cart(context, provider)
when {"GET", "/cart"}
show_cart(context, provider)
else
context.response.status = HTTP::Status::NOT_FOUND
context.response.print "Not Found"
end
end
private def render_home(context, provider)
cart_count = provider.data.cart_items.size
context.response.content_type = "text/html"
context.response.print <<-HTML
<h1>Shop</h1>
<p>Cart: #{cart_count} items</p>
<form method="POST" action="/cart/add">
<input type="hidden" name="item_id" value="#{rand(1000)}">
<button>Add Random Item</button>
</form>
<a href="/cart">View Cart</a>
HTML
end
private def add_to_cart(context, provider)
if item_id = context.request.form_params["item_id"]?
provider.data.cart_items << item_id.to_i64
provider.flash["notice"] = "Item added to cart!"
end
context.response.status = HTTP::Status::FOUND
context.response.headers["Location"] = "/"
end
private def show_cart(context, provider)
items = provider.data.cart_items
context.response.content_type = "text/html"
context.response.print <<-HTML
<h1>Your Cart</h1>
<ul>
#{items.map { |id| "<li>Item ##{id}</li>" }.join}
</ul>
<a href="/">Continue Shopping</a>
HTML
end
end
# Start server
server = HTTP::Server.new([
Session::SessionHandler.new(Session.provider),
AppHandler.new,
])
address = server.bind_tcp(8080)
puts "Listening on http://#{address}"
server.listen