-
Notifications
You must be signed in to change notification settings - Fork 16
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
fix: more than one cookie header field #21
Conversation
Hmm, this should only be a problem if the user also manually passes a Headers object that already contains a |
I don't set cookie header, I just create cookieJar |
Ah yes, now I see what you mean. The issue seems to be that the previous cookie header isn't removed and passed to the next request in case of a redirect. The next request however, again adds all valid cookies from the cookie jar, thus leading to the duplicate cookie header. I thought a bit about the current situation and I think, that the current behavior isn't exactly great. Should the user (intentionally or by mistake) supply a What's your opinion on this? |
I looked at how this situation is handled in async function fetch(cookieJars, url, options) {
let cookies = "";
// If user pass cookies with options, add them to separate cookieJar.
if (options?.headers?.cookie) {
const newCookieJar = new CookieJar();
// Parse options.headers.cookie to get array of 'name=value'
let cookiesFromOptions = options.headers.cookie
.split(/;\s*/)
.map(c => c.trim())
.filter(c => c != "");
// Add each cookies to cookieJar
cookiesFromOptions.forEach(c => newCookieJar.addCookie(c, url));
// Not to change original cookieJars create shalow copy of cookieJars plus new separate cookieJar
cookieJars = Array.isArray(cookieJars)
? [...cookieJars, newCookieJar]
: [cookieJars, newCookieJar];
} |
I have some errors in above code, but i won't delete it just to keep track of our thoughts. Shortly, I create cookieJar from cookie header from options and then use this cookieJar. I fixed these code: async function fetch(cookieJars, url, options) {
let cookies = "";
let cookieJarBasedOnOptions = null;
// If user pass cookies with options, add them to separate cookieJar.
if (options?.headers?.cookie) {
cookieJarBasedOnOptions = new CookieJar();
// Parse options.headers.cookie to get array of 'name=value'
let cookiesFromOptions = options.headers.cookie
.split(/;\s*/)
.map(c => c.trim())
.filter(c => c != "");
// Add each cookies to cookieJar
cookiesFromOptions.forEach(c =>
cookieJarBasedOnOptions.addCookie(c, url)
);
}
const addValidFromJars = jars => {
// since multiple cookie jars can be passed, filter duplicates by using a set of cookie names
const set = new Set();
jars.flatMap(jar => [...jar.cookiesValidForRequest(url)]).forEach(
cookie => {
if (set.has(cookie.name)) return;
set.add(cookie.name);
cookies += cookie.serialize() + "; ";
}
);
};
const convertToArray = variable => {
if (Array.isArray(variable)) {
return variable;
} else if (variable !== null) {
return [variable];
} else {
return [];
}
};
if (cookieJars || cookieJarBasedOnOptions) {
cookieJars = convertToArray(cookieJars).concat(
convertToArray(cookieJarBasedOnOptions)
);
if (
Array.isArray(cookieJars) &&
cookieJars.every(c => c instanceof CookieJar)
)
addValidFromJars(cookieJars.filter(jar => jar.flags.includes("r")));
else if (cookieJars instanceof CookieJar)
if (cookieJars.flags.includes("r")) addValidFromJars([cookieJars]);
else
throw paramError("First", "cookieJars", "fetch", [
"CookieJar",
"[CookieJar]"
]);
} |
Ah I see, so basically you're creating a temporary cookie jar for the user-supplied cookies via the I think a patch like the following should fully suffice to resolve this issue: diff --git a/src/index.mjs b/src/index.mjs
index 098449c..10a912b 100644
--- a/src/index.mjs
+++ b/src/index.mjs
@@ -55,6 +55,8 @@ async function fetch(cookieJars, url, options) {
// or, if headers is an object, construct a Headers object from it
options.headers = new Headers(options.headers);
if (cookies) {
+ if (options.headers.has("cookie"))
+ throw new Error("options.headers already contains a cookie header!");
options.headers.append("cookie", cookies.slice(0, -2));
}
if (wantFollow) options.redirect = "manual";
@@ -93,6 +95,8 @@ async function fetch(cookieJars, url, options) {
}
const location = result.headers.get("location");
options.redirect = "follow";
+ // delete cookie header from previous request to avoid a duplicate cookie header
+ options.headers.delete("cookie");
return fetch(cookieJars, location, options);
}
return result; |
In terms of resolving initial issue, it will resolve it. But we will lose the ability to pass |
True, but I don't think there would be a substantial loss of functionality here. The only purpose of this library is to handle the Besides providing malformed cookies, I just don't see any benefit of allowing user-supplied cookie headers, since all well-formed cookies can also be provided via |
I thought about it and now I agree with you. |
Nice, thanks for thinking about it and having this discussion with me. If we both agree that it's the right decision, it's less likely to be a stupid decision :D |
You can do it. |
I will explain situation:
POST request to login. In request I have these cookies.

302 redirect with Set-Cookie header

GET request to redirected location

As you can see there are duplicates of cookies (which leads to unsuccessful authentication), becuase of 58 line in index.js
options.headers.append("cookie", cookies.slice(0, -2));
After these line of code we get this in options.headers:
5.4. The Cookie Header
In the http request I still got one cookie header but i has duplicates.
When I change
append()
toset()
I have successfull authention and don't have duplicates of cookies.