"use strict"; var __rest = (this && this.__rest) || function (s, e) { var t = {}; for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0) t[p] = s[p]; if (s != null && typeof Object.getOwnPropertySymbols === "function") for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) { if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i])) t[p[i]] = s[p[i]]; } return t; }; var __importDefault = (this && this.__importDefault) || function (mod) { return (mod && mod.__esModule) ? mod : { "default": mod }; }; Object.defineProperty(exports, "__esModule", { value: true }); const tough_cookie_1 = require("tough-cookie"); const cookie_limit_1 = __importDefault(require("./cookie-limit")); const lodash_1 = require("lodash"); const url_1 = require("../utils/url"); const json_1 = require("../utils/json"); const url_2 = require("url"); const cookie_1 = require("../utils/cookie"); const LOCALHOST_DOMAIN = 'localhost'; const LOCALHOST_IP = '127.0.0.1'; class Cookies { constructor() { this._cookieJar = new tough_cookie_1.CookieJar(); this._findCookieSync = this._syncWrap('findCookie'); this._findCookiesSync = this._syncWrap('findCookies'); this._getAllCookiesSync = this._syncWrap('getAllCookies'); this._putCookieSync = this._syncWrap('putCookie'); this._removeCookieSync = this._syncWrap('removeCookie'); this._removeAllCookiesSync = this._syncWrap('removeAllCookies'); this._pendingSyncCookies = []; } _syncWrap(method) { return (...args) => { let syncErr; let syncResult; this._cookieJar.store[method](...args, (err, result) => { syncErr = err; syncResult = result; }); if (syncErr) throw syncErr; return syncResult; }; } static _hasLocalhostDomain(cookie) { if (cookie) return this._isLocalHostDomain(cookie.domain); return false; } _set(url, cookies, isClient) { cookies = (0, lodash_1.castArray)(cookies); return cookies.reduce((resultCookies, cookieStr) => { let cookie; if (!isClient) { if (cookieStr.length > cookie_limit_1.default) return resultCookies; cookie = tough_cookie_1.Cookie.parse(cookieStr, { loose: true }); if (!cookie) return resultCookies; } else cookie = cookieStr; // NOTE: If cookie.domain and url hostname are equal to localhost/127.0.0.1, // we should remove 'Domain=...' form cookieStr (GH-1491) if (Cookies._hasLocalhostDomain(cookie) && (isClient || (0, url_1.parseUrl)(url).hostname === cookie.domain)) cookie.domain = ''; const parsedCookie = this._cookieJar.setCookieSync(cookie, url, { http: !isClient, ignoreError: true, loose: true, }); if (parsedCookie) resultCookies.push(parsedCookie); return resultCookies; }, []); } serializeJar() { return (0, json_1.stringify)(this._cookieJar.serializeSync()); } setJar(serializedJar) { this._cookieJar = serializedJar ? tough_cookie_1.CookieJar.deserializeSync((0, json_1.parse)(serializedJar)) : new tough_cookie_1.CookieJar(); } _convertToExternalCookies(internalCookies) { return internalCookies.map(cookie => { const { key, value, domain, path, expires, maxAge, secure, httpOnly, sameSite, } = cookie; return { name: key, domain: domain || void 0, path: path || void 0, expires: expires === 'Infinity' ? void 0 : expires, maxAge: maxAge || void 0, value, secure, httpOnly, sameSite, }; }); } _convertToCookieProperties(externalCookie) { return externalCookie.map(cookie => { const { name } = cookie, rest = __rest(cookie, ["name"]); return name ? Object.assign({ key: name }, rest) : rest; }); } _findCookiesByApi(urls, key) { return urls.map(({ domain, path }) => { const cookies = key ? this._findCookieSync(domain, path, key) : this._findCookiesSync(domain, path); return cookies || []; }); } _filterCookies(cookies, filters) { const filterKeys = Object.keys(filters); return cookies.filter(cookie => filterKeys.every(key => cookie[key] === filters[key])); } _getCookiesByApi(cookie, urls, strict = false) { const { key, domain, path } = cookie, filters = __rest(cookie, ["key", "domain", "path"]); const currentUrls = domain && path ? [{ domain, path }] : urls; let receivedCookies; if ((currentUrls === null || currentUrls === void 0 ? void 0 : currentUrls[0]) && (!strict || key)) receivedCookies = (0, lodash_1.flattenDeep)(this._findCookiesByApi(currentUrls, key)); else { receivedCookies = (0, lodash_1.flattenDeep)(this._getAllCookiesSync()); Object.assign(filters, cookie); } return Object.keys(filters).length ? this._filterCookies(receivedCookies, filters) : receivedCookies; } getCookies(externalCookies, urls = []) { let resultCookies = []; if (!externalCookies || !externalCookies.length) resultCookies = this._getAllCookiesSync(); else { const parsedUrls = urls.map(url => { const { hostname, pathname } = new url_2.URL(url); return { domain: hostname, path: pathname }; }); const cookies = this._convertToCookieProperties(externalCookies); for (const cookie of cookies) { const receivedCookies = this._getCookiesByApi(cookie, parsedUrls); resultCookies.push(...receivedCookies); } } return this._convertToExternalCookies(resultCookies); } setCookies(externalCookies, url) { const cookies = this._convertToCookieProperties(externalCookies); const { hostname = '', pathname = '/' } = url ? new url_2.URL(url) : {}; for (const cookie of cookies) { if (!cookie.domain && !cookie.path) Object.assign(cookie, { domain: hostname, path: pathname }); const cookieToSet = new tough_cookie_1.Cookie(cookie); const cookieStr = cookieToSet.toString(); if (cookieStr.length > cookie_limit_1.default) break; this._putCookieSync(cookieToSet); this._pendingSyncCookies.push(cookieToSet); } } deleteCookies(externalCookies, urls = []) { if (!externalCookies || !externalCookies.length) { const deletedCookies = this._getAllCookiesSync(); this._pendingSyncCookies.push(...deletedCookies); return this._removeAllCookiesSync(); } const parsedUrls = urls.map(url => { const { hostname, pathname } = new url_2.URL(url); return { domain: hostname, path: pathname }; }); const cookies = this._convertToCookieProperties(externalCookies); for (const cookie of cookies) { const deletedCookies = this._getCookiesByApi(cookie, parsedUrls, true); for (const deletedCookie of deletedCookies) { if (deletedCookie.domain && deletedCookie.path && deletedCookie.key) { this._removeCookieSync(deletedCookie.domain, deletedCookie.path, deletedCookie.key); deletedCookie.expires = new Date(0); this._pendingSyncCookies.push(deletedCookie); } } } } takePendingSyncCookies() { const cookies = this._pendingSyncCookies; this._pendingSyncCookies = []; return cookies; } setByServer(url, cookies) { return this._set(url, cookies, false); } setByClient(syncCookies) { for (const syncCookie of syncCookies) { const cookie = new tough_cookie_1.Cookie(syncCookie); const url = { hostname: syncCookie.domain, pathname: syncCookie.path }; this._set(url, cookie, true); } } copySyncCookies(syncCookie, toUrl) { let hostname; let pathname; try { ({ hostname, pathname } = new url_2.URL(toUrl)); } catch (e) { return; } const { actual: cookies } = (0, cookie_1.parseClientSyncCookieStr)(syncCookie); for (const cookie of cookies) { const { domain, path, key } = cookie; const originCookie = this._findCookieSync(domain, path, key); const newCookie = new tough_cookie_1.Cookie(Object.assign({}, originCookie, { domain: hostname, path: pathname })); this._putCookieSync(newCookie); this._pendingSyncCookies.push(newCookie); } } getClientString(url) { return this._cookieJar.getCookieStringSync(url, { http: false }); } getHeader({ url, hostname }) { // NOTE: https://github.com/DevExpress/testcafe-hammerhead/issues/2715 // Cookies with the secure attribute should be passed to the localhost server(without ssl) or any other server(with ssl). // CookieJar only checks the protocol, but not a hostname. That is why we should to add secure attribute in case of localhost. const cookieJarOpts = Cookies._isLocalHostDomain(hostname) ? { http: true, secure: true } : { http: true }; return this._cookieJar.getCookieStringSync(url, cookieJarOpts) || null; } } exports.default = Cookies; Cookies._isLocalHostDomain = (domain) => domain === LOCALHOST_DOMAIN || domain === LOCALHOST_IP;module.exports = exports.default;