123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226 |
- "use strict";
- var defaultParseOptions = {
- decodeValues: true,
- map: false,
- silent: false,
- };
- function isNonEmptyString(str) {
- return typeof str === "string" && !!str.trim();
- }
- function parseString(setCookieValue, options) {
- var parts = setCookieValue.split(";").filter(isNonEmptyString);
- var nameValuePairStr = parts.shift();
- var parsed = parseNameValuePair(nameValuePairStr);
- var name = parsed.name;
- var value = parsed.value;
- options = options
- ? Object.assign({}, defaultParseOptions, options)
- : defaultParseOptions;
- try {
- value = options.decodeValues ? decodeURIComponent(value) : value; // decode cookie value
- } catch (e) {
- console.error(
- "set-cookie-parser encountered an error while decoding a cookie with value '" +
- value +
- "'. Set options.decodeValues to false to disable this feature.",
- e
- );
- }
- var cookie = {
- name: name,
- value: value,
- };
- parts.forEach(function (part) {
- var sides = part.split("=");
- var key = sides.shift().trimLeft().toLowerCase();
- var value = sides.join("=");
- if (key === "expires") {
- cookie.expires = new Date(value);
- } else if (key === "max-age") {
- cookie.maxAge = parseInt(value, 10);
- } else if (key === "secure") {
- cookie.secure = true;
- } else if (key === "httponly") {
- cookie.httpOnly = true;
- } else if (key === "samesite") {
- cookie.sameSite = value;
- } else {
- cookie[key] = value;
- }
- });
- return cookie;
- }
- function parseNameValuePair(nameValuePairStr) {
- // Parses name-value-pair according to rfc6265bis draft
- var name = "";
- var value = "";
- var nameValueArr = nameValuePairStr.split("=");
- if (nameValueArr.length > 1) {
- name = nameValueArr.shift();
- value = nameValueArr.join("="); // everything after the first =, joined by a "=" if there was more than one part
- } else {
- value = nameValuePairStr;
- }
- return { name: name, value: value };
- }
- function parse(input, options) {
- options = options
- ? Object.assign({}, defaultParseOptions, options)
- : defaultParseOptions;
- if (!input) {
- if (!options.map) {
- return [];
- } else {
- return {};
- }
- }
- if (input.headers) {
- if (typeof input.headers.getSetCookie === "function") {
- // for fetch responses - they combine headers of the same type in the headers array,
- // but getSetCookie returns an uncombined array
- input = input.headers.getSetCookie();
- } else if (input.headers["set-cookie"]) {
- // fast-path for node.js (which automatically normalizes header names to lower-case
- input = input.headers["set-cookie"];
- } else {
- // slow-path for other environments - see #25
- var sch =
- input.headers[
- Object.keys(input.headers).find(function (key) {
- return key.toLowerCase() === "set-cookie";
- })
- ];
- // warn if called on a request-like object with a cookie header rather than a set-cookie header - see #34, 36
- if (!sch && input.headers.cookie && !options.silent) {
- console.warn(
- "Warning: set-cookie-parser appears to have been called on a request object. It is designed to parse Set-Cookie headers from responses, not Cookie headers from requests. Set the option {silent: true} to suppress this warning."
- );
- }
- input = sch;
- }
- }
- if (!Array.isArray(input)) {
- input = [input];
- }
- options = options
- ? Object.assign({}, defaultParseOptions, options)
- : defaultParseOptions;
- if (!options.map) {
- return input.filter(isNonEmptyString).map(function (str) {
- return parseString(str, options);
- });
- } else {
- var cookies = {};
- return input.filter(isNonEmptyString).reduce(function (cookies, str) {
- var cookie = parseString(str, options);
- cookies[cookie.name] = cookie;
- return cookies;
- }, cookies);
- }
- }
- /*
- Set-Cookie header field-values are sometimes comma joined in one string. This splits them without choking on commas
- that are within a single set-cookie field-value, such as in the Expires portion.
- This is uncommon, but explicitly allowed - see https://tools.ietf.org/html/rfc2616#section-4.2
- Node.js does this for every header *except* set-cookie - see https://github.com/nodejs/node/blob/d5e363b77ebaf1caf67cd7528224b651c86815c1/lib/_http_incoming.js#L128
- React Native's fetch does this for *every* header, including set-cookie.
- Based on: https://github.com/google/j2objc/commit/16820fdbc8f76ca0c33472810ce0cb03d20efe25
- Credits to: https://github.com/tomball for original and https://github.com/chrusart for JavaScript implementation
- */
- function splitCookiesString(cookiesString) {
- if (Array.isArray(cookiesString)) {
- return cookiesString;
- }
- if (typeof cookiesString !== "string") {
- return [];
- }
- var cookiesStrings = [];
- var pos = 0;
- var start;
- var ch;
- var lastComma;
- var nextStart;
- var cookiesSeparatorFound;
- function skipWhitespace() {
- while (pos < cookiesString.length && /\s/.test(cookiesString.charAt(pos))) {
- pos += 1;
- }
- return pos < cookiesString.length;
- }
- function notSpecialChar() {
- ch = cookiesString.charAt(pos);
- return ch !== "=" && ch !== ";" && ch !== ",";
- }
- while (pos < cookiesString.length) {
- start = pos;
- cookiesSeparatorFound = false;
- while (skipWhitespace()) {
- ch = cookiesString.charAt(pos);
- if (ch === ",") {
- // ',' is a cookie separator if we have later first '=', not ';' or ','
- lastComma = pos;
- pos += 1;
- skipWhitespace();
- nextStart = pos;
- while (pos < cookiesString.length && notSpecialChar()) {
- pos += 1;
- }
- // currently special character
- if (pos < cookiesString.length && cookiesString.charAt(pos) === "=") {
- // we found cookies separator
- cookiesSeparatorFound = true;
- // pos is inside the next cookie, so back up and return it.
- pos = nextStart;
- cookiesStrings.push(cookiesString.substring(start, lastComma));
- start = pos;
- } else {
- // in param ',' or param separator ';',
- // we continue from that comma
- pos = lastComma + 1;
- }
- } else {
- pos += 1;
- }
- }
- if (!cookiesSeparatorFound || pos >= cookiesString.length) {
- cookiesStrings.push(cookiesString.substring(start, cookiesString.length));
- }
- }
- return cookiesStrings;
- }
- module.exports = parse;
- module.exports.parse = parse;
- module.exports.parseString = parseString;
- module.exports.splitCookiesString = splitCookiesString;
|