Skip to content

Commit

Permalink
add support for getSetCookie() method on fetch response headers objects.
Browse files Browse the repository at this point in the history
Fixes #56
Relates to whatwg/fetch#1346
  • Loading branch information
nfriedly committed Mar 18, 2023
1 parent 4fcef0b commit bbfdc6e
Show file tree
Hide file tree
Showing 3 changed files with 59 additions and 23 deletions.
14 changes: 8 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,15 +6,15 @@

Parses set-cookie headers into objects

Accepts a single `set-cookie` header value, an array of `set-cookie` header values, or a Node.js response object that may have 0 or more `set-cookie` headers.
Accepts a single `set-cookie` header value, an array of `set-cookie` header values, a Node.js response object, or a `fetch()` `Response` object that may have 0 or more `set-cookie` headers.

Also accepts an optional options object. Defaults:

```js
{
decodeValues: true, // Calls dcodeURIComponent on each value - default: true
decodeValues: true, // Calls decodeURIComponent on each value - default: true
map: false, // Return an object instead of an array - default: false
silent: false, // Suppress the warning that is loged when called on a request instead of a response - default: false
silent: false, // Suppress the warning that is logged when called on a request instead of a response - default: false
}
```

Expand All @@ -26,7 +26,7 @@ Returns either an array of cookie objects or a map of name => cookie object with
* `domain` - domain for the cookie (string or undefined, may begin with "." to indicate the named domain or any subdomain of it)
* `expires` - absolute expiration date for the cookie (Date object or undefined)
* `maxAge` - relative max age of the cookie in seconds from when the client receives it (integer or undefined)
* Note: when using with [express's res.cookie() method](http://expressjs.com/en/4x/api.html#res.cookie), multiply `maxAge` by 1000 to convert to miliseconds.
* Note: when using with [express's res.cookie() method](http://expressjs.com/en/4x/api.html#res.cookie), multiply `maxAge` by 1000 to convert to milliseconds.
* `secure` - indicates that this cookie should only be sent over HTTPs (true or undefined)
* `httpOnly` - indicates that this cookie should *not* be accessible to client-side JavaScript (true or undefined)
* `sameSite` - indicates a cookie ought not to be sent along with cross-site requests (string or undefined)
Expand Down Expand Up @@ -140,9 +140,9 @@ function modifySetCookie(res){
See a real-world example of this in [unblocker](https://github.com/nfriedly/node-unblocker/blob/08a89ec27274b46dcd80d0a324a59406f2bdad3d/lib/cookies.js#L67-L85)
## Usage in React Native
## Usage in React Native (and with some other fetch implementations)
React Native follows the Fetch spec more closely and combines all of the Set-Cookie header values into a single string.
React Native follows the Fetch spec more closely and combines all of the Set-Cookie header values into a single string.
The `splitCookiesString` method reverses this.
```js
Expand All @@ -160,6 +160,8 @@ console.log(cookies); // should be an array of cookies
This behavior may become a default part of parse in the next major release, but requires the extra step for now.
Note that the `fetch()` spec now includes a `getSetCookie()` method that provides un-combined `Set-Cookie` headers. This library will automatically use that method if it is present.
## API
### parse(input, [options])
Expand Down
40 changes: 23 additions & 17 deletions lib/set-cookie.js
Original file line number Diff line number Diff line change
Expand Up @@ -89,24 +89,30 @@ function parse(input, options) {
}
}

if (input.headers && input.headers["set-cookie"]) {
// fast-path for node.js (which automatically normalizes header names to lower-case
input = input.headers["set-cookie"];
} else if (input.headers) {
// 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."
);
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;
}
input = sch;
}
if (!Array.isArray(input)) {
input = [input];
Expand Down
28 changes: 28 additions & 0 deletions test/fetch.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
"use strict";
var assert = require("assert");
var setCookie = require("../lib/set-cookie.js");

describe("fetch", () => {
before(() => {
// conditionall skip these tests if the environment doesn't support the necessary features
if (
typeof fetch !== "undefined" &&
typeof Response !== "undefined" &&
typeof new Response().headers.getSetCookie !== "function"
) {
this.skip();
}
});

it("should use getSetCookie method on a Response object", () => {
const fakeResponse = {
headers: {
getSetCookie: () => ["foo=bar"],
},
};

var actual = setCookie.parse(fakeResponse);
var expected = [{ name: "foo", value: "bar" }];
assert.deepEqual(actual, expected);
});
});

0 comments on commit bbfdc6e

Please sign in to comment.