54 lines
2.1 KiB
Dart
54 lines
2.1 KiB
Dart
import 'dart:async';
|
|
import 'dart:io';
|
|
|
|
import 'package:http/http.dart' as http;
|
|
|
|
import 'errors/auth_exception.dart';
|
|
import 'errors/network_exception.dart';
|
|
import 'errors/not_found_exception.dart';
|
|
import 'errors/server_exception.dart';
|
|
|
|
/// Runs [send] and converts transport-level failures (socket/timeout/client
|
|
/// errors) into a [NetworkException] tagged with [label] (e.g. `Talk <uri>`).
|
|
/// Passes through whatever [send] produces, including `null` for the base-class
|
|
/// request hooks that may skip the call.
|
|
Future<http.Response?> sendGuarded(
|
|
String label,
|
|
Future<http.Response>? Function() send,
|
|
) async {
|
|
try {
|
|
return await send();
|
|
} on SocketException catch (e) {
|
|
throw NetworkException(technicalDetails: '$label: ${e.message}');
|
|
} on TimeoutException catch (e) {
|
|
throw NetworkException.timeout(technicalDetails: '$label: $e');
|
|
} on http.ClientException catch (e) {
|
|
throw NetworkException(technicalDetails: '$label: ${e.message}');
|
|
}
|
|
}
|
|
|
|
/// Collapses whitespace and caps an HTTP error body at 500 chars so it can be
|
|
/// embedded in an [AppException]'s technical details without dumping headers.
|
|
String previewBody(String body) {
|
|
final collapsed = body.replaceAll(RegExp(r'\s+'), ' ').trim();
|
|
return collapsed.length > 500 ? '${collapsed.substring(0, 500)}…' : collapsed;
|
|
}
|
|
|
|
/// Builds a `<label> -> HTTP <status>[ body=<preview>]` technical detail line.
|
|
String httpErrorDetail(String label, String body, int status) {
|
|
final preview = previewBody(body);
|
|
return preview.isEmpty
|
|
? '$label -> HTTP $status'
|
|
: '$label -> HTTP $status body=$preview';
|
|
}
|
|
|
|
/// Throws the [AppException] matching a non-2xx HTTP [status], carrying
|
|
/// [detail] as technical details: 401/403 map to auth errors, 404 to
|
|
/// not-found, everything else to a generic server error.
|
|
Never throwForStatus(int status, String detail) {
|
|
if (status == 401) throw AuthException.unauthorized(technicalDetails: detail);
|
|
if (status == 403) throw AuthException.forbidden(technicalDetails: detail);
|
|
if (status == 404) throw NotFoundException(technicalDetails: detail);
|
|
throw ServerException(statusCode: status, technicalDetails: detail);
|
|
}
|