generators are awesome
rewrite internal cookie storage fix incorrect splitting of key value pairs when parsing serialized cookies minor version bump
This commit is contained in:
parent
4a84e2ad7c
commit
14a874f208
24
README.md
24
README.md
|
@ -43,11 +43,29 @@ A class that stores cookies.
|
||||||
|
|
||||||
#### addCookie(cookie[, url])
|
#### addCookie(cookie[, url])
|
||||||
Adds a cookie to the jar.
|
Adds a cookie to the jar.
|
||||||
- `cookie` A [Cookie](#class-cookie) instance to add to the cookie jar. Alternatively this can also be a string, for example the string received from a website. In this case `url` should be specified.
|
- `cookie` A [Cookie](#class-cookie) instance to add to the cookie jar. Alternatively this can also be a string, for example a serialized cookie received from a website. In this case `url` should be specified.
|
||||||
- `url` The url a cookie has been received from.
|
- `url` The url a cookie has been received from.
|
||||||
|
|
||||||
#### forEach(callback)
|
#### addFromFile(file)
|
||||||
Just a wrapper for `CookieJar.cookies.forEach(callback)`.
|
Reads a cookie jar from the disk and adds the contained cookies.
|
||||||
|
|
||||||
|
#### domains()
|
||||||
|
Returns an array of the domains currently stored cookies for.
|
||||||
|
|
||||||
|
#### *iterValidForRequest(domain, url)
|
||||||
|
Returns an iterator over all cookies valid for a request to `domain` and `url`.
|
||||||
|
|
||||||
|
#### *iterValid()
|
||||||
|
Returns an iterator over all valid (non-expired) cookies.
|
||||||
|
|
||||||
|
#### *iterAll()
|
||||||
|
Returns an iterator over all cookies currently stored.
|
||||||
|
|
||||||
|
#### *iter(domain)
|
||||||
|
Returns an iterator over all cookies for a specific domain.
|
||||||
|
|
||||||
|
#### deleteExpired()
|
||||||
|
Removes all expired cookies from the jar.
|
||||||
|
|
||||||
#### save()
|
#### save()
|
||||||
Saves the cookie jar to disk. Only non-expired cookies are saved.
|
Saves the cookie jar to disk. Only non-expired cookies are saved.
|
||||||
|
|
2
package-lock.json
generated
2
package-lock.json
generated
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "node-fetch-cookies",
|
"name": "node-fetch-cookies",
|
||||||
"version": "1.0.6",
|
"version": "1.1.0",
|
||||||
"lockfileVersion": 1,
|
"lockfileVersion": 1,
|
||||||
"requires": true,
|
"requires": true,
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "node-fetch-cookies",
|
"name": "node-fetch-cookies",
|
||||||
"version": "1.0.6",
|
"version": "1.1.0",
|
||||||
"description": "node-fetch wrapper that adds support for cookie-jars",
|
"description": "node-fetch wrapper that adds support for cookie-jars",
|
||||||
"main": "src/index.mjs",
|
"main": "src/index.mjs",
|
||||||
"engines": {
|
"engines": {
|
||||||
|
|
|
@ -10,36 +10,63 @@ export default class CookieJar {
|
||||||
throw new TypeError("First parameter is not a string!");
|
throw new TypeError("First parameter is not a string!");
|
||||||
if(this.file && typeof this.file !== "string")
|
if(this.file && typeof this.file !== "string")
|
||||||
throw new TypeError("Second parameter is not a string!");
|
throw new TypeError("Second parameter is not a string!");
|
||||||
|
if(this.file && fs.existsSync(this.file))
|
||||||
|
this.addFromFile(this.file);
|
||||||
|
else
|
||||||
|
this.cookies = new Map();
|
||||||
if(Array.isArray(cookies)) {
|
if(Array.isArray(cookies)) {
|
||||||
if(!cookies.every(c => c instanceof Cookie))
|
if(!cookies.every(c => c instanceof Cookie))
|
||||||
throw new TypeError("Third parameter is not an array of cookies!");
|
throw new TypeError("Third parameter is not an array of cookies!");
|
||||||
else
|
else
|
||||||
cookies.forEach(cookie => this.cookies.set(cookie.name, cookie));
|
cookies.forEach(cookie => this.addCookie(cookie));
|
||||||
}
|
}
|
||||||
else if(cookies instanceof Cookie)
|
else if(cookies instanceof Cookie)
|
||||||
this.cookies.set(cookies.name, cookies);
|
this.addCookie(cookies);
|
||||||
else if(cookies)
|
else if(cookies)
|
||||||
throw new TypeError("Third parameter is neither an array nor a cookie!");
|
throw new TypeError("Third parameter is neither an array nor a cookie!");
|
||||||
if(this.file && this.cookies.size === 0 && this.file.length !== 0 && fs.existsSync(this.file))
|
|
||||||
this.cookies = new Map(JSON.parse(fs.readFileSync(this.file)).map(([k, v]) => [k, Cookie.fromObject(v)]));
|
|
||||||
}
|
}
|
||||||
addCookie(c, fromURL) {
|
addCookie(c, fromURL) {
|
||||||
if(typeof c === "string")
|
if(typeof c === "string")
|
||||||
c = new Cookie(c, fromURL);
|
c = new Cookie(c, fromURL);
|
||||||
this.cookies.set(c.name, c);
|
if(!(c instanceof Cookie))
|
||||||
|
throw new TypeError("First parameter is neither a string nor a cookie!");
|
||||||
|
if(!this.cookies.get(c.domain))
|
||||||
|
this.cookies.set(c.domain, new Map());
|
||||||
|
this.cookies.get(c.domain).set(c.name, c);
|
||||||
}
|
}
|
||||||
forEach(callback) {
|
addFromFile(file) {
|
||||||
this.cookies.forEach(callback);
|
JSON.parse(fs.readFileSync(this.file)).forEach(c => this.addCookie(Cookie.fromObject(c)));
|
||||||
|
}
|
||||||
|
domains() {
|
||||||
|
return [...this.cookies.keys()];
|
||||||
|
}
|
||||||
|
*iterValidForRequest(domain, url) {
|
||||||
|
for(const cookie of this.iter(domain))
|
||||||
|
if(cookie.isValidForRequest(url))
|
||||||
|
yield cookie;
|
||||||
|
}
|
||||||
|
*iterValid() {
|
||||||
|
for(const cookie of this.iterAll())
|
||||||
|
if(!cookie.hasExpired())
|
||||||
|
yield cookie;
|
||||||
|
}
|
||||||
|
*iterAll() {
|
||||||
|
for(const domain of this.domains())
|
||||||
|
yield* this.iter(domain);
|
||||||
|
}
|
||||||
|
*iter(domain) {
|
||||||
|
for(const cookie of (this.cookies.get(domain) || []).values())
|
||||||
|
yield cookie;
|
||||||
|
}
|
||||||
|
deleteExpired() {
|
||||||
|
const filteredCookies = [...this.iterValid()];
|
||||||
|
this.cookies = new Map();
|
||||||
|
filteredCookies.forEach(c => this.addCookie(c));
|
||||||
}
|
}
|
||||||
save() {
|
save() {
|
||||||
if(typeof this.file !== "string")
|
if(typeof this.file !== "string")
|
||||||
throw new Error("No file has been specified for this cookie jar!");
|
throw new Error("No file has been specified for this cookie jar!");
|
||||||
// only save cookies that haven't expired
|
// only save cookies that haven't expired
|
||||||
let cookiesToSave = new Map();
|
fs.writeFileSync(this.file, JSON.stringify([...this.iterValid()]));
|
||||||
this.forEach(cookie => {
|
|
||||||
if(!cookie.hasExpired())
|
|
||||||
cookiesToSave.set(cookie.name, cookie);
|
|
||||||
});
|
|
||||||
fs.writeFileSync(this.file, JSON.stringify([...cookiesToSave]));
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
|
@ -18,18 +18,27 @@ const validatePath = (cookiePath, requestPath) => {
|
||||||
return (requestPath + "/").startsWith(cookiePath + "/");
|
return (requestPath + "/").startsWith(cookiePath + "/");
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const splitN = (str, sep, n) => {
|
||||||
|
const splitted = str.split(sep);
|
||||||
|
if(n < splitted.length - 1) {
|
||||||
|
splitted[n] = splitted.slice(n).join(sep);
|
||||||
|
splitted.splice(n + 1);
|
||||||
|
}
|
||||||
|
return splitted;
|
||||||
|
};
|
||||||
|
|
||||||
export default class Cookie {
|
export default class Cookie {
|
||||||
constructor(str, url) {
|
constructor(str, url) {
|
||||||
if(typeof str !== "string")
|
if(typeof str !== "string")
|
||||||
throw new TypeError("Input not a string");
|
throw new TypeError("Input not a string");
|
||||||
|
|
||||||
const splitted = str.split("; ");
|
const splitted = str.split("; ");
|
||||||
[this.name, this.value] = splitted[0].split("=");
|
[this.name, this.value] = splitN(splitted[0], "=", 1);
|
||||||
if(this.value.startsWith("\"") && this.value.endsWith("\""))
|
if(this.value.startsWith("\"") && this.value.endsWith("\""))
|
||||||
this.value = this.value.slice(1, -1);
|
this.value = this.value.slice(1, -1);
|
||||||
|
|
||||||
for(let i = 1; i < splitted.length; i++) {
|
for(let i = 1; i < splitted.length; i++) {
|
||||||
let [k, v] = splitted[i].split("=");
|
let [k, v] = splitN(splitted[i], "=", 1);
|
||||||
k = k.toLowerCase();
|
k = k.toLowerCase();
|
||||||
if(v) {
|
if(v) {
|
||||||
if(k === "expires") {
|
if(k === "expires") {
|
||||||
|
@ -40,7 +49,7 @@ export default class Cookie {
|
||||||
throw new TypeError("Invalid value for Expires \"" + v + "\"!");
|
throw new TypeError("Invalid value for Expires \"" + v + "\"!");
|
||||||
}
|
}
|
||||||
else if(k === "max-age") {
|
else if(k === "max-age") {
|
||||||
const seconds = parseInt(v);
|
const seconds = ~~+v;
|
||||||
if(seconds.toString() !== v)
|
if(seconds.toString() !== v)
|
||||||
throw new TypeError("Invalid value for Max-Age \"" + v + "\"!");
|
throw new TypeError("Invalid value for Max-Age \"" + v + "\"!");
|
||||||
this.expiry = new Date();
|
this.expiry = new Date();
|
||||||
|
|
|
@ -1,26 +1,32 @@
|
||||||
import fetch from "node-fetch";
|
import fetch from "node-fetch";
|
||||||
import CookieJar from "./cookie-jar";
|
import CookieJar from "./cookie-jar";
|
||||||
import Cookie from "./cookie";
|
import Cookie from "./cookie";
|
||||||
|
import urlParser from "url";
|
||||||
|
|
||||||
async function cookieFetch(cookieJars, url, options) {
|
async function cookieFetch(cookieJars, url, options) {
|
||||||
let cookies = "";
|
let cookies = "";
|
||||||
|
const domains =
|
||||||
|
urlParser
|
||||||
|
.parse(url)
|
||||||
|
.hostname
|
||||||
|
.split(".")
|
||||||
|
.map((_, i, a) => a.slice(i).join("."))
|
||||||
|
.slice(0, -1);
|
||||||
|
const addValidFromJar = jar =>
|
||||||
|
domains
|
||||||
|
.map(d => [...jar.iterValidForRequest(d, url)])
|
||||||
|
.reduce((a, b) => [...a, ...b])
|
||||||
|
.forEach(c => cookies += c.serialize() + "; ");
|
||||||
if(cookieJars) {
|
if(cookieJars) {
|
||||||
if(Array.isArray(cookieJars) && cookieJars.every(c => c instanceof CookieJar)) {
|
if(Array.isArray(cookieJars) && cookieJars.every(c => c instanceof CookieJar)) {
|
||||||
cookieJars.forEach(jar => {
|
cookieJars.forEach(jar => {
|
||||||
if(!jar.flags.includes("r"))
|
if(!jar.flags.includes("r"))
|
||||||
return;
|
return;
|
||||||
jar.forEach(c => {
|
addValidFromJar(jar);
|
||||||
if(c.isValidForRequest(url))
|
|
||||||
cookies += c.serialize() + "; ";
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
else if(cookieJars instanceof CookieJar && cookieJars.flags.includes("r")) {
|
|
||||||
cookieJars.forEach(c => {
|
|
||||||
if(c.isValidForRequest(url))
|
|
||||||
cookies += c.serialize() + "; ";
|
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
else if(cookieJars instanceof CookieJar && cookieJars.flags.includes("r"))
|
||||||
|
addValidFromJar(cookieJars);
|
||||||
else
|
else
|
||||||
throw new TypeError("First paramter is neither a cookie jar nor an array of cookie jars!");
|
throw new TypeError("First paramter is neither a cookie jar nor an array of cookie jars!");
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user