Skip to content

Commit 2472b8b

Browse files
authored
[cookie_manager] Fix set-cookie multi-value parsing (#1675)
### New Pull Request Checklist - [x] I have read the [Documentation](https://pub.dev/documentation/dio/latest/) - [x] I have searched for a similar pull request in the [project](https://github.com/cfug/dio/pulls) and found none - [x] I have updated this branch with the latest `main` branch to avoid conflicts (via merge from master or rebase) - [x] I have added the required tests to prove the fix/feature I'm adding - [ ] I have updated the documentation (if necessary) - [x] I have run the tests without failures ### Additional context and info (if any) Fix #1674
1 parent 17bdbb3 commit 2472b8b

File tree

3 files changed

+45
-3
lines changed

3 files changed

+45
-3
lines changed

plugins/cookie_manager/CHANGELOG.md

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
# CHANGELOG
22

3-
# 2.1.1
3+
## 2.1.1
44

55
* Fix #1651
6+
* Fix #1674
67

78
## 2.1.0
89

plugins/cookie_manager/lib/src/cookie_mgr.dart

Lines changed: 16 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,17 @@ const _kIsWeb = bool.hasEnvironment('dart.library.js_util')
88
? bool.fromEnvironment('dart.library.js_util')
99
: identical(0, 0.0);
1010

11+
/// - `(?<=)` is a positive lookbehind assertion that matches a comma (",")
12+
/// only if it's preceded by a specific pattern. In this case, the lookbehind
13+
/// assertion is empty, which means it matches any comma that's preceded by any character.
14+
/// - `(,)` captures the comma as a group.
15+
/// - `(?=[^;]+?=)` is a positive lookahead assertion that matches a comma only
16+
/// if it's followed by a specific pattern. In this case, it matches any comma
17+
/// that's followed by one or more characters that are not semicolons (";") and
18+
/// then an equals sign ("="). This ensures that the comma is not part of a cookie
19+
/// attribute like "expires=Sun, 19 Feb 3000 01:43:15 GMT", which could also contain commas.
20+
final _setCookieReg = RegExp('(?<=)(,)(?=[^;]+?=)');
21+
1122
/// Cookie manager for HTTP requests based on [CookieJar].
1223
class CookieManager extends Interceptor {
1324
const CookieManager(
@@ -70,9 +81,12 @@ class CookieManager extends Interceptor {
7081
}
7182

7283
Future<void> _saveCookies(Response response) async {
73-
final cookies = response.headers[HttpHeaders.setCookieHeader];
84+
final setCookies = response.headers[HttpHeaders.setCookieHeader];
7485

75-
if (cookies != null) {
86+
if (setCookies != null) {
87+
final cookies = setCookies
88+
.map((str) => str.split(_setCookieReg))
89+
.expand((element) => element);
7690
await cookieJar.saveFromResponse(
7791
response.requestOptions.uri,
7892
cookies.map((str) => Cookie.fromSetCookieValue(str)).toList(),

plugins/cookie_manager/test/cookies_test.dart

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -49,6 +49,33 @@ void main() {
4949
HttpHeaders.cookieHeader: mockSecondRequestCookies,
5050
});
5151

52+
cookieManager.onRequest(options, mockRequestInterceptorHandler);
53+
});
54+
test('testing set-cookies parsing', () async {
55+
const List<String> mockResponseCookies = [
56+
'key=value; expires=Sun, 19 Feb 3000 00:42:14 GMT; path=/; HttpOnly; secure; SameSite=Lax',
57+
'key1=value1; expires=Sun, 19 Feb 3000 01:43:15 GMT; path=/; HttpOnly; secure; SameSite=Lax, '
58+
'key2=value2; expires=Sat, 20 May 3000 00:43:15 GMT; path=/; HttpOnly; secure; SameSite=Lax',
59+
];
60+
const exampleUrl = 'https://example.com';
61+
62+
final expectResult = 'key=value; key1=value1; key2=value2';
63+
64+
final cookieJar = CookieJar();
65+
final cookieManager = CookieManager(cookieJar);
66+
final mockRequestInterceptorHandler =
67+
MockRequestInterceptorHandler(expectResult);
68+
final mockResponseInterceptorHandler = MockResponseInterceptorHandler();
69+
final requestOptions = RequestOptions(baseUrl: exampleUrl);
70+
71+
final mockResponse = Response(
72+
requestOptions: requestOptions,
73+
headers: Headers.fromMap(
74+
{HttpHeaders.setCookieHeader: mockResponseCookies},
75+
));
76+
cookieManager.onResponse(mockResponse, mockResponseInterceptorHandler);
77+
final options = RequestOptions(baseUrl: exampleUrl);
78+
5279
cookieManager.onRequest(options, mockRequestInterceptorHandler);
5380
});
5481
}

0 commit comments

Comments
 (0)