Skip to content

Commit 591bb82

Browse files
authored
fix(gotrue): Handle empty error response (#1143)
* fix(gotrue): handle empty error response * test: modify test to verify behavior of empty response exception
1 parent 6a86752 commit 591bb82

File tree

6 files changed

+60
-18
lines changed

6 files changed

+60
-18
lines changed

packages/gotrue/lib/src/fetch.dart

+21-10
Original file line numberDiff line numberDiff line change
@@ -45,27 +45,38 @@ class GotrueFetch {
4545
if (error is! Response) {
4646
throw AuthRetryableFetchException(message: error.toString());
4747
}
48+
final response = error;
4849

4950
// If the status is 500 or above, it's likely a server error,
5051
// and can be retried.
51-
if (error.statusCode >= 500) {
52+
if (response.statusCode >= 500) {
5253
throw AuthRetryableFetchException(
53-
message: error.body,
54-
statusCode: error.statusCode.toString(),
54+
message: response.body,
55+
statusCode: response.statusCode.toString(),
5556
);
5657
}
5758

5859
final dynamic data;
60+
61+
// Catch this case as trying to decode it will throw a misleading [FormatException]
62+
if (response.body.isEmpty) {
63+
throw AuthUnknownException(
64+
message:
65+
'Received an empty response with status code ${response.statusCode}',
66+
originalError: response,
67+
);
68+
}
5969
try {
60-
data = jsonDecode(error.body);
70+
data = jsonDecode(response.body);
6171
} catch (error) {
6272
throw AuthUnknownException(
63-
message: error.toString(), originalError: error);
73+
message: 'Failed to decode error response',
74+
originalError: error,
75+
);
6476
}
65-
6677
String? errorCode;
6778

68-
final responseApiVersion = ApiVersion.fromResponse(error);
79+
final responseApiVersion = ApiVersion.fromResponse(response);
6980

7081
if (responseApiVersion?.isSameOrAfter(ApiVersions.v20240101) ?? false) {
7182
errorCode = _getErrorCode(data, 'code');
@@ -85,21 +96,21 @@ class GotrueFetch {
8596
.isEmpty) {
8697
throw AuthWeakPasswordException(
8798
message: _getErrorMessage(data),
88-
statusCode: error.statusCode.toString(),
99+
statusCode: response.statusCode.toString(),
89100
reasons: List<String>.from(data['weak_password']['reasons']),
90101
);
91102
}
92103
} else if (errorCode == ErrorCode.weakPassword.code) {
93104
throw AuthWeakPasswordException(
94105
message: _getErrorMessage(data),
95-
statusCode: error.statusCode.toString(),
106+
statusCode: response.statusCode.toString(),
96107
reasons: List<String>.from(data['weak_password']?['reasons'] ?? []),
97108
);
98109
}
99110

100111
throw AuthApiException(
101112
_getErrorMessage(data),
102-
statusCode: error.statusCode.toString(),
113+
statusCode: response.statusCode.toString(),
103114
code: errorCode,
104115
);
105116
}

packages/gotrue/lib/src/gotrue_client.dart

+2-2
Original file line numberDiff line numberDiff line change
@@ -1006,8 +1006,8 @@ class GoTrueClient {
10061006

10071007
return AuthResponse(session: session);
10081008
}
1009-
} catch (error) {
1010-
notifyException(error);
1009+
} catch (error, stackTrace) {
1010+
notifyException(error, stackTrace);
10111011
rethrow;
10121012
}
10131013
}

packages/gotrue/lib/src/types/auth_exception.dart

+30-2
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import 'package:gotrue/src/types/error_code.dart';
2+
import 'package:http/http.dart' as http;
23

34
class AuthException implements Exception {
45
/// Human readable error message associated with the error.
@@ -46,24 +47,47 @@ class AuthSessionMissingException extends AuthException {
4647
message ?? 'Auth session missing!',
4748
statusCode: '400',
4849
);
50+
@override
51+
String toString() =>
52+
'AuthSessionMissingException(message: $message, statusCode: $statusCode)';
4953
}
5054

5155
class AuthRetryableFetchException extends AuthException {
5256
AuthRetryableFetchException({
5357
String message = 'AuthRetryableFetchException',
5458
super.statusCode,
5559
}) : super(message);
60+
61+
@override
62+
String toString() =>
63+
'AuthRetryableFetchException(message: $message, statusCode: $statusCode)';
5664
}
5765

5866
class AuthApiException extends AuthException {
5967
AuthApiException(super.message, {super.statusCode, super.code});
68+
69+
@override
70+
String toString() =>
71+
'AuthApiException(message: $message, statusCode: $statusCode, code: $code)';
6072
}
6173

6274
class AuthUnknownException extends AuthException {
75+
/// May contain a non 2xx [http.Response] object or the original thrown error.
6376
final Object originalError;
6477

65-
AuthUnknownException({required String message, required this.originalError})
66-
: super(message);
78+
AuthUnknownException({
79+
required String message,
80+
required this.originalError,
81+
}) : super(
82+
message,
83+
statusCode: originalError is http.Response
84+
? originalError.statusCode.toString()
85+
: null,
86+
);
87+
88+
@override
89+
String toString() =>
90+
'AuthUnknownException(message: $message, originalError: $originalError, statusCode: $statusCode)';
6791
}
6892

6993
class AuthWeakPasswordException extends AuthException {
@@ -74,4 +98,8 @@ class AuthWeakPasswordException extends AuthException {
7498
required super.statusCode,
7599
required this.reasons,
76100
}) : super(message, code: ErrorCode.weakPassword.code);
101+
102+
@override
103+
String toString() =>
104+
'AuthWeakPasswordException(message: $message, statusCode: $statusCode, reasons: $reasons)';
77105
}

packages/gotrue/lib/src/types/auth_state.dart

+1-1
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,6 @@ class AuthState {
1313

1414
@override
1515
String toString() {
16-
return 'AuthState{event: $event, session: $session, fromBroadcast: $fromBroadcast}';
16+
return 'AuthState(event: $event, session: $session, fromBroadcast: $fromBroadcast)';
1717
}
1818
}

packages/gotrue/test/client_test.dart

+5-2
Original file line numberDiff line numberDiff line change
@@ -484,8 +484,11 @@ void main() {
484484
try {
485485
await client.signInWithPassword(email: email1, password: password);
486486
} catch (error) {
487-
expect(error, isA<AuthException>());
488-
expect((error as AuthException).statusCode, '420');
487+
expect(error, isA<AuthUnknownException>());
488+
error as AuthUnknownException;
489+
expect(error.statusCode, '420');
490+
expect(error.originalError, isA<http.Response>());
491+
expect(error.message, contains('empty response'));
489492
}
490493
});
491494
});

packages/gotrue/test/custom_http_client.dart

+1-1
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ class CustomHttpClient extends BaseClient {
1010
Future<StreamedResponse> send(BaseRequest request) async {
1111
//Return custom status code to check for usage of this client.
1212
return StreamedResponse(
13-
request.finalize(),
13+
Stream.empty(),
1414
420,
1515
request: request,
1616
);

0 commit comments

Comments
 (0)