Skip to content

Commit cacb479

Browse files
committed
fix: when WebVPN token is expired, the webvpn_proxy is stuck in the FormatException state
1 parent b2d694a commit cacb479

File tree

2 files changed

+64
-11
lines changed

2 files changed

+64
-11
lines changed

lib/util/io/dio_utils.dart

+35
Original file line numberDiff line numberDiff line change
@@ -104,4 +104,39 @@ class DioUtils {
104104
setProxy(dio, SettingsProvider.getInstance().proxy);
105105
return dio;
106106
}
107+
108+
/// Fetch with the [options], but when T is JSON-like and the response is not,
109+
/// throw a [DioException] WITH the response data.
110+
/// (The original [fetch] will throw a [DioException] WITHOUT the response data when [FormatException] occurs.)
111+
static Future<Response<T>> fetchWithJsonError<T>(
112+
Dio dio, RequestOptions options) async {
113+
Response<String> response = await dio.fetch(options);
114+
if (T == String) {
115+
return response as Response<T>;
116+
} else {
117+
try {
118+
dynamic transformedResponseData = await dio.transformer
119+
.transformResponse(
120+
options.copyWith(responseType: ResponseType.json),
121+
ResponseBody.fromString(response.data!, response.statusCode!,
122+
headers: response.headers.map,
123+
statusMessage: response.statusMessage));
124+
return Response<T>(
125+
data: transformedResponseData,
126+
requestOptions: response.requestOptions,
127+
statusCode: response.statusCode,
128+
statusMessage: response.statusMessage,
129+
isRedirect: response.isRedirect,
130+
redirects: response.redirects,
131+
headers: response.headers,
132+
);
133+
} catch (e) {
134+
throw DioException(
135+
requestOptions: options,
136+
response: response,
137+
error: e,
138+
);
139+
}
140+
}
141+
}
107142
}

lib/util/webvpn_proxy.dart

+29-11
Original file line numberDiff line numberDiff line change
@@ -79,21 +79,21 @@ class WebvpnProxy {
7979
}
8080
}
8181

82-
// Check if we have logged in to WebVPN, returns false if we haven't
83-
static bool checkResponse(Response<dynamic> response) {
82+
// Check if we should login to WebVPN. Return `true` if we should login now.
83+
static bool isResponseRequiringLogin(Response<dynamic> response) {
8484
// When 302 is raised when the method is `POST`, it means that we haven't logged in
8585
if (response.requestOptions.method == "POST" &&
8686
response.statusCode == 302 &&
8787
response.headers['location'] != null &&
8888
response.headers['location']!.isNotEmpty) {
89-
return false;
89+
return true;
9090
}
9191

9292
if (response.realUri.toString().startsWith(WEBVPN_LOGIN_URL)) {
93-
return false;
93+
return true;
9494
}
9595

96-
return true;
96+
return false;
9797
}
9898

9999
/// Bind WebVPN proxy to a person info so that it updates automatically when [personInfo] changes.
@@ -284,19 +284,37 @@ class WebvpnProxy {
284284
await loginWebvpn(dio);
285285

286286
// First attempt
287-
Response<T> response = await dio.fetch<T>(options);
288-
if (checkResponse(response)) {
289-
return response;
287+
try {
288+
Response<T> response = await DioUtils.fetchWithJsonError(dio, options);
289+
if (!isResponseRequiringLogin(response)) {
290+
return response;
291+
}
292+
} on DioException catch (e) {
293+
if (e.response == null) {
294+
rethrow;
295+
}
296+
if (!isResponseRequiringLogin(e.response!)) {
297+
rethrow;
298+
}
290299
}
291300

292301
// Re-login
293302
isLoggedIn = false;
294303
await loginWebvpn(dio);
295304

296305
// Second attempt
297-
response = await dio.fetch<T>(options);
298-
if (checkResponse(response)) {
299-
return response;
306+
try {
307+
Response<T> response = await DioUtils.fetchWithJsonError(dio, options);
308+
if (!isResponseRequiringLogin(response)) {
309+
return response;
310+
}
311+
} on DioException catch (e) {
312+
if (e.response == null) {
313+
rethrow;
314+
}
315+
if (!isResponseRequiringLogin(e.response!)) {
316+
rethrow;
317+
}
300318
}
301319

302320
// All attempts failed

0 commit comments

Comments
 (0)