tough-cookie 0.12.1 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/lib/cookie.js CHANGED
@@ -1,29 +1,40 @@
1
- /*
2
- * Copyright GoInstant, Inc. and other contributors. All rights reserved.
3
- * Permission is hereby granted, free of charge, to any person obtaining a copy
4
- * of this software and associated documentation files (the "Software"), to
5
- * deal in the Software without restriction, including without limitation the
6
- * rights to use, copy, modify, merge, publish, distribute, sublicense, and/or
7
- * sell copies of the Software, and to permit persons to whom the Software is
8
- * furnished to do so, subject to the following conditions:
1
+ /*!
2
+ * Copyright (c) 2015, Salesforce.com, Inc.
3
+ * All rights reserved.
4
+ *
5
+ * Redistribution and use in source and binary forms, with or without
6
+ * modification, are permitted provided that the following conditions are met:
7
+ *
8
+ * 1. Redistributions of source code must retain the above copyright notice,
9
+ * this list of conditions and the following disclaimer.
10
+ *
11
+ * 2. Redistributions in binary form must reproduce the above copyright notice,
12
+ * this list of conditions and the following disclaimer in the documentation
13
+ * and/or other materials provided with the distribution.
9
14
  *
10
- * The above copyright notice and this permission notice shall be included in
11
- * all copies or substantial portions of the Software.
15
+ * 3. Neither the name of Salesforce.com nor the names of its contributors may
16
+ * be used to endorse or promote products derived from this software without
17
+ * specific prior written permission.
12
18
  *
13
- * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
- * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
- * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
- * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
- * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
18
- * FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS
19
- * IN THE SOFTWARE.
19
+ * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
20
+ * AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
21
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
22
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
23
+ * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
24
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
25
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
26
+ * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
27
+ * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
28
+ * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
29
+ * POSSIBILITY OF SUCH DAMAGE.
20
30
  */
21
-
22
31
  'use strict';
23
32
  var net = require('net');
24
33
  var urlParse = require('url').parse;
25
34
  var pubsuffix = require('./pubsuffix');
26
35
  var Store = require('./store').Store;
36
+ var MemoryCookieStore = require('./memstore').MemoryCookieStore;
37
+ var pathMatch = require('./pathMatch').pathMatch;
27
38
 
28
39
  var punycode;
