performRequest method

  1. @override
Future<FittorResponse> performRequest(
  1. FittorRequest request,
  2. Stopwatch stopwatch
)
override

Implementation

@override
Future<FittorResponse> performRequest(
    FittorRequest request, Stopwatch stopwatch) async {
  final completer = Completer<FittorResponse>();

  final xhr = web.XMLHttpRequest();

  // Set timeout
  final timeout = request.timeout ?? const Duration(seconds: 30);
  xhr.timeout = timeout.inMilliseconds;

  // Use text response type for simplicity and compatibility
  xhr.responseType = 'text';

  // Handle load event
  xhr.onload = (web.Event event) {
    stopwatch.stop();

    try {
      final responseHeaders = FittorHeaders();
      final allHeaders = xhr.getAllResponseHeaders();
      if (allHeaders.isNotEmpty) {
        final headerLines = allHeaders.split('\r\n');
        for (final line in headerLines) {
          if (line.trim().isNotEmpty && line.contains(':')) {
            final colonIndex = line.indexOf(':');
            final key = line.substring(0, colonIndex).trim();
            final value = line.substring(colonIndex + 1).trim();
            if (key.isNotEmpty) {
              responseHeaders.set(key, value);
            }
          }
        }
      }

      // Get response body as text and convert to bytes
      final responseText = xhr.responseText;
      final bodyBytes = Uint8List.fromList(utf8.encode(responseText));

      final fittorResponse = FittorResponse(
        statusCode: xhr.status,
        statusMessage: xhr.statusText,
        headers: responseHeaders,
        bodyBytes: bodyBytes,
        requestDuration: stopwatch.elapsed,
      );

      completer.complete(fittorResponse);
    } catch (e) {
      stopwatch.stop();
      completer.completeError(
        FittorNetworkException('Failed to process response: $e', e),
      );
    }
  }.toJS;

  // Handle error
  xhr.onerror = (web.Event event) {
    stopwatch.stop();
    final errorMessage =
        xhr.statusText.isNotEmpty ? xhr.statusText : 'Network error occurred';
    completer.completeError(
      FittorNetworkException('$errorMessage (${xhr.status})'),
    );
  }.toJS;

  // Handle timeout
  xhr.ontimeout = (web.Event event) {
    stopwatch.stop();
    completer.completeError(
      FittorTimeoutException('Request timeout', timeout),
    );
  }.toJS;

  // Handle abort
  xhr.onabort = (web.Event event) {
    stopwatch.stop();
    completer.completeError(
      const FittorNetworkException('Request was aborted'),
    );
  }.toJS;

  try {
    // Open the request
    xhr.open(request.methodName, request.uri.toString());

    // Set headers
    request.headers.toMap().forEach((key, value) {
      try {
        xhr.setRequestHeader(key, value);
      } catch (e) {
        // Some headers are restricted by browsers, silently ignore
      }
    });

    // Send the request
    if (request.body != null) {
      if (request.body is String) {
        xhr.send((request.body as String).toJS);
      } else {
        // Convert other body types to string for simplicity
        xhr.send(request.body.toString().toJS);
      }
    } else {
      xhr.send();
    }
  } catch (e) {
    stopwatch.stop();
    completer.completeError(
      FittorNetworkException('Failed to send request: $e', e),
    );
  }

  return completer.future;
}