Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 17 additions & 0 deletions lib/hiddifycore/core_start_failure.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import 'package:grpc/grpc.dart';
import 'package:hiddify/features/connection/model/connection_failure.dart';
import 'package:hiddify/singbox/model/core_status.dart';

({CoreStatus status, ConnectionFailure failure}) mapCoreStartGrpcError(GrpcError error) {
final grpcMessage = error.message?.trim();
final detail = [
'failed to start background core',
error.codeName,
if (grpcMessage != null && grpcMessage.isNotEmpty) grpcMessage,
].join(': ');
final status = CoreStatus.stopped(alert: CoreAlert.startFailed, message: detail);
final failure = error.code == StatusCode.unavailable
? ConnectionFailure.backgroundCoreNotAvailable(detail)
: ConnectionFailure.unexpected(detail);
return (status: status, failure: failure);
}
12 changes: 4 additions & 8 deletions lib/hiddifycore/hiddify_core_service.dart
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import 'package:hiddify/core/preferences/general_preferences.dart';
import 'package:hiddify/features/connection/model/connection_failure.dart';
import 'package:hiddify/features/settings/data/config_option_repository.dart';
import 'package:hiddify/hiddifycore/core_interface/core_interface.dart';
import 'package:hiddify/hiddifycore/core_start_failure.dart';
import 'package:hiddify/hiddifycore/generated/v2/hcommon/common.pb.dart';
import 'package:hiddify/hiddifycore/generated/v2/hcore/hcore.pb.dart';
import 'package:hiddify/hiddifycore/generated/v2/hcore/hcore_service.pbgrpc.dart';
Expand Down Expand Up @@ -184,15 +185,10 @@ class HiddifyCoreService with InfraLogger {
}
} on GrpcError catch (e) {
loggy.error("failed to start bg core: $e");
final (:status, :failure) = mapCoreStartGrpcError(e);
statusController.add(currentState = status);
ref.read(coreRestartSignalProvider.notifier).restart();
if (e.code == StatusCode.unavailable) {
return left(const ConnectionFailure.unexpected("background core is not started yet!"));
}
// throw InvalidConfig(e.message);
// throw DioException.connectionError(requestOptions: RequestOptions(), reason: e.codeName, error: e);

// throw DioException(requestOptions: RequestOptions(), error: e);
return left(const ConnectionFailure.unexpected("failed to start background core"));
return left(failure);
}

// if (res.messageType != MessageType.EMPTY) return left(res);
Expand Down
35 changes: 35 additions & 0 deletions test/hiddifycore/core_start_failure_test.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import 'package:flutter_test/flutter_test.dart';
import 'package:grpc/grpc.dart';
import 'package:hiddify/features/connection/model/connection_failure.dart';
import 'package:hiddify/hiddifycore/core_start_failure.dart';
import 'package:hiddify/singbox/model/core_status.dart';

void main() {
test('preserves gRPC details and publishes a stopped status', () {
final result = mapCoreStartGrpcError(
const GrpcError.unknown('start service: detour to an empty direct outbound makes no sense'),
);

const detail =
'failed to start background core: UNKNOWN: '
'start service: detour to an empty direct outbound makes no sense';
expect(result.status, const CoreStatus.stopped(alert: CoreAlert.startFailed, message: detail));
expect(result.failure, const ConnectionFailure.unexpected(detail));
});

test('classifies an unavailable Core separately', () {
final result = mapCoreStartGrpcError(const GrpcError.unavailable('connection refused'));

const detail = 'failed to start background core: UNAVAILABLE: connection refused';
expect(result.status, const CoreStatus.stopped(alert: CoreAlert.startFailed, message: detail));
expect(result.failure, const ConnectionFailure.backgroundCoreNotAvailable(detail));
});

test('does not add an empty message segment', () {
final result = mapCoreStartGrpcError(const GrpcError.unknown());

const detail = 'failed to start background core: UNKNOWN';
expect(result.status, const CoreStatus.stopped(alert: CoreAlert.startFailed, message: detail));
expect(result.failure, const ConnectionFailure.unexpected(detail));
});
}