Skip to content

Commit dee3a8c

Browse files
committed
More tests and a cookie refactor
1 parent 664170e commit dee3a8c

File tree

12 files changed

+202
-132
lines changed

12 files changed

+202
-132
lines changed

CHANGELOG.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,15 @@
1+
### 6.0.0 (April 17, 2020)
2+
* Use a more compact cookie format
3+
* Fix issue where a cookie key could have a space appear inside of it
4+
* Allow for localStorage fallback with the `disableCookie` option.
5+
* Only new and unseen devices will get the compact cookie. `cookieForceUpgrade` will force all browsers to upgrade and delete the old cookie.
6+
7+
### 5.12.0 (April 17, 2020)
8+
* Use an IIFE build for the snippet. This will issues where the snippet wouldn't load for require js users.
9+
10+
### 5.11.0 (April 6, 2020)
11+
* Add a `sameSiteCookie` option to set the SameSite cookie. It is set to `None` by default
12+
113
### 5.11.0 (April 6, 2020)
214
* Add a `sameSiteCookie` option to set the SameSite cookie. It is set to `None` by default
315

src/amplitude-client.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import Constants from './constants';
22
import cookieStorage from './cookiestorage';
3-
import MetadataStorage from './metaDataStorage';
3+
import MetadataStorage from '../src/metadata-storage';
44
import getUtmData from './utm';
55
import Identify from './identify';
66
import localStorage from './localstorage'; // jshint ignore:line

src/base-cookie.js

