summaryrefslogtreecommitdiff
path: root/lib
diff options
context:
space:
mode:
Diffstat (limited to 'lib')
-rw-r--r--lib/zero/response.rb26
-rw-r--r--lib/zero/response/cookie.rb105
2 files changed, 125 insertions, 6 deletions
diff --git a/lib/zero/response.rb b/lib/zero/response.rb
index ab4acde..cb58cfe 100644
--- a/lib/zero/response.rb
+++ b/lib/zero/response.rb
@@ -1,3 +1,5 @@
+require 'zero/response/cookie'
+
module Zero
# This is the representation of a response
class Response
@@ -10,18 +12,17 @@ module Zero
# Constructor
# Sets default status code to 200.
- #
def initialize
@status = 200
@header = {}
@body = []
+ @cookies = {}
end
# Sets the status.
# Also converts every input directly to an integer.
#
# @param [Integer] status The status code
- #
def status=(status)
@status = status.to_i
end
@@ -53,8 +54,8 @@ module Zero
# Removes Content-Type, Content-Length and body on status code 204 and 304.
#
# @return [Array] Usable by webservers
- #
def to_a
+ add_cookie_headers
# Remove content length and body, on status 204 and 304
if status == 204 or status == 304
header.delete('Content-Length')
@@ -72,7 +73,6 @@ module Zero
# Sets the content length header to the current length of the body
# Also creates one, if it does not exists
- #
def content_length
self.header['Content-Length'] = body.join.bytesize.to_s
end
@@ -81,7 +81,6 @@ module Zero
# Also creates it, if it does not exists
#
# @param [String] value Content-Type tp set
- #
def content_type=(value)
self.header['Content-Type'] = value
end
@@ -89,10 +88,25 @@ module Zero
# Sets the Location header to the given URL and the status code to 302.
#
# @param [String] location Redirect URL
- #
def redirect(location, status = 302)
self.status = status
self.header['Location'] = location
end
+
+ # get the cookie for the response
+ #
+ # This returns the cookie holding all crumbs for the response.
+ # @response [Cookie] the cookie with crumbs holding the information
+ def cookie
+ @cookie ||= Cookie.new
+ end
+
+ private
+
+ # merge the cookie header into the other headers
+ def add_cookie_headers
+ return unless @cookie
+ header.merge!(cookie.to_header)
+ end
end
end
diff --git a/lib/zero/response/cookie.rb b/lib/zero/response/cookie.rb
new file mode 100644
index 0000000..45ef65f
--- /dev/null
+++ b/lib/zero/response/cookie.rb
@@ -0,0 +1,105 @@
+module Zero
+ class Response
+ class Cookie
+ # initialize an empty cookie
+ def initialize
+ @crumbs = {}
+ end
+
+ # add a new crumb
+ #
+ # This adds a new crumb to the cookie specified through the key.
+ # @param key [String] the identifier for the crumb
+ # @param value [String] the value for the crumb
+ # @param options [Hash] hash with further options for the crumb
+ # @option options [Time] :expire the time when the crumb should expire
+ # @option options [String] :domain the domain the crumb should be sent to
+ # @option options [String] :path path when the crumb should be sent
+ # @option options [Array] :flags set flags for :secure or :http_only
+ def add_crumb(key, value, options = {:flags => []})
+ @crumbs[key] = Crumb.new(key, value, options)
+ end
+
+ # get the crumb for the key
+ #
+ # This method returns the crumb for the specified key. The crumb holds all
+ # information, like the expire time and domain and so on.
+ # @param key [String] the key to return
+ # @returns [Cookie::Crumb] a cookie crumb or nil when the key
+ # does not exist
+ def get_crumb(key)
+ @crumbs[key]
+ end
+
+ # merge all crumbs to one header line
+ #
+ # This merges all crumbs together to a header line, where each cookie is
+ # separated by the `Set-Cookie` header.
+ # @returns [Hash] a key value pair to merge with the headers
+ def to_header
+ {'Set-Cookie' => @crumbs.map{|key, crumb| crumb}.join("\nSet-Cookie: ")}
+ end
+
+ private
+
+ class Crumb
+ attr_reader :key, :secure, :http_only
+ attr_accessor :domain, :path, :expire, :value
+
+ def initialize(key, value, options = {})
+ options[:flags] ||= []
+ @key = key
+ @value = value
+ @domain = options[:domain]
+ @expire = options[:expire]
+ @path = options[:path]
+ @secure = options[:flags].include?(:secure)
+ @http_only = options[:flags].include?(:http_only)
+ end
+
+ # set the `http_only` flag
+ #
+ # This method sets the flag to only allow modifications from the
+ # server and makes the browser not allow modifications through
+ # javascript.
+ def deny_client_side_modification!
+ @http_only = true
+ end
+
+ # remove the `http_only` flag
+ #
+ # This removes the `http_only` flag to allow modifications of the
+ # crumb through javascript.
+ def allow_client_side_modification!
+ @http_only = false
+ end
+
+ # set the `secure` flag
+ #
+ # This sets the `secure` flag on the crumb which tells the browser to
+ # only send it through secure channels, like https.
+ # Keep in mind, that this does not encrypt the content of the Crumb!
+ def secure!
+ @secure = true
+ end
+
+ # unset the `secure` flag
+ #
+ # This unsets the `secure` flag which tells the browser, that it can
+ # send the crumb over unsecure channel too, like plain http.
+ def unsecure!
+ @secure = false
+ end
+
+ def to_s
+ "#{@key}=#{@value}" +
+ (@expire ? "; Expires=#{@expire.rfc2822}" : '') +
+ (@path ? "; Path=#{@path}" : '') +
+ (@domain ? "; Domain=#{domain}" : '') +
+ (@http_only ? '; HttpOnly' : '') +
+ (@secure ? '; Secure' : '')
+ end
+ end
+ end
+ end
+end