Skip to content

Commit 871e979

Browse files
committed
Added HTTP_COOKIE and test cases.
Added WSF_COOKIE class, inherit from HTTP_COOKIE.
1 parent 5f4ab50 commit 871e979

File tree

3 files changed

+475
-0
lines changed

3 files changed

+475
-0
lines changed
Lines changed: 320 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,320 @@
1+
note
2+
description: "[
3+
This class represents the value of a HTTP cookie, transferred in a request.
4+
The class has features to build an HTTP cookie.
5+
6+
Following a newer RFC standard for Cookies RCF6265.
7+
8+
Domain
9+
* WARNING: Some existing user agents treat an absent Domain attribute as if the Domain attribute were present and contained the current host name.
10+
* For example, if example.com returns a Set-Cookie header without a Domain attribute, these user agents will erroneously send the cookie to www.example.com as well.
11+
12+
Max-Age, Expires
13+
* If a cookie has both the Max-Age and the Expires attribute, the Max-Age attribute has precedence and controls the expiration date of the cookie.
14+
* If a cookie has neither the Max-Age nor the Expires attribute, the user agent will retain the cookie until "the current session is over" (as defined by the user agent).
15+
* You will need to call the feature
16+
17+
HttpOnly, Secure
18+
* Note that the HttpOnly attribute is independent of the Secure attribute: a cookie can have both the HttpOnly and the Secure attribute.
19+
20+
]"
21+
date: "$Date$"
22+
revision: "$Revision$"
23+
EIS: "name=HTTP Cookie specification", "src=https://httpwg.github.io/specs/rfc6265.html", "protocol=uri"
24+
class
25+
HTTP_COOKIE
26+
27+
create
28+
make
29+
30+
feature {NONE} -- Initialization
31+
32+
make (a_name: READABLE_STRING_32; a_value: READABLE_STRING_32)
33+
-- Create an object instance of cookie with name `a_name' and value `a_value'.
34+
require
35+
make_sense: (a_name /= Void and a_value /= Void) and then (not a_name.is_empty and not a_value.is_empty)
36+
do
37+
set_name (a_name)
38+
set_value(a_value)
39+
set_max_age (-1)
40+
ensure
41+
name_set: name = a_name
42+
value_set: value = a_value
43+
max_age_set: max_age < 0
44+
end
45+
46+
feature -- Access
47+
48+
name: STRING_32
49+
-- name of the cookie.
50+
51+
value: STRING_32
52+
-- value of the cookie.
53+
54+
expiration: detachable STRING
55+
-- Value of the Expires attribute.
56+
57+
path: detachable STRING_32
58+
-- Value of the Path attribute.
59+
-- Path to which the cookie applies.
60+
--| The path "/", specify a cookie that apply to all URLs in your site.
61+
62+
domain: detachable STRING_32
63+
-- Value of the Domain attribute.
64+
-- Domain to which the cookies apply.
65+
66+
secure: BOOLEAN
67+
-- Value of the Secure attribute.
68+
-- By default False.
69+
--| Idicate if the cookie should only be sent over secured(encrypted connections, for example SSL).
70+
71+
http_only: BOOLEAN
72+
-- Value of the http_only attribute.
73+
-- By default false.
74+
--| Limits the scope of the cookie to HTTP requests.
75+
76+
max_age: INTEGER
77+
-- Value of the Max-Age attribute.
78+
--| How much time in seconds should elapsed before the cooki expires.
79+
--| By default max_age < 0 indicate a cookie will last only for the current user-agent (Browser, etc) session.
80+
--| A value of 0 instructs the user-agent to delete the cookie.
81+
82+
has_valid_characters (a_name: READABLE_STRING_32):BOOLEAN
83+
-- Has `a_name' valid characters for cookies?
84+
local
85+
l_iterator: STRING_ITERATION_CURSOR
86+
l_found: BOOLEAN
87+
do
88+
create l_iterator.make (a_name)
89+
from
90+
l_iterator.start
91+
until
92+
l_iterator.after or l_found
93+
loop
94+
if valid_characters.index_of (l_iterator.item.to_character_8, 0) = -1 then
95+
Result := False
96+
l_found := True
97+
else
98+
l_iterator.forth
99+
end
100+
end
101+
end
102+
103+
include_max_age: BOOLEAN
104+
-- Does the Set-Cookie header will include Max-Age attribute?
105+
--|By default will include both.
106+
107+
include_expires: BOOLEAN
108+
-- Does the Set-Cookie header will include Expires attribute?
109+
--|By default will include both.
110+
111+
feature -- Change Element
112+
113+
set_name (a_name: READABLE_STRING_32)
114+
-- Set `name' with `a_name'.
115+
do
116+
name := a_name
117+
ensure
118+
name_set: name = a_name
119+
end
120+
121+
set_value (a_value: READABLE_STRING_32)
122+
-- Set `value' with `a_value'.
123+
do
124+
value := a_value
125+
ensure
126+
value_set: value = a_value
127+
end
128+
129+
set_expiration (a_date: READABLE_STRING_32)
130+
-- Set `expiration' with `a_date'
131+
do
132+
expiration := a_date
133+
ensure
134+
expiration_set: attached expiration as l_expiration and then l_expiration.same_string (a_date)
135+
end
136+
137+
set_expiration_date (a_date: DATE_TIME)
138+
-- Set `expiration' with `a_date'
139+
do
140+
set_expiration (date_to_rfc1123_http_date_format (a_date))
141+
ensure
142+
expiration_set: attached expiration as l_expiration and then l_expiration.same_string (date_to_rfc1123_http_date_format (a_date))
143+
end
144+
145+
set_path (a_path: READABLE_STRING_32)
146+
-- Set `path' with `a_path'
147+
do
148+
path := a_path
149+
ensure
150+
path_set: path = a_path
151+
end
152+
153+
set_domain (a_domain: READABLE_STRING_32)
154+
-- Set `domain' with `a_domain'
155+
-- Note: you should avoid using "localhost" as `domain' for local cookies
156+
-- since they are not always handled by browser (for instance Chrome)
157+
require
158+
domain_without_port_info: a_domain /= Void implies a_domain.index_of (':', 1) = 0
159+
do
160+
domain := a_domain
161+
ensure
162+
domain_set: domain = a_domain
163+
end
164+
165+
set_secure (a_secure: BOOLEAN)
166+
-- Set `secure' with `a_secure'
167+
do
168+
secure := a_secure
169+
ensure
170+
secure_set: secure = a_secure
171+
end
172+
173+
set_http_only (a_http_only: BOOLEAN)
174+
-- Set `http_only' with `a_http_only'
175+
do
176+
http_only := a_http_only
177+
ensure
178+
http_only_set: http_only = a_http_only
179+
end
180+
181+
set_max_age (a_max_age: INTEGER)
182+
-- Set `max_age' with `a_max_age'
183+
do
184+
max_age := a_max_age
185+
ensure
186+
max_age_set: max_age = a_max_age
187+
end
188+
189+
190+
mark_max_age
191+
-- Set `include_max_age' to True.
192+
-- Set `include_expires' to False.
193+
-- Set-Cookie will include only Max-Age attribute and not Expires.
194+
do
195+
include_max_age := True
196+
include_expires := False
197+
ensure
198+
max_age_true: include_max_age
199+
expire_false: not include_expires
200+
end
201+
202+
mark_expires
203+
-- Set `include_expires' to True.
204+
-- Set `include_max_age' to False
205+
-- Set-Cookie will include only Expires attribute and not Max_Age.
206+
do
207+
include_expires := True
208+
include_max_age := False
209+
ensure
210+
expires_true: include_expires
211+
max_age_false: not include_max_age
212+
end
213+
214+
set_default_expires_max_age
215+
-- Set `include_expires' to False.
216+
-- Set `include_max_age' to False
217+
-- Set-Cookie will include both Max-Age, Expires attributes.
218+
do
219+
include_expires := False
220+
include_max_age := False
221+
ensure
222+
expires_false: not include_expires
223+
max_age_false: not include_max_age
224+
end
225+
226+
feature -- Date Utils
227+
228+
date_to_rfc1123_http_date_format (dt: DATE_TIME): STRING_8
229+
-- String representation of `dt' using the RFC 1123
230+
local
231+
d: HTTP_DATE
232+
do
233+
create d.make_from_date_time (dt)
234+
Result := d.string
235+
end
236+
237+
feature -- Output
238+
239+
cookie_header: STRING
240+
-- String representation of Set-Cookie header of current.
241+
local
242+
s: STRING
243+
do
244+
s := {HTTP_HEADER_NAMES}.header_set_cookie + colon_space + name + "=" + value
245+
if
246+
attached domain as l_domain and then not l_domain.same_string ("localhost")
247+
then
248+
s.append ("; Domain=")
249+
s.append (l_domain)
250+
end
251+
if attached path as l_path then
252+
s.append ("; Path=")
253+
s.append (l_path)
254+
end
255+
-- Expire
256+
if include_expires then
257+
if attached expiration as l_expires then
258+
s.append ("; Expires=")
259+
s.append (l_expires)
260+
end
261+
-- Max-Age
262+
elseif include_max_age then
263+
s.append ("; Max-Age=")
264+
s.append (max_age.out)
265+
else
266+
-- Default
267+
check default: (not include_expires) and (not include_max_age) end
268+
if attached expiration as l_expires then
269+
s.append ("; Expires=")
270+
s.append (l_expires)
271+
end
272+
273+
s.append ("; Max-Age=")
274+
s.append (max_age.out)
275+
end
276+
277+
if secure then
278+
s.append ("; Secure")
279+
end
280+
if http_only then
281+
s.append ("; HttpOnly")
282+
end
283+
Result := s
284+
end
285+
286+
feature {NONE} -- Constants
287+
288+
289+
colon_space: IMMUTABLE_STRING_8
290+
once
291+
create Result.make_from_string (": ")
292+
end
293+
294+
295+
legal_characters, valid_characters: SPECIAL [CHARACTER_8]
296+
-- RFC6265 that specifies that the following is valid for characters in cookies. Cookies are also supposed to be double quoted.
297+
-- The following character ranges are valid:http://tools.ietf.org/html/rfc6265#section-4.1.1
298+
-- %x21 / %x23-2B / %x2D-3A / %x3C-5B / %x5D-7E
299+
-- 0x21: !
300+
-- 0x23-2B: #$%&'()*+
301+
-- 0x2D-3A: -./0123456789:
302+
-- 0x3C-5B: <=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[
303+
-- 0x5D-7E: ]^_`abcdefghijklmnopqrstuvwxyz{|}~
304+
note
305+
EIS: "name=valid-characters", "src=http://tools.ietf.org/html/rfc6265#section-4.1.1", "protocol=uri"
306+
once
307+
Result := ("!#$%%&'()*+-./0123456789:<=>?@ABCDEFGHIJKLMNOPQRSTUVWXYZ[]^_`abcdefghijklmnopqrstuvwxyz{|}~").area
308+
end
309+
310+
note
311+
copyright: "2011-2015, Jocelyn Fiat, Eiffel Software and others"
312+
license: "Eiffel Forum License v2 (see http://www.eiffel.com/licensing/forum.txt)"
313+
source: "[
314+
Eiffel Software
315+
5949 Hollister Ave., Goleta, CA 93117 USA
316+
Telephone 805-685-1006, Fax 805-685-6869
317+
Website http://www.eiffel.com
318+
Customer support http://support.eiffel.com
319+
]"
320+
end

0 commit comments

Comments
 (0)