Skip to content

Commit ba4e9e6

Browse files
authored
Merge pull request #503 from DanXi-Dev/fix-webvpn-again
fix: expand redirect code handling for WebVPN login
2 parents f86c83e + 829a4f1 commit ba4e9e6

File tree

2 files changed

+31
-6
lines changed

2 files changed

+31
-6
lines changed

lib/util/io/dio_utils.dart

+29-5
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,30 @@ class DioUtils {
4545
return status! < 400;
4646
});
4747

48+
/// Get the location to which the [response] is redirected.
49+
///
50+
/// If the response is not a valid redirect response, return null.
51+
///
52+
/// It doesn't check whether the "location" is empty, relative, etc.
53+
/// You should do it yourself.
54+
///
55+
/// ## Formality
56+
/// This method is a bit different from the original one (see [HttpClientResponse.isRedirect] in `http_impl.dart`),
57+
/// which considers the request HTTP method and excludes some invalid combinations (e.g., POST can only be redirected by 303).
58+
/// But in practice, we don't need to (and cannot) be so strict. Some badly designed servers may return 302 for POST requests.
59+
static String? getRedirectLocation(Response<dynamic> response) {
60+
final statusCode = response.statusCode;
61+
bool isRedirect = statusCode == HttpStatus.movedPermanently ||
62+
statusCode == HttpStatus.permanentRedirect ||
63+
statusCode == HttpStatus.found ||
64+
statusCode == HttpStatus.seeOther ||
65+
statusCode == HttpStatus.temporaryRedirect;
66+
if (isRedirect) {
67+
return response.headers['location']?[0];
68+
}
69+
return null;
70+
}
71+
4872
/// Process the redirect response manually and return the final response.
4973
///
5074
/// What makes this method necessary is that the default behavior of [Dio] is
@@ -54,10 +78,8 @@ class DioUtils {
5478
static Future<Response<dynamic>> processRedirect(
5579
Dio dio, Response<dynamic> response) async {
5680
// Prevent the redirect being processed by HttpClient, with the 302 response caught manually.
57-
if (response.statusCode == 302 &&
58-
response.headers['location'] != null &&
59-
response.headers['location']!.isNotEmpty) {
60-
String location = response.headers['location']![0];
81+
String? location = getRedirectLocation(response);
82+
if (location != null) {
6183
if (location.isEmpty) return response;
6284
if (!Uri.parse(location).isAbsolute) {
6385
location = '${response.requestOptions.uri.origin}/$location';
@@ -110,7 +132,9 @@ class DioUtils {
110132
/// (The original [fetch] will throw a [DioException] WITHOUT the response data when [FormatException] occurs.)
111133
static Future<Response<T>> fetchWithJsonError<T>(
112134
Dio dio, RequestOptions options) async {
113-
if (T == dynamic || options.responseType == ResponseType.bytes || options.responseType == ResponseType.stream) {
135+
if (T == dynamic ||
136+
options.responseType == ResponseType.bytes ||
137+
options.responseType == ResponseType.stream) {
114138
// If T is dynamic, or the caller wants bytes or stream, just call the original fetch.
115139
// Because we are going to parse the response data as [String] below!
116140
return await dio.fetch(options) as Response<T>;

lib/util/webvpn_proxy.dart

+2-1
Original file line numberDiff line numberDiff line change
@@ -158,7 +158,8 @@ class WebvpnProxy {
158158
Dio dio, IndependentCookieJar jar, PersonInfo? info) async {
159159
Response<dynamic>? res = await dio.get(WEBVPN_ID_REQUEST_URL,
160160
options: DioUtils.NON_REDIRECT_OPTION_WITH_FORM_TYPE);
161-
if (res.statusCode == 302) {
161+
if (DioUtils.getRedirectLocation(res) != null) {
162+
// if we are redirected to UIS, we need to login to UIS first
162163
await DioUtils.processRedirect(dio, res);
163164
res = await UISLoginTool.loginUIS(dio, WEBVPN_UIS_LOGIN_URL, jar, info);
164165
if (res == null) {

0 commit comments

Comments
 (0)