29
40
  try {
@@ -34,42 +45,29 @@ try {
34
45
 
35
46
  var DATE_DELIM = /[\x09\x20-\x2F\x3B-\x40\x5B-\x60\x7B-\x7E]/;
36
47
 
37
- // From RFC2616 S2.2:
38
- var TOKEN = /[\x21\x23-\x26\x2A\x2B\x2D\x2E\x30-\x39\x41-\x5A\x5E-\x7A\x7C\x7E]/;
39
-
40
48
  // From RFC6265 S4.1.1
41
49
  // note that it excludes \x3B ";"
42
50
  var COOKIE_OCTET = /[\x21\x23-\x2B\x2D-\x3A\x3C-\x5B\x5D-\x7E]/;
43
51
  var COOKIE_OCTETS = new RegExp('^'+COOKIE_OCTET.source+'$');
44
52
 
45
- // The name/key cannot be empty but the value can (S5.2):
46
- var COOKIE_PAIR_STRICT = new RegExp('^('+TOKEN.source+'+)=("?)('+COOKIE_OCTET.source+'*)\\2$');
47
- var COOKIE_PAIR = /^([^=\s]+)\s*=\s*("?)\s*(.*)\s*\2\s*$/;
53
+ // Double quotes are part of the value (see: S4.1.1).
54
+ // '\r', '\n' and '\0' should be treated as a terminator in the "relaxed" mode
55
+ // (see: https://github.com/ChromiumWebApps/chromium/blob/b3d3b4da8bb94c1b2e061600df106d590fda3620/net/cookies/parsed_cookie.cc#L60)
56
+ // '=' and ';' are attribute/values separators
57
+ // (see: https://github.com/ChromiumWebApps/chromium/blob/b3d3b4da8bb94c1b2e061600df106d590fda3620/net/cookies/parsed_cookie.cc#L64)
58
+ var COOKIE_PAIR = /^([^=;]+)\s*=\s*(("?)[^\n\r\0]*\3)/;
48
59
 
49
- // RFC6265 S4.1.1 defines extension-av as 'any CHAR except CTLs or ";"'
60
+ // RFC6265 S4.1.1 defines path value as 'any CHAR except CTLs or ";"'
50
61
  // Note ';' is \x3B
51
- var NON_CTL_SEMICOLON = /[\x20-\x3A\x3C-\x7E]+/;
52
- var EXTENSION_AV = NON_CTL_SEMICOLON;
53
- var PATH_VALUE = NON_CTL_SEMICOLON;
62
+ var PATH_VALUE = /[\x20-\x3A\x3C-\x7E]+/;
54
63
 
55
64
  // Used for checking whether or not there is a trailing semi-colon
56
65
  var TRAILING_SEMICOLON = /;+$/;
57
66
 
58
- /* RFC6265 S5.1.1.5:
59
- * [fail if] the day-of-month-value is less than 1 or greater than 31
60
- */
61
- var DAY_OF_MONTH = /^(0?[1-9]|[12][0-9]|3[01])$/;
67
+ var DAY_OF_MONTH = /^(\d{1,2})[^\d]*$/;
68
+ var TIME = /^(\d{1,2})[^\d]*:(\d{1,2})[^\d]*:(\d{1,2})[^\d]*$/;
69
+ var MONTH = /^(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)/i;
62
70
 
63
- /* RFC6265 S5.1.1.5:
64
- * [fail if]
65
- * * the hour-value is greater than 23,
66
- * * the minute-value is greater than 59, or
67
- * * the second-value is greater than 59.
68
- */
69
- var TIME = /(0?[0-9]|1[0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9])/;
70
- var STRICT_TIME = /^(0?[0-9]|1[0-9]|2[0-3]):([0-5][0-9]):([0-5][0-9])$/;
71
-
72
- var MONTH = /^(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)$/i;
73
71
  var MONTH_TO_NUM = {
74
72
  jan:0, feb:1, mar:2, apr:3, may:4, jun:5,
75
73
  jul:6, aug:7, sep:8, oct:9, nov:10, dec:11
@@ -81,18 +79,19 @@ var NUM_TO_DAY = [
81
79
  'Sun','Mon','Tue','Wed','Thu','Fri','Sat'
82
80
  ];
83
81
 
84
- var YEAR = /^([1-9][0-9]{1,3})$/; // 2 to 4 digits
82
+ var YEAR = /^(\d{2}|\d{4})$/; // 2 to 4 digits
85
83
 
86
84
  var MAX_TIME = 2147483647000; // 31-bit max
87
85
  var MIN_TIME = 0; // 31-bit min
88
86
 
87
+ var cookiesCreated = 0; // Number of cookies created in runtime
88
+
89
89
 
90
90
  // RFC6265 S5.1.1 date parser:
91
- function parseDate(str,strict) {
91
+ function parseDate(str) {
92
92
  if (!str) {
93
93
  return;
94
94
  }
95
- var found_time, found_dom, found_month, found_year;
96
95
 
97
96
  /* RFC6265 S5.1.1:
98
97
  * 2. Process each date-token sequentially in the order the date-tokens
@@ -103,8 +102,12 @@ function parseDate(str,strict) {
103
102
  return;
104
103
  }
105
104
 
106
- var date = new Date();
107
- date.setMilliseconds(0);
105
+ var hour = null;
106
+ var minutes = null;
107
+ var seconds = null;
108
+ var day = null;
109
+ var month = null;
110
+ var year = null;
108
111
 
109
112
  for (var i=0; i<tokens.length; i++) {
110
113
  var token = tokens[i].trim();
@@ -120,13 +123,22 @@ function parseDate(str,strict) {
120
123
  * the date-token, respectively. Skip the remaining sub-steps and continue
121
124
  * to the next date-token.
122
125
  */
123
- if (!found_time) {
124
- result = (strict ? STRICT_TIME : TIME).exec(token);
126
+ if (seconds === null) {
127
+ result = TIME.exec(token);
125
128
  if (result) {
126
- found_time = true;
127
- date.setUTCHours(result[1]);
128
- date.setUTCMinutes(result[2]);
129
- date.setUTCSeconds(result[3]);
129
+ hour = parseInt(result[1], 10);
130
+ minutes = parseInt(result[2], 10);
131
+ seconds = parseInt(result[3], 10);
132
+ /* RFC6265 S5.1.1.5:
133
+ * [fail if]
134
+ * * the hour-value is greater than 23,
135
+ * * the minute-value is greater than 59, or
136
+ * * the second-value is greater than 59.
137
+ */
138
+ if(hour > 23 || minutes > 59 || seconds > 59) {
139
+ return;
140
+ }
141
+
130
142
  continue;
131
143
  }
132
144
  }
@@ -136,11 +148,16 @@ function parseDate(str,strict) {
136
148
  * the day-of-month-value to the number denoted by the date-token. Skip
137
149
  * the remaining sub-steps and continue to the next date-token.
138
150
  */
139
- if (!found_dom) {
151
+ if (day === null) {
140
152
  result = DAY_OF_MONTH.exec(token);
141
153
  if (result) {
142
- found_dom = true;
143
- date.setUTCDate(result[1]);
154
+ day = parseInt(result, 10);
155
+ /* RFC6265 S5.1.1.5:
156
+ * [fail if] the day-of-month-value is less than 1 or greater than 31
157
+ */
158
+ if(day < 1 || day > 31) {
159
+ return;
160
+ }
144
161
  continue;
145
162
  }
146
163
  }
@@ -150,11 +167,10 @@ function parseDate(str,strict) {
150
167
  * the month denoted by the date-token. Skip the remaining sub-steps and
151
168
  * continue to the next date-token.
152
169
  */
153
- if (!found_month) {
170
+ if (month === null) {
154
171
  result = MONTH.exec(token);
155
172
  if (result) {
156
- found_month = true;
157
- date.setUTCMonth(MONTH_TO_NUM[result[1].toLowerCase()]);
173
+ month = MONTH_TO_NUM[result[1].toLowerCase()];
158
174
  continue;
159
175
  }
160
176
  }
@@ -164,10 +180,10 @@ function parseDate(str,strict) {
164
180
  * denoted by the date-token. Skip the remaining sub-steps and continue to
165
181
  * the next date-token.
166
182
  */
167
- if (!found_year) {
183
+ if (year === null) {
168
184
  result = YEAR.exec(token);
169
185
  if (result) {
170
- var year = result[0];
186
+ year = parseInt(result[0], 10);
171
187
  /* From S5.1.1:
172
188
  * 3. If the year-value is greater than or equal to 70 and less
173
189
  * than or equal to 99, increment the year-value by 1900.
@@ -183,20 +199,16 @@ function parseDate(str,strict) {
183
199
  if (year < 1601) {
184
200
  return; // 5. ... the year-value is less than 1601
185
201
  }
186
-
187
- found_year = true;
188
- date.setUTCFullYear(year);
189
- continue;
190
202
  }
191
203
  }
192
204
  }
193
205
 
194
- if (!(found_time && found_dom && found_month && found_year)) {
206
+ if (seconds === null || day === null || month === null || year === null) {
195
207
  return; // 5. ... at least one of the found-day-of-month, found-month, found-
196
208
  // year, or found-time flags is not set,
197
209
  }
198
210
 
199
- return date;
211
+ return new Date(Date.UTC(year, month, day, hour, minutes, seconds));
200
212
  }
201
213
 
202
214
  function formatDate(date) {
@@ -303,52 +315,19 @@ function defaultPath(path) {
303
315
  return path.slice(0, rightSlash);
304
316
  }
305
317
 
306
- /*
307
- * "A request-path path-matches a given cookie-path if at least one of the
308
- * following conditions holds:"
309
- */
310
- function pathMatch(reqPath,cookiePath) {
311
- // "o The cookie-path and the request-path are identical."
312
- if (cookiePath === reqPath) {
313
- return true;
314
- }
315
-
316
- var idx = reqPath.indexOf(cookiePath);
317
- if (idx === 0) {
318
- // "o The cookie-path is a prefix of the request-path, and the last
319
- // character of the cookie-path is %x2F ("/")."
320
- if (cookiePath.substr(-1) === "/") {
321
- return true;
322
- }
323
-
324
- // " o The cookie-path is a prefix of the request-path, and the first
325
- // character of the request-path that is not included in the cookie- path
326
- // is a %x2F ("/") character."
327
- if (reqPath.substr(cookiePath.length,1) === "/") {
328
- return true;
329
- }
330
- }
331
318
 
332
- return false;
333
- }
334
-
335
- function parse(str, strict) {
319
+ function parse(str) {
336
320
  str = str.trim();
337
321
 
338
322
  // S4.1.1 Trailing semi-colons are not part of the specification.
339
- // If we are not in strict mode we remove the trailing semi-colons.
340
323
  var semiColonCheck = TRAILING_SEMICOLON.exec(str);
341
324
  if (semiColonCheck) {
342
- if (strict) {
343
- return;
344
- }
345
325
  str = str.slice(0, semiColonCheck.index);
346
326
  }
347
327
 
348
328
  // We use a regex to parse the "name-value-pair" part of S5.2
349
329
  var firstSemi = str.indexOf(';'); // S5.2 step 1
350
- var pairRx = strict ? COOKIE_PAIR_STRICT : COOKIE_PAIR;
351
- var result = pairRx.exec(firstSemi === -1 ? str : str.substr(0,firstSemi));
330
+ var result = COOKIE_PAIR.exec(firstSemi === -1 ? str : str.substr(0,firstSemi));
352
331
 
353
332
  // Rx satisfies the "the name string is empty" and "lacks a %x3D ("=")"
354
333
  // constraints as well as trimming any whitespace.
@@ -357,8 +336,8 @@ function parse(str, strict) {
357
336
  }
358
337
 
359
338
  var c = new Cookie();
360
- c.key = result[1]; // the regexp should trim() already
361
- c.value = result[3]; // [2] is quotes or empty-string
339
+ c.key = result[1].trim();
340
+ c.value = result[2].trim();
362
341
 
363
342
  if (firstSemi === -1) {
364
343
  return c;
@@ -386,13 +365,9 @@ function parse(str, strict) {
386
365
  var cookie_avs = unparsed.split(/\s*;\s*/);
387
366
  while (cookie_avs.length) {
388
367
  var av = cookie_avs.shift();
389
-
390
- if (strict && !EXTENSION_AV.test(av)) {
391
- return;
392
- }
393
-
394
368
  var av_sep = av.indexOf('=');
395
369
  var av_key, av_value;
370
+
396
371
  if (av_sep === -1) {
397
372
  av_key = av;
398
373
  av_value = null;
@@ -402,47 +377,51 @@ function parse(str, strict) {
402
377
  }
403
378
 
404
379
  av_key = av_key.trim().toLowerCase();
380
+
405
381
  if (av_value) {
406
382
  av_value = av_value.trim();
407
383
  }
408
384
 
409
385
  switch(av_key) {
410
386
  case 'expires': // S5.2.1
411
- if (!av_value) {if(strict){return;}else{break;} }
412
- var exp = parseDate(av_value,strict);
413
- // "If the attribute-value failed to parse as a cookie date, ignore the
414
- // cookie-av."
415
- if (exp == null) { if(strict){return;}else{break;} }
416
- c.expires = exp;
417
- // over and underflow not realistically a concern: V8's getTime() seems to
418
- // store something larger than a 32-bit time_t (even with 32-bit node)
387
+ if (av_value) {
388
+ var exp = parseDate(av_value);
389
+ // "If the attribute-value failed to parse as a cookie date, ignore the
390
+ // cookie-av."
391
+ if (exp) {
392
+ // over and underflow not realistically a concern: V8's getTime() seems to
393
+ // store something larger than a 32-bit time_t (even with 32-bit node)
394
+ c.expires = exp;
395
+ }
396
+ }
419
397
  break;
420
398
 
421
399
  case 'max-age': // S5.2.2
422
- if (!av_value) { if(strict){return;}else{break;} }
423
- // "If the first character of the attribute-value is not a DIGIT or a "-"
424
- // character ...[or]... If the remainder of attribute-value contains a
425
- // non-DIGIT character, ignore the cookie-av."
426
- if (!/^-?[0-9]+$/.test(av_value)) { if(strict){return;}else{break;} }
427
- var delta = parseInt(av_value,10);
428
- if (strict && delta <= 0) {
429
- return; // S4.1.1
400
+ if (av_value) {
401
+ // "If the first character of the attribute-value is not a DIGIT or a "-"
402
+ // character ...[or]... If the remainder of attribute-value contains a
403
+ // non-DIGIT character, ignore the cookie-av."
404
+ if (/^-?[0-9]+$/.test(av_value)) {
405
+ var delta = parseInt(av_value, 10);
406
+ // "If delta-seconds is less than or equal to zero (0), let expiry-time
407
+ // be the earliest representable date and time."
408
+ c.setMaxAge(delta);
409
+ }
430
410
  }
431
- // "If delta-seconds is less than or equal to zero (0), let expiry-time
432
- // be the earliest representable date and time."
433
- c.setMaxAge(delta);
434
411
  break;
435
412
 
436
413
  case 'domain': // S5.2.3
437
414
  // "If the attribute-value is empty, the behavior is undefined. However,
438
415
  // the user agent SHOULD ignore the cookie-av entirely."
439
- if (!av_value) { if(strict){return;}else{break;} }
440
- // S5.2.3 "Let cookie-domain be the attribute-value without the leading %x2E
441
- // (".") character."
442
- var domain = av_value.trim().replace(/^\./,'');
443
- if (!domain) { if(strict){return;}else{break;} } // see "is empty" above
444
- // "Convert the cookie-domain to lower case."
445
- c.domain = domain.toLowerCase();
416
+ if (av_value) {
417
+ // S5.2.3 "Let cookie-domain be the attribute-value without the leading %x2E
418
+ // (".") character."
419
+ var domain = av_value.trim().replace(/^\./, '');
420
+ if (domain) {
421
+ // "Convert the cookie-domain to lower case."
422
+ c.domain = domain.toLowerCase();
423
+ }
424
+ }
446
425
  break;
447
426
 
448
427
  case 'path': // S5.2.4
@@ -456,10 +435,7 @@ function parse(str, strict) {
456
435
  * We'll represent the default-path as null since it depends on the
457
436
  * context of the parsing.
458
437
  */
459
- if (!av_value || av_value.substr(0,1) != "/") {
460
- if(strict){return;}else{break;}
461
- }
462
- c.path = av_value;
438
+ c.path = av_value && av_value[0] === "/" ? av_value : null;
463
439
  break;
464
440
 
465
441
  case 'secure': // S5.2.5
@@ -468,12 +444,10 @@ function parse(str, strict) {
468
444
  * the user agent MUST append an attribute to the cookie-attribute-list
469
445
  * with an attribute-name of Secure and an empty attribute-value."
470
446
  */
471
- if (av_value != null) { if(strict){return;} }
472
447
  c.secure = true;
473
448
  break;
474
449
 
475
450
  case 'httponly': // S5.2.6 -- effectively the same as 'secure'
476
- if (av_value != null) { if(strict){return;} }
477
451
  c.httpOnly = true;
478
452
  break;
479
453
 
@@ -486,6 +460,10 @@ function parse(str, strict) {
486
460
 
487
461
  // ensure a default date for sorting:
488
462
  c.creation = new Date();
463
+ //NOTE: add runtime index for the cookieCompare() to resolve the situation when Date's precision is not enough .
464
+ //Store initial UTC time as well, so we will be able to determine if we need to fallback to the Date object.
465
+ c._creationRuntimeIdx = ++cookiesCreated;
466
+ c._initialCreationTime = c.creation.getTime();
489
467
  return c;
490
468
  }
491
469
 
@@ -539,31 +517,18 @@ function cookieCompare(a,b) {
539
517
  if (deltaLen !== 0) {
540
518
  return deltaLen;
541
519
  }
542
- // ascending for time: a CMP b
543
- return (a.creation ? a.creation.getTime() : MAX_TIME) -
544
- (b.creation ? b.creation.getTime() : MAX_TIME);
545
- }
546
520
 
547
- // Gives the permutation of all possible domainMatch()es of a given domain. The
548
- // array is in shortest-to-longest order. Handy for indexing.
549
- function permuteDomain(domain) {
550
- var pubSuf = pubsuffix.getPublicSuffix(domain);
551
- if (!pubSuf) {
552
- return null;
553
- }
554
- if (pubSuf == domain) {
555
- return [domain];
556
- }
521
+ var aTime = a.creation ? a.creation.getTime() : MAX_TIME;
522
+ var bTime = b.creation ? b.creation.getTime() : MAX_TIME;
557
523
 
558
- var prefix = domain.slice(0,-(pubSuf.length+1)); // ".example.com"
559
- var parts = prefix.split('.').reverse();
560
- var cur = pubSuf;
561
- var permutations = [cur];
562
- while (parts.length) {
563
- cur = parts.shift()+'.'+cur;
564
- permutations.push(cur);
524
+ // NOTE: if creation dates are equal and they were not modified from the outside,
525
+ // then use _creationRuntimeIdx for the comparison.
526
+ if(aTime === bTime && aTime === a._initialCreationTime && bTime === b._initialCreationTime) {
527
+ return a._creationRuntimeIdx - b._creationRuntimeIdx;
565
528
  }
566
- return permutations;
529
+
530
+ // ascending for time: a CMP b
531
+ return aTime - bTime;
567
532
  }
568
533
 
569
534
  // Gives the permutation of all possible pathMatch()es of a given path. The
@@ -588,6 +553,21 @@ function permutePath(path) {
588
553
  return permutations;
589
554
  }
590
555
 
556
+ function getCookieContext(url) {
557
+ if (url instanceof Object) {
558
+ return url;
559
+ }
560
+ // NOTE: decodeURI will throw on malformed URIs (see GH-32).
561
+ // Therefore, we will just skip decoding for such URIs.
562
+ try {
563
+ url = decodeURI(url);
564
+ }
565
+ catch(err) {
566
+ // Silently swallow error
567
+ }
568
+
569
+ return urlParse(url);
570
+ }
591
571
 
592
572
  function Cookie (opts) {
593
573
  if (typeof opts !== "object") {
@@ -619,6 +599,8 @@ Cookie.prototype.extensions = null;
619
599
  Cookie.prototype.hostOnly = null; // boolean when set
620
600
  Cookie.prototype.pathIsDefault = null; // boolean when set
621
601
  Cookie.prototype.creation = null; // Date when set; defaulted by Cookie.parse
602
+ Cookie.prototype._initialCreationTime = null; // Used to determine if cookie.creation was modified
603
+ Cookie.prototype._creationRuntimeIdx = null; // Runtime index of the created cookie, used in cookieCompare()
622
604
  Cookie.prototype.lastAccessed = null; // Date when set
623
605
 
624
606
  var cookieProperties = Object.freeze(Object.keys(Cookie.prototype).map(function(p) {
@@ -642,7 +624,7 @@ Cookie.prototype.validate = function validate() {
642
624
  if (!COOKIE_OCTETS.test(this.value)) {
643
625
  return false;
644
626
  }
645
- if (this.expires != Infinity && !(this.expires instanceof Date) && !parseDate(this.expires,true)) {
627
+ if (this.expires != Infinity && !(this.expires instanceof Date) && !parseDate(this.expires)) {
646
628
  return false;
647
629
  }
648
630
  if (this.maxAge != null && this.maxAge <= 0) {
@@ -801,15 +783,13 @@ Cookie.prototype.canonicalizedDomain = function canonicalizedDomain() {
801
783
  };
802
784
 
803
785
 
804
- var memstore;
805
786
  function CookieJar(store, rejectPublicSuffixes) {
806
787
  if (rejectPublicSuffixes != null) {
807
788
  this.rejectPublicSuffixes = rejectPublicSuffixes;
808
789
  }
809
790
 
810
791
  if (!store) {
811
- memstore = memstore || require('./memstore');
812
- store = new memstore.MemoryCookieStore();
792
+ store = new MemoryCookieStore();
813
793
  }
814
794
  this.store = store;
815
795
  }
@@ -820,7 +800,7 @@ var CAN_BE_SYNC = [];
820
800
  CAN_BE_SYNC.push('setCookie');
821
801
  CookieJar.prototype.setCookie = function(cookie, url, options, cb) {
822
802
  var err;
823
- var context = (url instanceof Object) ? url : urlParse(url);
803
+ var context = getCookieContext(url);
824
804
  if (options instanceof Function) {
825
805
  cb = options;
826
806
  options = {};
@@ -830,7 +810,7 @@ CookieJar.prototype.setCookie = function(cookie, url, options, cb) {
830
810
 
831
811
  // S5.3 step 1
832
812
  if (!(cookie instanceof Cookie)) {
833
- cookie = Cookie.parse(cookie, options.strict === true);
813
+ cookie = Cookie.parse(cookie);
834
814
  }
835
815
  if (!cookie) {
836
816
  err = new Error("Cookie failed to parse");
@@ -869,15 +849,12 @@ CookieJar.prototype.setCookie = function(cookie, url, options, cb) {
869
849
  cookie.domain = host;
870
850
  }
871
851
 
872
- // S5.3 step 7: "Otherwise, set the cookie's path to the default-path of the
873
- // request-uri"
874
- if (!cookie.path) {
852
+ //S5.2.4 If the attribute-value is empty or if the first character of the
853
+ //attribute-value is not %x2F ("/"):
854
+ //Let cookie-path be the default-path.
855
+ if (!cookie.path || cookie.path[0] !== '/') {
875
856
  cookie.path = defaultPath(context.pathname);
876
857
  cookie.pathIsDefault = true;
877
- } else {
878
- if (cookie.path.length > 1 && cookie.path.substr(-1) == '/') {
879
- cookie.path = cookie.path.slice(0,-1);
880
- }
881
858
  }
882
859
 
883
860
  // S5.3 step 8: NOOP; secure attribute
@@ -934,7 +911,7 @@ CookieJar.prototype.setCookie = function(cookie, url, options, cb) {
934
911
  // RFC6365 S5.4
935
912
  CAN_BE_SYNC.push('getCookies');
936
913
  CookieJar.prototype.getCookies = function(url, options, cb) {
937
- var context = (url instanceof Object) ? url : urlParse(url);
914
+ var context = getCookieContext(url);
938
915
  if (options instanceof Function) {
939
916
  cb = options;
940
917
  options = {};
@@ -1035,9 +1012,12 @@ CookieJar.prototype.getCookieString = function(/*..., cb*/) {
1035
1012
  if (err) {
1036
1013
  cb(err);
1037
1014
  } else {
1038
- cb(null, cookies.map(function(c){
1039
- return c.cookieString();
1040
- }).join('; '));
1015
+ cb(null, cookies
1016
+ .sort(cookieCompare)
1017
+ .map(function(c){
1018
+ return c.cookieString();
1019
+ })
1020
+ .join('; '));
1041
1021
  }
1042
1022
  };
1043
1023
  args.push(next);
@@ -1092,6 +1072,7 @@ module.exports = {
1092
1072
  CookieJar: CookieJar,
1093
1073
  Cookie: Cookie,
1094
1074
  Store: Store,
1075
+ MemoryCookieStore: MemoryCookieStore,
1095
1076
  parseDate: parseDate,
1096
1077
  formatDate: formatDate,
1097
1078
  parse: parse,
@@ -1101,7 +1082,7 @@ module.exports = {
1101
1082
  pathMatch: pathMatch,
1102
1083
  getPublicSuffix: pubsuffix.getPublicSuffix,
1103
1084
  cookieCompare: cookieCompare,
1104
- permuteDomain: permuteDomain,
1085
+ permuteDomain: require('./permuteDomain').permuteDomain,
1105
1086
  permutePath: permutePath,
1106
- canonicalDomain: canonicalDomain,
1087
+ canonicalDomain: canonicalDomain
1107
1088
  };