以下源码摘自 /Users/cbd/.rvm/rubies/ruby-2.3.0/lib/ruby/2.3.0/cgi/session.rb
# frozen_string_literal: false
#
# cgi/session.rb - session support for cgi scripts
#
# Copyright (C) 2001 Yukihiro "Matz" Matsumoto
# Copyright (C) 2000 Network Applied Communication Laboratory, Inc.
# Copyright (C) 2000 Information-technology Promotion Agency, Japan
#
# 简体中文翻译 2016 Cbd-Focus, Shanghai
#
# Author: Yukihiro "Matz" Matsumoto
#
# Documentation: William Webber ([email protected])
require 'cgi'
require 'tmpdir'
class CGI
# == 概述
#
# 本文件构建CGI::Session类,为CGI脚本提供session支持.
# session是关联单一客户端的一连串相互链接的HTTP请求和响应。
# 在各个请求间,与session关联的信息存储在服务器上。
# 客户端和服务器在每个请求和响应之间传递session id.
# 由此,为无状态的HTTP请求和响应协议加入了状态信息。
#
# == 生命周期
#
# CGI::Session实例由CGI对象创建.
# 默认情况下,若该客户端的CGI::Session实例已存在,则沿用现有session,若不存在就新实例化一个.
# 通过new_session 选项可以来设置'总是'或者'永不'创建新的session.
# 详见#new() .
#
# #delete() 从session库中删除一个session,然而它并没有从客户端删除session id.
# 如果客户端在另一个请求里使用与之相同的session id,则新建的session会沿用这个老的 session id.
#
# == session 数据的赋值取值
#
# Session 类采用键值对儿来维护session数据.
# 可以用'[]'方法,通过索引来给session实例赋值和取值,这很像哈希(并没有支持哈希的其他方法).
#
# 对于这个请求,当session处理完成后,需要调用close()方法关闭session.这会持久存储session的状态.
# 如果你想持久存储一个还未完结的session处理,调用update()方法.
#
# == session状态存储
# 调用者可以在CGI::Session::new 用 database_manager 来指定session数据的存储样式.
# 下面是标准库里提供的几种存储类型:
# CGI::Session::Filestore:: 在文件中存储纯文本.只用于字符串数据.也是默认的存储类型.
# CGI::Session::MemoryStore:: 存在内存哈希中.数据只在当前的ruby解释器运行时存在.
# CGI::Session::PStore:: 以整理后的格式存储.功能由cgi/session/pstore.rb实现.支持任意格式数据,并提供文件锁和事务.
#
# 当然也可DIY一种存储方式,只需定义类实现下面方法:
# new(session,options)
# restore #返回session数据的哈希
# update
# close
# delete
#
# Changing storage type mid-session does not work. Note in particular
# that by default the FileStore and PStore session data files have the
# same name. If your application switches from one to the other without
# making sure that filenames will be different
# and clients still have old sessions lying around in cookies, then
# things will break nastily!
#
# == 维护session id
# 大多数session状态在服务器上维护,而session id必须在客户端和服务端来回传递.
# 最简单的方式是使用cookies.
# 当客户端启用cookies,CGI::Session类 会通过cookies来传递session id.
# 当客户端禁用cookies,session id就必须作为参数包在请求里发送给服务器.
# CGI::Session类 协同 CGI类,将会在所有生成的表单里添加包含sessionid的隐藏域(通过 CGI#form()方法生成).
#
# 为其他机制提供非内建支持,例如 URL重写.
# 当使用其他机制时,调用者需要通过session_id属性来提取session id,并手工编码URL,手工在HTML表单中加入隐藏域.
# == 使用样例
#
# === Setting the user's name
#
# require 'cgi'
# require 'cgi/session'
# require 'cgi/session/pstore' # provides CGI::Session::PStore
#
# cgi = CGI.new("html4")
#
# session = CGI::Session.new(cgi,
# 'database_manager' => CGI::Session::PStore, # use PStore
# 'session_key' => '_rb_sess_id', # custom session key
# 'session_expires' => Time.now + 30 * 60, # 30 minute timeout
# 'prefix' => 'pstore_sid_') # PStore option
# if cgi.has_key?('user_name') and cgi['user_name'] != ''
# # coerce to String: cgi[] returns the
# # string-like CGI::QueryExtension::Value
# session['user_name'] = cgi['user_name'].to_s
# elsif !session['user_name']
# session['user_name'] = "guest"
# end
# session.close
#
# === Creating a new session safely
#
# require 'cgi'
# require 'cgi/session'
#
# cgi = CGI.new("html4")
#
# # We make sure to delete an old session if one exists,
# # not just to free resources, but to prevent the session
# # from being maliciously hijacked later on.
# begin
# session = CGI::Session.new(cgi, 'new_session' => false)
# session.delete
# rescue ArgumentError # if no old session
# end
# session = CGI::Session.new(cgi, 'new_session' => true)
# session.close
#
class Session
class NoSession < RuntimeError #:nodoc:
end
# The id of this session.
attr_reader :session_id, :new_session
def Session::callback(dbman) #:nodoc:
Proc.new{
dbman[0].close unless dbman.empty?
}
end
# Create a new session id.
#
# The session id is a secure random number by SecureRandom
# if possible, otherwise an SHA512 hash based upon the time,
# a random number, and a constant string. This routine is
# used internally for automatically generated session ids.
def create_new_id
require 'securerandom'
begin
# 通过 OpenSSL生成,或者 系统提供的内核熵池
session_id = SecureRandom.hex(16)
rescue NotImplementedError
# 在现代操作系统上永远不会出现这个异常
# 如果系统不支持SecureRandom的随机串儿生成方式,就用SHA512
require 'digest'
d = Digest('SHA512').new
now = Time::now
d.update(now.to_s)
d.update(String(now.usec))
d.update(String(rand(0)))
d.update(String($$))
d.update('foobar')
session_id = d.hexdigest[0, 32]
end
session_id
end
private :create_new_id
# 为+request+新实例化一个 CGI::Session 对象
#
# +request+ 是一个+CGI+类的实例(见 cgi.rb).
# +option+ is a hash of options for initialising this
# +option+ 是初始化这个 CGI:Session 实例的选项,哈希结构.
# 可识别下列选项:
#
# session_key:: the parameter name used for the session id.
# Defaults to '_session_id'.
# session_id:: the session id to use. If not provided, then
# it is retrieved from the +session_key+ parameter
# of the request, or automatically generated for
# a new session.
# new_session:: if true, force creation of a new session. If not set,
# a new session is only created if none currently
# exists. If false, a new session is never created,
# and if none currently exists and the +session_id+
# option is not set, an ArgumentError is raised.
# database_manager:: the name of the class providing storage facilities
# for session state persistence. Built-in support
# is provided for +FileStore+ (the default),
# +MemoryStore+, and +PStore+ (from
# cgi/session/pstore.rb). See the documentation for
# these classes for more details.
#
# The following options are also recognised, but only apply if the
# session id is stored in a cookie.
#
# session_expires:: the time the current session expires, as a
# +Time+ object. If not set, the session will terminate
# when the user's browser is closed.
# session_domain:: the hostname domain for which this session is valid.
# If not set, defaults to the hostname of the server.
# session_secure:: if +true+, this session will only work over HTTPS.
# session_path:: the path for which this session applies. Defaults
# to the directory of the CGI script.
#
# +option+ is also passed on to the session storage class initializer; see
# the documentation for each session storage class for the options
# they support.
#
# The retrieved or created session is automatically added to +request+
# as a cookie, and also to its +output_hidden+ table, which is used
# to add hidden input elements to forms.
#
# *WARNING* the +output_hidden+
# fields are surrounded by a