Lines changed: 23 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
1+
import Constants from './constants';
2+
13
const get = (name) => {
24
try {
3-
var ca = document.cookie.split(';');
4-
var value = null;
5-
for (var i = 0; i < ca.length; i++) {
6-
var c = ca[i];
5+
const ca = document.cookie.split(';');
6+
let value = null;
7+
for (let i = 0; i < ca.length; i++) {
8+
let c = ca[i];
79
while (c.charAt(0) === ' ') {
810
c = c.substring(1, c.length);
911
}
@@ -14,21 +16,19 @@ const get = (name) => {
1416
}
1517

1618
return value;
17-
1819
} catch (e) {
1920
return null;
2021
}
2122
};
2223

23-
2424
const set = (name, value, opts) => {
25-
var expires = value !== null ? opts.expirationDays : -1 ;
25+
let expires = value !== null ? opts.expirationDays : -1 ;
2626
if (expires) {
27-
var date = new Date();
27+
const date = new Date();
2828
date.setTime(date.getTime() + (expires * 24 * 60 * 60 * 1000));
2929
expires = date;
3030
}
31-
var str = name + '=' + value;
31+
let str = name + '=' + value;
3232
if (expires) {
3333
str += '; expires=' + expires.toUTCString();
3434
}
@@ -45,7 +45,21 @@ const set = (name, value, opts) => {
4545
document.cookie = str;
4646
};
4747

48+
49+
// test that cookies are enabled - navigator.cookiesEnabled yields false positives in IE, need to test directly
50+
const areCookiesEnabled = () => {
51+
const uid = String(new Date());
52+
try {
53+
set(Constants.COOKIE_TEST, uid, {});
54+
const _areCookiesEnabled = get(Constants.COOKIE_TEST + '=') === uid;
55+
set(Constants.COOKIE_TEST, null, {});
56+
return _areCookiesEnabled;
57+
} catch (e) {}
58+
return false;
59+
};
60+
4861
export default {
4962
set,
5063
get,
64+
areCookiesEnabled
5165
};

src/cookie.js

Lines changed: 1 addition & 68 deletions
Original file line numberDiff line numberDiff line change
@@ -3,10 +3,10 @@
33
*/
44

55
import Base64 from './base64';
6-
import Constants from './constants';
76
import utils from './utils';
87
import getLocation from './get-location';
98
import baseCookie from './base-cookie';
9+
import topDomain from './top-domain';
1010

1111

1212
var _options = {
@@ -22,51 +22,6 @@ var reset = function() {
2222
};
2323
};
2424

25-
const getHost = (url) => {
26-
const a = document.createElement('a');
27-
a.href = url;
28-
return a.hostname || location.hostname;
29-
};
30-
31-
let _topDomain = '';
32-
33-
const topDomain = (url) => {
34-
if (_topDomain) {
35-
return topDomain;
36-
}
37-
const host = getHost(url);
38-
const parts = host.split('.');
39-
const last = parts[parts.length - 1];
40-
const levels = [];
41-
42-
if (parts.length === 4 && last === parseInt(last, 10)) {
43-
return levels;
44-
}
45-
46-
if (parts.length <= 1) {
47-
return levels;
48-
}
49-
50-
for (let i = parts.length - 2; i >= 0; --i) {
51-
levels.push(parts.slice(i).join('.'));
52-
}
53-
54-
for (let i = 0; i < levels.length; ++i) {
55-
const cname = '__tld_test__';
56-
const domain = levels[i];
57-
const opts = { domain: '.' + domain };
58-
59-
baseCookie.set(cname, 1, opts);
60-
if (baseCookie.get(cname)) {
61-
baseCookie.set(cname, null, opts);
62-
return domain;
63-
}
64-
}
65-
66-
return '';
67-
};
68-
69-
7025
var options = function(opts) {
7126
if (arguments.length === 0) {
7227
return _options;
@@ -151,34 +106,12 @@ var remove = function(name) {
151106
}
152107
};
153108

154-
let _areCookiesEnabled = null;
155-
156-
// test that cookies are enabled - navigator.cookiesEnabled yields false positives in IE, need to test directly
157-
const areCookiesEnabled = () => {
158-
if (_areCookiesEnabled !== null) {
159-
return _areCookiesEnabled;
160-
}
161-
var uid = String(new Date());
162-
var result;
163-
try {
164-
set(Constants.COOKIE_TEST, uid);
165-
_areCookiesEnabled = get(Constants.COOKIE_TEST) === uid;
166-
remove(Constants.COOKIE_TEST);
167-
return result;
168-
} catch (e) {
169-
// cookies are not enabled
170-
}
171-
return false;
172-
};
173-
174109
export default {
175110
reset,
176111
options,
177-
topDomain,
178112
get,
179113
set,
180114
remove,
181-
areCookiesEnabled,
182115
setRaw,
183116
getRaw
184117
};
Lines changed: 33 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -4,36 +4,58 @@
44
*/
55

66
import Base64 from './base64';
7-
import Cookie from './cookie';
87
import baseCookie from './base-cookie';
8+
import getLocation from './get-location';
99
import localStorage from './localstorage'; // jshint ignore:line
10+
import topDomain from './top-domain';
1011

1112
class MetadataStorage {
1213
constructor({storageKey, disableCookies, domain, secure, sameSite, expirationDays}) {
1314
this.storageKey = storageKey;
14-
this.disableCookieStorage = !Cookie.areCookiesEnabled() || disableCookies;
15+
this.disableCookieStorage = !baseCookie.areCookiesEnabled() || disableCookies;
1516
this.domain = domain;
1617
this.secure = secure;
1718
this.sameSite = sameSite;
1819
this.expirationDays = expirationDays;
19-
this.topDomain = domain || Cookie.topDomain();
20+
const writableTopDomain = topDomain(getLocation().href);
21+
this.cookieDomain = domain || (writableTopDomain ? '.' + writableTopDomain : null);
2022
}
2123

2224
getCookieStorageKey() {
23-
return `${this.storageKey}${this.domain ? `_${this.domain}` : ''}`;
25+
if (!this.domain) {
26+
return this.storageKey;
27+
}
28+
29+
const suffix = this.domain.charAt(0) === '.' ? this.domain.substring(1) : this.domain;
30+
31+
return `${this.storageKey}${suffix ? `_${suffix}` : ''}`;
2432
}
2533

2634
save({ deviceId, userId, optOut, sessionId, lastEventTime, eventId, identifyId, sequenceNumber }) {
2735
// do not change the order of these items
28-
const value = `${deviceId}.${Base64.encode(userId || '')}.${optOut ? '1' : ''}.${sessionId}.${lastEventTime}.${eventId}.${identifyId}.${sequenceNumber}`;
36+
const value = [
37+
deviceId,
38+
Base64.encode(userId || ''),
39+
optOut ? '1' : '',
40+
sessionId ? sessionId.toString(32) : '0',
41+
lastEventTime ? lastEventTime.toString(32) : '0',
42+
eventId ? eventId.toString(32) : '0',
43+
identifyId ? identifyId.toString(32) : '0',
44+
sequenceNumber ? sequenceNumber.toString(32) : '0'
45+
].join('.');
2946

3047
if (this.disableCookieStorage) {
3148
localStorage.setItem(this.storageKey, value);
3249
} else {
3350
baseCookie.set(
3451
this.getCookieStorageKey(),
3552
value,
36-
{ domain: this.topDomain, secure: this.secure, sameSite: this.sameSite, expirationDays: this.expirationDays }
53+
{
54+
domain: this.cookieDomain,
55+
secure: this.secure,
56+
sameSite: this.sameSite,
57+
expirationDays: this.expirationDays
58+
}
3759
);
3860
}
3961
}
@@ -66,11 +88,11 @@ class MetadataStorage {
6688
deviceId: values[0],
6789
userId,
6890
optOut: values[2] === '1',
69-
sessionId: parseInt(values[3], 10),
70-
lastEventTime: parseInt(values[4], 10),
71-
eventId: parseInt(values[5], 10),
72-
identifyId: parseInt(values[6], 10),
73-
sequenceNumber: parseInt(values[7], 10)
91+
sessionId: parseInt(values[3], 32),
92+
lastEventTime: parseInt(values[4], 32),
93+
eventId: parseInt(values[5], 32),
94+
identifyId: parseInt(values[6], 32),
95+
sequenceNumber: parseInt(values[7], 32)
7496
};
7597
}
7698
}

src/top-domain.js

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
import baseCookie from './base-cookie';
2+
3+
const getHost = (url) => {
4+
const a = document.createElement('a');
5+
a.href = url;
6+
return a.hostname || location.hostname;
7+
};
8+
9+
const topDomain = (url) => {
10+
const host = getHost(url);
11+
const parts = host.split('.');
12+
const levels = [];
13+
14+
for (let i = parts.length - 2; i >= 0; --i) {
15+
levels.push(parts.slice(i).join('.'));
16+
}
17+
18+
for (let i = 0; i < levels.length; ++i) {
19+
const cname = '__tld_test__';
20+
const domain = levels[i];
21+
const opts = { domain: '.' + domain };
22+
23+
baseCookie.set(cname, 1, opts);
24+
if (baseCookie.get(cname)) {
25+
baseCookie.set(cname, null, opts);
26+
return domain;
27+
}
28+
}
29+
30+
return '';
31+
};
32+
33+
export default topDomain;

0 commit comments

Comments
 (0)