Skip to content

Commit 0fe470c

Browse files
Merge pull request js-cookie#71 from js-cookie/write-converter
Add write converter in addition to read converter Closes js-cookiegh-71. Closes js-cookiegh-70.
2 parents d832048 + 951353e commit 0fe470c

File tree

4 files changed

+145
-11
lines changed

4 files changed

+145
-11
lines changed

README.md

Lines changed: 19 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@ A simple, lightweight JavaScript API for handling cookies
99
* [Unobtrusive](#json) JSON support
1010
* Supports AMD/CommonJS
1111
* [RFC 6265](https://tools.ietf.org/html/rfc6265) compliant
12-
* Enable [custom decoding](#converter)
1312
* Very good [Wiki](https://github.com/js-cookie/js-cookie/wiki)
13+
* Enable [custom encoding/decoding](#converters)
1414
* **~800 bytes** gzipped!
1515

1616
**If you're viewing this at https://github.com/js-cookie/js-cookie, you're reading the documentation for the master branch.
@@ -213,7 +213,9 @@ Cookies.get('name'); // => 'value'
213213
Cookies.remove('name', { secure: true });
214214
```
215215

216-
## Converter
216+
## Converters
217+
218+
### Read
217219

218220
Create a new instance of the api that overrides the default decoding implementation.
219221
All get methods that rely in a proper decoding to work, such as `Cookies.get()` and `Cookies.get('name')`, will run the converter first for each cookie.
@@ -234,15 +236,25 @@ cookies.get('default'); // 北
234236
cookies.get(); // { escaped: '北', default: '北' }
235237
```
236238

237-
Example for parsing the value from a cookie generated with PHP's `setcookie()` method:
239+
### Write
240+
241+
Create a new instance of the api that overrides the default encoding implementation:
238242

239243
```javascript
240-
// 'cookie+with+space' => 'cookie with space'
241-
Cookies.withConverter(function (value) {
242-
return value.replace(/\+/g, ' ');
243-
}).get('foo');
244+
Cookies.withConverter({
245+
read: function (value, name) {
246+
// Read converter
247+
},
248+
write: function (value, name) {
249+
// Write converter
250+
}
251+
});
244252
```
245253

254+
## Server-side integration
255+
256+
Check out the [Servers Docs](SERVER_SIDE.md)
257+
246258
## Contributing
247259

248260
Check out the [Contributing Guidelines](CONTRIBUTING.md)

SERVER_SIDE.md

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
# Server-side integration
2+
3+
There are some servers that are not compliant with the [RFC 6265](http://tools.ietf.org/html/rfc6265). For those, some characters that are not encoded by JavaScript Cookie might be treated differently.
4+
5+
Here we document the most important server-side peculiarities and their workarounds. Feel free to send a [Pull Request](https://github.com/js-cookie/js-cookie/blob/master/CONTRIBUTING.md#pull-requests) if you see something that can be improved.
6+
7+
*Disclaimer: This documentation is entirely based on community provided information. The examples below should be used only as a reference.*
8+
9+
## PHP
10+
11+
In PHP, `setcookie()` function encodes cookie values using `urlencode()` function, which applies `%`-encoding but also encodes spaces as `+` signs, [for historical reasons](http://php.net/manual/en/function.urlencode.php#function.urlencode). When cookies are read back via `$_COOKIE` or `filter_input(INPUT_COOKIE)`, they would go trough a decoding process which decodes `%`-encoded sequences and also converts `+` signs back to spaces. However, the plus (`+`) sign is valid cookie character by itself, which means that libraries that adhere to standards will interpret `+` signs differently to PHP.
12+
13+
This presents two types of problems:
14+
15+
1. PHP writes a cookie via `setcookie()` and all spaces get converted to `+` signs. JavaScript Cookie read `+` signs and uses them literally, since it is a valid cookie character.
16+
2. JavaScript Cookie writes a cookie with a value that contains `+` signs and stores it as is, since it is a valid cookie character. PHP read a cookie and converts `+` signs to spaces.
17+
18+
To make both PHP and JavaScript Cookie play nicely together?
19+
20+
**In PHP**, use `setrawcookie()` instead of `setcookie()`:
21+
22+
```php
23+
setrawcookie($name, rawurlencode($value));
24+
```
25+
26+
**In JavaScript**, use a custom converter.
27+
28+
**Example**:
29+
30+
```javascript
31+
var PHPCookies = Cookies.withConverter({
32+
write: function (value) {
33+
// Encode all characters according to the "encodeURIComponent" spec
34+
return encodeURIComponent(value)
35+
// Revert the characters that are unnecessarly encoded but are
36+
// allowed in a cookie value, except for the plus sign (%2B)
37+
.replace(/%(23|24|26|3A|3C|3E|3D|2F|3F|40|5B|5D|5E|60|7B|7D|7C)/g, decodeURIComponent);
38+
},
39+
read: function (value) {
40+
return value
41+
// Decode the plus sign to spaces first, otherwise "legit" encoded pluses
42+
// will be replaced incorrectly
43+
.replace(/\+/g, ' ')
44+
// Decode all characters according to the "encodeURIComponent" spec
45+
.replace(/(%[0-9A-Z]{2})+/g, decodeURIComponent);
46+
}
47+
});
48+
```
49+
50+
Rack seems to have [a similar problem](https://github.com/js-cookie/js-cookie/issues/70#issuecomment-132503017).
51+
52+
## Tomcat 7.x
53+
54+
It seems that there is a situation where Tomcat does not [read the parens correctly](https://github.com/js-cookie/js-cookie/issues/92#issue-107743407). To fix this you need to write a custom write converter.
55+
56+
**Example**:
57+
58+
```javascript
59+
var TomcatCookies = Cookies.withConverter({
60+
write: function (value) {
61+
// Encode all characters according to the "encodeURIComponent" spec
62+
return encodeURIComponent(value)
63+
// Revert the characters that are unnecessarly encoded but are
64+
// allowed in a cookie value
65+
.replace(/%(23|24|26|2B|3A|3C|3E|3D|2F|3F|40|5B|5D|5E|60|7B|7D|7C)/g, decodeURIComponent)
66+
// Encode the parens that are interpreted incorrectly by Tomcat
67+
.replace(/[\(\)]/g, escape);
68+
}
69+
});
70+
```
71+
72+
Alternatively, you can check the [Java Cookie](https://github.com/js-cookie/java-cookie) project, which integrates nicely with JavaScript Cookie.
73+
74+
## JBoss 7.1.1
75+
76+
It seems that the servlet implementation of JBoss 7.1.1 [does not read some characters correctly](https://github.com/js-cookie/js-cookie/issues/70#issuecomment-148944674), even though they are allowed as per [RFC 6265](https://tools.ietf.org/html/rfc6265#section-4.1.1). To fix this you need to write a custom converter to send those characters correctly.
77+
78+
**Example**:
79+
80+
```javascript
81+
var JBossCookies = Cookies.withConverter({
82+
write: function (value) {
83+
// Encode all characters according to the "encodeURIComponent" spec
84+
return encodeURIComponent(value)
85+
// Revert the characters that are unnecessarly encoded but are
86+
// allowed in a cookie value
87+
.replace(/%(23|24|26|2B|3A|3C|3E|3D|2F|3F|40|5B|5D|5E|60|7B|7D|7C)/g, decodeURIComponent)
88+
// Encode again the characters that are not allowed in JBoss 7.1.1, like "[" and "]":
89+
.replace(/[\[\]]/g, encodeURIComponent);
90+
}
91+
});
92+
```
93+
94+
Alternatively, you can check the [Java Cookie](https://github.com/js-cookie/java-cookie) project, which integrates nicely with JavaScript Cookie.

src/js.cookie.js

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -55,8 +55,12 @@
5555
}
5656
} catch (e) {}
5757

58-
value = encodeURIComponent(String(value));
59-
value = value.replace(/%(23|24|26|2B|3A|3C|3E|3D|2F|3F|40|5B|5D|5E|60|7B|7D|7C)/g, decodeURIComponent);
58+
if (!converter.write) {
59+
value = encodeURIComponent(String(value))
60+
.replace(/%(23|24|26|2B|3A|3C|3E|3D|2F|3F|40|5B|5D|5E|60|7B|7D|7C)/g, decodeURIComponent);
61+
} else {
62+
value = converter.write(value, key);
63+
}
6064

6165
key = encodeURIComponent(String(key));
6266
key = key.replace(/%(23|24|26|2B|5E|60|7C)/g, decodeURIComponent);
@@ -94,7 +98,9 @@
9498
}
9599

96100
try {
97-
cookie = converter && converter(cookie, name) || cookie.replace(rdecode, decodeURIComponent);
101+
cookie = converter.read ?
102+
converter.read(cookie, name) : converter(cookie, name) ||
103+
cookie.replace(rdecode, decodeURIComponent);
98104

99105
if (this.json) {
100106
try {
@@ -135,5 +141,5 @@
135141
return api;
136142
}
137143

138-
return init();
144+
return init(function () {});
139145
}));

test/tests.js

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -279,6 +279,28 @@ QUnit.test('should be able to conditionally decode a single malformed cookie', f
279279
assert.strictEqual(document.cookie, '', 'should remove everything');
280280
});
281281

282+
// github.com/js-cookie/js-cookie/issues/70
283+
QUnit.test('should be able to create a write decoder', function (assert) {
284+
assert.expect(1);
285+
Cookies.withConverter({
286+
write: function (value) {
287+
return value.replace('+', '%2B');
288+
}
289+
}).set('c', '+');
290+
assert.strictEqual(document.cookie, 'c=%2B', 'should call the write converter');
291+
});
292+
293+
QUnit.test('should be able to use read and write decoder', function (assert) {
294+
assert.expect(1);
295+
document.cookie = 'c=%2B';
296+
var cookies = Cookies.withConverter({
297+
read: function (value) {
298+
return value.replace('%2B', '+');
299+
}
300+
});
301+
assert.strictEqual(cookies.get('c'), '+', 'should call the read converter');
302+
});
303+
282304
QUnit.module('JSON handling', lifecycle);
283305

284306
QUnit.test('Number', function (assert) {

0 commit comments

Comments
 (0)