From 1b0ba629583aadda91dc3c8e829f6cf871b83ffc Mon Sep 17 00:00:00 2001 From: Paul Suckow <46897578+Dampfwalze@users.noreply.github.com> Date: Mon, 3 Apr 2023 18:11:21 +0200 Subject: [PATCH 1/4] client_cookie: Add extended cookie directive parsing --- client_cookie/lib/src/client_cookie_base.dart | 74 ++++++++++++++++--- 1 file changed, 62 insertions(+), 12 deletions(-) diff --git a/client_cookie/lib/src/client_cookie_base.dart b/client_cookie/lib/src/client_cookie_base.dart index a32700d..487f848 100644 --- a/client_cookie/lib/src/client_cookie_base.dart +++ b/client_cookie/lib/src/client_cookie_base.dart @@ -59,7 +59,13 @@ class ClientCookie { } /// Parses one 'set-cookie' item - factory ClientCookie.fromSetCookie(String cookieItem) { + /// + /// Parameters: + /// - [enableExtendedDirectiveParsing] + /// When enabled and a directive is not recognized, the parser will try extended methods to find the right directive. These methods are: + /// - Comparing Case-insensitive + factory ClientCookie.fromSetCookie(String cookieItem, + {bool enableExtendedDirectiveParsing = true}) { final List parts = cookieItem.split(';').reversed.map((String str) => str.trim()).toList(); @@ -81,14 +87,25 @@ class ClientCookie { } for (String directive in parts) { - final List points = - directive.split('=').map((String str) => str.trim()).toList(); - if (points.length == 0 || points.length > 2) + final List points = directive // + .split('=') + .map((String str) => str.trim()) + .toList(); + + if (points.length == 0 || points.length > 2) { throw Exception('Invalid directive!'); - final String key = points.first; + } + + String key = points.first; final String val = points.length == 2 ? points.last : ''; if (!_parsers.containsKey(key)) { - throw _invalidDirective; + if (!enableExtendedDirectiveParsing) { + throw _invalidDirective; + } + key = _parsers.keys.firstWhere( + (e) => e.toLowerCase() == key.toLowerCase(), + orElse: () => throw _invalidDirective, + ); } map[key] = _parsers[key](val); } @@ -234,11 +251,19 @@ class ClientCookie { /// A store for Cookies class CookieStore { + /// Influences the parsing of the 'set-cookie' header. + /// + /// When enabled and a directive is not recognized, the parser will try extended methods to find the right directive. These methods are: + /// - Comparing Case-insensitive + bool enableExtendedDirectiveParsing; + /// The actual storeage Map cookieMap = {}; List get cookies => cookieMap.values.toList(); + CookieStore({this.enableExtendedDirectiveParsing = true}); + /// Returns a cookie by [name] /// /// Returns [null] if cookie with name is not present @@ -276,19 +301,37 @@ class CookieStore { } /// Parses and adds all 'set-cookies' from [http.Response] to the Cookie store - void addFromResponse(http.Response resp) { + /// + /// Parameters: + /// - [enableExtendedDirectiveParsing] + /// When enabled and a directive is not recognized, the parser will try extended methods to find the right directive. These methods are: + /// - Comparing Case-insensitive + /// + /// overrides [CookieStore.enableExtendedDirectiveParsing] + void addFromResponse(http.Response resp, + {bool? enableExtendedDirectiveParsing}) { if (resp.headers.containsKey('set-cookie')) { addFromHeader(resp.headers['set-cookie']!); } } /// Parses and adds all 'set-cookies' from [http.Response] to the Cookie store - void addFromHeader(String? setCookieLine) { + /// + /// Parameters: + /// - [enableExtendedDirectiveParsing] + /// When enabled and a directive is not recognized, the parser will try extended methods to find the right directive. These methods are: + /// - Comparing Case-insensitive + /// + /// overrides [CookieStore.enableExtendedDirectiveParsing] + void addFromHeader(String? setCookieLine, + {bool? enableExtendedDirectiveParsing}) { if (setCookieLine == null || setCookieLine.isEmpty) { return; } - final map = parseSetCookie(setCookieLine); + final map = parseSetCookie(setCookieLine, + enableExtendedDirectiveParsing: enableExtendedDirectiveParsing ?? + this.enableExtendedDirectiveParsing); for (String name in map.keys) { final ClientCookie cookie = map[name]!; @@ -304,8 +347,14 @@ class CookieStore { String toString() => 'CookieStore($cookieMap)'; } -/// Parses and adds all 'set-cookies' from [http.Response] to the Cookiewqa parseSetCookie(String? setCookieLine) { +/// Parses and adds all 'set-cookies' from [http.Response] to the Cookie store +/// +/// Parameters: +/// - [enableExtendedDirectiveParsing] +/// When enabled and a directive is not recognized, the parser will try extended methods to find the right directive. These methods are: +/// - Comparing Case-insensitive +Map parseSetCookie(String? setCookieLine, + {bool enableExtendedDirectiveParsing = true}) { final cookieMap = {}; if (setCookieLine == null || setCookieLine.isEmpty) { @@ -313,7 +362,8 @@ Map parseSetCookie(String? setCookieLine) { } for (String itemStr in setCookieLine.split(',')) { - final cookie = ClientCookie.fromSetCookie(itemStr); + final cookie = ClientCookie.fromSetCookie(itemStr, + enableExtendedDirectiveParsing: enableExtendedDirectiveParsing); cookieMap[cookie.name] = cookie; } From 67bd590877982adb6fa3f762af0aa5d8ebe3da1d Mon Sep 17 00:00:00 2001 From: Paul Suckow <46897578+Dampfwalze@users.noreply.github.com> Date: Mon, 3 Apr 2023 20:54:02 +0200 Subject: [PATCH 2/4] client_cookie: More robust splitting of cookies --- client_cookie/lib/src/client_cookie_base.dart | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/client_cookie/lib/src/client_cookie_base.dart b/client_cookie/lib/src/client_cookie_base.dart index 487f848..859d057 100644 --- a/client_cookie/lib/src/client_cookie_base.dart +++ b/client_cookie/lib/src/client_cookie_base.dart @@ -361,7 +361,8 @@ Map parseSetCookie(String? setCookieLine, return cookieMap; } - for (String itemStr in setCookieLine.split(',')) { + final cookieStrings = setCookieLine.split(RegExp(r',(?=[A-Za-z0-9_-]+\s*=)')); + for (String itemStr in cookieStrings) { final cookie = ClientCookie.fromSetCookie(itemStr, enableExtendedDirectiveParsing: enableExtendedDirectiveParsing); cookieMap[cookie.name] = cookie; From 06d1b8082f1972abdbbb02b4f6194f4a58dc52ec Mon Sep 17 00:00:00 2001 From: Paul Suckow <46897578+Dampfwalze@users.noreply.github.com> Date: Mon, 3 Apr 2023 23:28:19 +0200 Subject: [PATCH 3/4] client_cookie: Add dot to valid cookie name characters --- client_cookie/lib/src/client_cookie_base.dart | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/client_cookie/lib/src/client_cookie_base.dart b/client_cookie/lib/src/client_cookie_base.dart index 859d057..53242e5 100644 --- a/client_cookie/lib/src/client_cookie_base.dart +++ b/client_cookie/lib/src/client_cookie_base.dart @@ -361,7 +361,8 @@ Map parseSetCookie(String? setCookieLine, return cookieMap; } - final cookieStrings = setCookieLine.split(RegExp(r',(?=[A-Za-z0-9_-]+\s*=)')); + final cookieStrings = + setCookieLine.split(RegExp(r',(?=[A-Za-z0-9._-]+\s*=)')); for (String itemStr in cookieStrings) { final cookie = ClientCookie.fromSetCookie(itemStr, enableExtendedDirectiveParsing: enableExtendedDirectiveParsing); From 01471a6672bb4cbfc0ac8fccf0e7ee033808ee5b Mon Sep 17 00:00:00 2001 From: Paul Suckow <46897578+Dampfwalze@users.noreply.github.com> Date: Wed, 5 Apr 2023 10:32:58 +0200 Subject: [PATCH 4/4] client_cookie: Fix: Cookies in cookie header should be separated with `;` instead of `,` https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Cookie#syntax --- client_cookie/lib/src/client_cookie_base.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/client_cookie/lib/src/client_cookie_base.dart b/client_cookie/lib/src/client_cookie_base.dart index 53242e5..b7fe143 100644 --- a/client_cookie/lib/src/client_cookie_base.dart +++ b/client_cookie/lib/src/client_cookie_base.dart @@ -297,7 +297,7 @@ class CookieStore { for (String rem in removes) cookieMap.remove(rem); - return rets.join(', '); + return rets.join('; '); } /// Parses and adds all 'set-cookies' from [http.Response] to the Cookie store