source: node_modules/cookie/index.js

main
Last change on this file was d24f17c, checked in by Aleksandar Panovski <apano77@…>, 15 months ago

Initial commit

  • Property mode set to 100644
File size: 5.1 KB
Line 
1/*!
2 * cookie
3 * Copyright(c) 2012-2014 Roman Shtylman
4 * Copyright(c) 2015 Douglas Christopher Wilson
5 * MIT Licensed
6 */
7
8'use strict';
9
10/**
11 * Module exports.
12 * @public
13 */
14
15exports.parse = parse;
16exports.serialize = serialize;
17
18/**
19 * Module variables.
20 * @private
21 */
22
23var __toString = Object.prototype.toString
24
25/**
26 * RegExp to match field-content in RFC 7230 sec 3.2
27 *
28 * field-content = field-vchar [ 1*( SP / HTAB ) field-vchar ]
29 * field-vchar = VCHAR / obs-text
30 * obs-text = %x80-FF
31 */
32
33var fieldContentRegExp = /^[\u0009\u0020-\u007e\u0080-\u00ff]+$/;
34
35/**
36 * Parse a cookie header.
37 *
38 * Parse the given cookie header string into an object
39 * The object has the various cookies as keys(names) => values
40 *
41 * @param {string} str
42 * @param {object} [options]
43 * @return {object}
44 * @public
45 */
46
47function parse(str, options) {
48 if (typeof str !== 'string') {
49 throw new TypeError('argument str must be a string');
50 }
51
52 var obj = {}
53 var opt = options || {};
54 var dec = opt.decode || decode;
55
56 var index = 0
57 while (index < str.length) {
58 var eqIdx = str.indexOf('=', index)
59
60 // no more cookie pairs
61 if (eqIdx === -1) {
62 break
63 }
64
65 var endIdx = str.indexOf(';', index)
66
67 if (endIdx === -1) {
68 endIdx = str.length
69 } else if (endIdx < eqIdx) {
70 // backtrack on prior semicolon
71 index = str.lastIndexOf(';', eqIdx - 1) + 1
72 continue
73 }
74
75 var key = str.slice(index, eqIdx).trim()
76
77 // only assign once
78 if (undefined === obj[key]) {
79 var val = str.slice(eqIdx + 1, endIdx).trim()
80
81 // quoted values
82 if (val.charCodeAt(0) === 0x22) {
83 val = val.slice(1, -1)
84 }
85
86 obj[key] = tryDecode(val, dec);
87 }
88
89 index = endIdx + 1
90 }
91
92 return obj;
93}
94
95/**
96 * Serialize data into a cookie header.
97 *
98 * Serialize the a name value pair into a cookie string suitable for
99 * http headers. An optional options object specified cookie parameters.
100 *
101 * serialize('foo', 'bar', { httpOnly: true })
102 * => "foo=bar; httpOnly"
103 *
104 * @param {string} name
105 * @param {string} val
106 * @param {object} [options]
107 * @return {string}
108 * @public
109 */
110
111function serialize(name, val, options) {
112 var opt = options || {};
113 var enc = opt.encode || encode;
114
115 if (typeof enc !== 'function') {
116 throw new TypeError('option encode is invalid');
117 }
118
119 if (!fieldContentRegExp.test(name)) {
120 throw new TypeError('argument name is invalid');
121 }
122
123 var value = enc(val);
124
125 if (value && !fieldContentRegExp.test(value)) {
126 throw new TypeError('argument val is invalid');
127 }
128
129 var str = name + '=' + value;
130
131 if (null != opt.maxAge) {
132 var maxAge = opt.maxAge - 0;
133
134 if (isNaN(maxAge) || !isFinite(maxAge)) {
135 throw new TypeError('option maxAge is invalid')
136 }
137
138 str += '; Max-Age=' + Math.floor(maxAge);
139 }
140
141 if (opt.domain) {
142 if (!fieldContentRegExp.test(opt.domain)) {
143 throw new TypeError('option domain is invalid');
144 }
145
146 str += '; Domain=' + opt.domain;
147 }
148
149 if (opt.path) {
150 if (!fieldContentRegExp.test(opt.path)) {
151 throw new TypeError('option path is invalid');
152 }
153
154 str += '; Path=' + opt.path;
155 }
156
157 if (opt.expires) {
158 var expires = opt.expires
159
160 if (!isDate(expires) || isNaN(expires.valueOf())) {
161 throw new TypeError('option expires is invalid');
162 }
163
164 str += '; Expires=' + expires.toUTCString()
165 }
166
167 if (opt.httpOnly) {
168 str += '; HttpOnly';
169 }
170
171 if (opt.secure) {
172 str += '; Secure';
173 }
174
175 if (opt.priority) {
176 var priority = typeof opt.priority === 'string'
177 ? opt.priority.toLowerCase()
178 : opt.priority
179
180 switch (priority) {
181 case 'low':
182 str += '; Priority=Low'
183 break
184 case 'medium':
185 str += '; Priority=Medium'
186 break
187 case 'high':
188 str += '; Priority=High'
189 break
190 default:
191 throw new TypeError('option priority is invalid')
192 }
193 }
194
195 if (opt.sameSite) {
196 var sameSite = typeof opt.sameSite === 'string'
197 ? opt.sameSite.toLowerCase() : opt.sameSite;
198
199 switch (sameSite) {
200 case true:
201 str += '; SameSite=Strict';
202 break;
203 case 'lax':
204 str += '; SameSite=Lax';
205 break;
206 case 'strict':
207 str += '; SameSite=Strict';
208 break;
209 case 'none':
210 str += '; SameSite=None';
211 break;
212 default:
213 throw new TypeError('option sameSite is invalid');
214 }
215 }
216
217 return str;
218}
219
220/**
221 * URL-decode string value. Optimized to skip native call when no %.
222 *
223 * @param {string} str
224 * @returns {string}
225 */
226
227function decode (str) {
228 return str.indexOf('%') !== -1
229 ? decodeURIComponent(str)
230 : str
231}
232
233/**
234 * URL-encode value.
235 *
236 * @param {string} str
237 * @returns {string}
238 */
239
240function encode (val) {
241 return encodeURIComponent(val)
242}
243
244/**
245 * Determine if value is a Date.
246 *
247 * @param {*} val
248 * @private
249 */
250
251function isDate (val) {
252 return __toString.call(val) === '[object Date]' ||
253 val instanceof Date
254}
255
256/**
257 * Try decoding a string using a decoding function.
258 *
259 * @param {string} str
260 * @param {function} decode
261 * @private
262 */
263
264function tryDecode(str, decode) {
265 try {
266 return decode(str);
267 } catch (e) {
268 return str;
269 }
270}
Note: See TracBrowser for help on using the repository browser.