From 2b1defed5149833740d645522c8c26f4985e3b1b Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Tue, 15 Jul 2025 16:31:15 +0000 Subject: [PATCH 1/4] Fix URI, retry, and fragment handling in HTTP interceptor Co-authored-by: me --- BUGS_FIXED.md | 24 ++++++++++++++++++++++++ lib/extensions/uri.dart | 9 ++++++++- lib/http/intercepted_client.dart | 4 ++-- lib/models/retry_policy.dart | 2 +- 4 files changed, 35 insertions(+), 4 deletions(-) create mode 100644 BUGS_FIXED.md diff --git a/BUGS_FIXED.md b/BUGS_FIXED.md new file mode 100644 index 0000000..f27e801 --- /dev/null +++ b/BUGS_FIXED.md @@ -0,0 +1,24 @@ +# Bugs Fixed in HTTP Interceptor + +## Bug #1: `maxRetryAttempts` cannot be overridden in subclasses + +**File**: `lib/models/retry_policy.dart` +**Issue**: The `maxRetryAttempts` field was declared as `final int maxRetryAttempts = 1;` which prevented subclasses from overriding it. +**Fix**: Changed it to `int get maxRetryAttempts => 1;` to allow subclasses to override the getter. +**Impact**: This allows proper inheritance for retry policies, as demonstrated in the example where `ExpiredTokenRetryPolicy` overrides `maxRetryAttempts` to return 2. + +## Bug #2: `_attemptRequest` recursive calls missing `isStream` parameter + +**File**: `lib/http/intercepted_client.dart` +**Issue**: When retrying requests, the recursive calls to `_attemptRequest` didn't pass the `isStream` parameter, which could cause issues when retrying streamed requests. +**Fix**: Added `isStream: isStream` parameter to both recursive calls in the retry logic (lines 295 and 304). +**Impact**: This ensures that streamed requests are properly handled during retries, maintaining the correct response type. + +## Bug #3: URI fragment (#) is lost in `addParameters` method + +**File**: `lib/extensions/uri.dart` +**Issue**: The `addParameters` method used `origin + path` to build the URL but ignored the fragment part of the URI, causing fragments to be lost. +**Fix**: Added logic to preserve the fragment by appending `#$fragment` to the final URL if a fragment exists. +**Impact**: This ensures that URI fragments are preserved when adding query parameters, maintaining the complete URL structure. + +All three bugs have been successfully fixed and the code should now work correctly in all scenarios. \ No newline at end of file diff --git a/lib/extensions/uri.dart b/lib/extensions/uri.dart index f0613f4..8d94a71 100644 --- a/lib/extensions/uri.dart +++ b/lib/extensions/uri.dart @@ -19,6 +19,13 @@ extension AddParameters on Uri { newParameters[key] = value; }); - return buildUrlString(paramUrl, newParameters).toUri(); + String finalUrl = buildUrlString(paramUrl, newParameters); + + // Preserve the fragment if it exists + if (fragment.isNotEmpty) { + finalUrl += '#$fragment'; + } + + return finalUrl.toUri(); } } diff --git a/lib/http/intercepted_client.dart b/lib/http/intercepted_client.dart index 5afac3a..0a9deac 100644 --- a/lib/http/intercepted_client.dart +++ b/lib/http/intercepted_client.dart @@ -293,7 +293,7 @@ class InterceptedClient extends BaseClient { _retryCount += 1; await Future.delayed(retryPolicy! .delayRetryAttemptOnResponse(retryAttempt: _retryCount)); - return _attemptRequest(request); + return _attemptRequest(request, isStream: isStream); } } on Exception catch (error) { if (retryPolicy != null && @@ -302,7 +302,7 @@ class InterceptedClient extends BaseClient { _retryCount += 1; await Future.delayed(retryPolicy! .delayRetryAttemptOnException(retryAttempt: _retryCount)); - return _attemptRequest(request); + return _attemptRequest(request, isStream: isStream); } else { rethrow; } diff --git a/lib/models/retry_policy.dart b/lib/models/retry_policy.dart index 4a2a498..2535d35 100644 --- a/lib/models/retry_policy.dart +++ b/lib/models/retry_policy.dart @@ -45,7 +45,7 @@ abstract class RetryPolicy { FutureOr shouldAttemptRetryOnResponse(BaseResponse response) => false; /// Number of maximum request attempts that can be retried. - final int maxRetryAttempts = 1; + int get maxRetryAttempts => 1; Duration delayRetryAttemptOnException({required int retryAttempt}) => Duration.zero; From 9050465e3da382283727588aa4c9d6608aa98f2c Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Tue, 15 Jul 2025 16:40:31 +0000 Subject: [PATCH 2/4] Changes from background composer bc-723123dd-9fcc-4565-af9d-a426c2531eff --- BUGS_FIXED.md | 24 ------------------------ 1 file changed, 24 deletions(-) delete mode 100644 BUGS_FIXED.md diff --git a/BUGS_FIXED.md b/BUGS_FIXED.md deleted file mode 100644 index f27e801..0000000 --- a/BUGS_FIXED.md +++ /dev/null @@ -1,24 +0,0 @@ -# Bugs Fixed in HTTP Interceptor - -## Bug #1: `maxRetryAttempts` cannot be overridden in subclasses - -**File**: `lib/models/retry_policy.dart` -**Issue**: The `maxRetryAttempts` field was declared as `final int maxRetryAttempts = 1;` which prevented subclasses from overriding it. -**Fix**: Changed it to `int get maxRetryAttempts => 1;` to allow subclasses to override the getter. -**Impact**: This allows proper inheritance for retry policies, as demonstrated in the example where `ExpiredTokenRetryPolicy` overrides `maxRetryAttempts` to return 2. - -## Bug #2: `_attemptRequest` recursive calls missing `isStream` parameter - -**File**: `lib/http/intercepted_client.dart` -**Issue**: When retrying requests, the recursive calls to `_attemptRequest` didn't pass the `isStream` parameter, which could cause issues when retrying streamed requests. -**Fix**: Added `isStream: isStream` parameter to both recursive calls in the retry logic (lines 295 and 304). -**Impact**: This ensures that streamed requests are properly handled during retries, maintaining the correct response type. - -## Bug #3: URI fragment (#) is lost in `addParameters` method - -**File**: `lib/extensions/uri.dart` -**Issue**: The `addParameters` method used `origin + path` to build the URL but ignored the fragment part of the URI, causing fragments to be lost. -**Fix**: Added logic to preserve the fragment by appending `#$fragment` to the final URL if a fragment exists. -**Impact**: This ensures that URI fragments are preserved when adding query parameters, maintaining the complete URL structure. - -All three bugs have been successfully fixed and the code should now work correctly in all scenarios. \ No newline at end of file From b469efe8dbc5911cba9b9910c5a9fa06eaf9d48d Mon Sep 17 00:00:00 2001 From: Alejandro Ulate Date: Fri, 18 Jul 2025 09:48:50 -0600 Subject: [PATCH 3/4] test: add new test for overriding maxRetryAttempts --- test/models/retry_policy_test.dart | 19 ++++++++++++++++++- 1 file changed, 18 insertions(+), 1 deletion(-) diff --git a/test/models/retry_policy_test.dart b/test/models/retry_policy_test.dart index 9e6b49f..65e0d2e 100644 --- a/test/models/retry_policy_test.dart +++ b/test/models/retry_policy_test.dart @@ -12,6 +12,14 @@ main() { test("defaults to 1", () { expect(testObject.maxRetryAttempts, 1); }); + + test("can be overridden", () { + testObject = TestRetryPolicy( + maxRetryAttempts: 5, + ); + + expect(testObject.maxRetryAttempts, 5); + }); }); group("delayRetryAttemptOnException", () { @@ -60,4 +68,13 @@ main() { }); } -class TestRetryPolicy extends RetryPolicy {} +class TestRetryPolicy extends RetryPolicy { + TestRetryPolicy({ + int maxRetryAttempts = 1, + }) : internalMaxRetryAttempts = maxRetryAttempts; + + final int internalMaxRetryAttempts; + + @override + int get maxRetryAttempts => internalMaxRetryAttempts; +} From bf1c8929a74a3da041fcaf3531c33e7df0384f79 Mon Sep 17 00:00:00 2001 From: Alejandro Ulate Date: Fri, 18 Jul 2025 09:51:15 -0600 Subject: [PATCH 4/4] style: fixing analyze report --- lib/extensions/uri.dart | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/lib/extensions/uri.dart b/lib/extensions/uri.dart index 8d94a71..86460f6 100644 --- a/lib/extensions/uri.dart +++ b/lib/extensions/uri.dart @@ -20,7 +20,7 @@ extension AddParameters on Uri { }); String finalUrl = buildUrlString(paramUrl, newParameters); - + // Preserve the fragment if it exists if (fragment.isNotEmpty) { finalUrl += '#$fragment';