feat: implement CookieManager for enhanced cookie handling
This commit is contained in:
74
dist/cookieManager.js
vendored
Normal file
74
dist/cookieManager.js
vendored
Normal file
@ -0,0 +1,74 @@
|
||||
import fs from "fs";
|
||||
export default class CookieManager {
|
||||
cookies = {};
|
||||
parse(headers, domain) {
|
||||
if (!this.cookies[domain])
|
||||
this.cookies[domain] = {};
|
||||
headers?.forEach(header => {
|
||||
const [cookiePart, ...attributes] = header.split(';').map(part => part.trim());
|
||||
const [name, value] = cookiePart.split('=');
|
||||
const cookie = { value: value || "", domain };
|
||||
attributes.forEach(attr => {
|
||||
const [key, val] = attr.split('=');
|
||||
const lowerKey = key.toLowerCase();
|
||||
switch (lowerKey) {
|
||||
case 'path':
|
||||
cookie.path = val;
|
||||
break;
|
||||
case 'expires':
|
||||
cookie.expires = val ? new Date(val) : undefined;
|
||||
break;
|
||||
case 'max-age':
|
||||
cookie.maxAge = parseInt(val, 10);
|
||||
break;
|
||||
case 'httponly':
|
||||
cookie.httpOnly = true;
|
||||
break;
|
||||
case 'secure':
|
||||
cookie.secure = true;
|
||||
break;
|
||||
case 'samesite':
|
||||
cookie.sameSite = val;
|
||||
break;
|
||||
}
|
||||
});
|
||||
Object.assign(this.cookies[domain], { [name]: cookie });
|
||||
});
|
||||
}
|
||||
format(domain) {
|
||||
this.cleanupExpiredCookies();
|
||||
return Object.entries(this.cookies[domain] || {})
|
||||
.map(([key, value]) => `${key}=${value.value}`)
|
||||
.join('; ');
|
||||
}
|
||||
getCookies(domain) {
|
||||
this.cleanupExpiredCookies();
|
||||
return this.cookies[domain] || {};
|
||||
}
|
||||
cleanupExpiredCookies() {
|
||||
const now = new Date();
|
||||
Object.keys(this.cookies).forEach(domain => {
|
||||
Object.keys(this.cookies[domain]).forEach(key => {
|
||||
if (this.cookies[domain][key].expires && this.cookies[domain][key].expires < now)
|
||||
delete this.cookies[domain][key];
|
||||
});
|
||||
});
|
||||
}
|
||||
saveToFile(filePath) {
|
||||
this.cleanupExpiredCookies();
|
||||
fs.writeFileSync(filePath, JSON.stringify(this.cookies, null, 2), "utf8");
|
||||
}
|
||||
loadFromFile(filePath) {
|
||||
if (!fs.existsSync(filePath))
|
||||
return console.warn(`The file ${filePath} does not exist.`);
|
||||
const fileContent = fs.readFileSync(filePath, "utf8");
|
||||
const loadedCookies = JSON.parse(fileContent);
|
||||
Object.keys(loadedCookies).forEach(domain => {
|
||||
Object.keys(loadedCookies[domain]).forEach(cookieName => {
|
||||
const cookie = loadedCookies[domain][cookieName];
|
||||
cookie.expires = cookie.expires ? new Date(cookie.expires) : undefined;
|
||||
});
|
||||
});
|
||||
this.cookies = loadedCookies;
|
||||
}
|
||||
}
|
23
dist/index.js
vendored
23
dist/index.js
vendored
@ -3,23 +3,10 @@ import https from "https";
|
||||
import { URL } from "url";
|
||||
import querystring from "querystring";
|
||||
import zlib from "zlib";
|
||||
import CookieManager from './cookieManager.js';
|
||||
export const cookieManager = new CookieManager();
|
||||
class fetch {
|
||||
cookies = {};
|
||||
constructor() { }
|
||||
parseCookies(cookieHeader) {
|
||||
if (!cookieHeader)
|
||||
return;
|
||||
cookieHeader.split(";").forEach(cookie => {
|
||||
const [key, value] = cookie.split("=");
|
||||
if (key && value)
|
||||
this.cookies[key.trim()] = value.trim();
|
||||
});
|
||||
}
|
||||
formatCookies() {
|
||||
return Object.entries(this.cookies)
|
||||
.map(([key, value]) => `${key}=${value}`)
|
||||
.join("; ");
|
||||
}
|
||||
decompress(data, encoding) {
|
||||
return encoding === "br" ? zlib.brotliDecompressSync(data) :
|
||||
encoding === "gzip" ? zlib.gunzipSync(data) :
|
||||
@ -62,14 +49,14 @@ class fetch {
|
||||
headers: {
|
||||
...options.headers,
|
||||
...(body ? { "Content-Length": Buffer.byteLength(body) } : {}),
|
||||
"Cookie": this.formatCookies(),
|
||||
"Cookie": cookieManager.format(hostname),
|
||||
"Accept-Encoding": "br, gzip, deflate",
|
||||
}
|
||||
};
|
||||
return new Promise((resolve, reject) => {
|
||||
const requester = protocol === "https:" ? https : http;
|
||||
const req = requester.request(requestOptions, res => {
|
||||
this.parseCookies(res.headers["set-cookie"]?.join("; "));
|
||||
cookieManager.parse(res.headers["set-cookie"], hostname);
|
||||
if (options.followRedirects && res.statusCode && res.statusCode >= 300 && res.statusCode < 400 && res.headers.location) {
|
||||
req.destroy();
|
||||
if (redirectCount >= (options.maxRedirects || 5))
|
||||
@ -82,7 +69,7 @@ class fetch {
|
||||
return resolve({
|
||||
body: res,
|
||||
headers: res.headers,
|
||||
cookies: this.cookies,
|
||||
cookies: cookieManager.getCookies(hostname),
|
||||
text: () => this.readData(res, "text"),
|
||||
json: () => this.readData(res, "json"),
|
||||
buffer: () => this.readData(res, "buffer"),
|
||||
|
Reference in New Issue
Block a user