send method

  1. @override
Future<StreamedResponse> send(
  1. BaseRequest request, {
  2. CancellationToken? cancellationToken,
})
override

Sends an HTTP request and asynchronously returns the response.

Implementation

@override
Future<StreamedResponse> send(
  BaseRequest request, {
  CancellationToken? cancellationToken,
}) async {
  if (cancellationToken?.isCancelled ?? false) {
    throw cancellationToken!.exception!;
  } else if (_isClosed) {
    throw ClientException(
        'HTTP request failed. Client is already closed.', request.url);
  }

  XMLHttpRequest? xhr = XMLHttpRequest();
  final completer = CancellableCompleter<StreamedResponse>.sync(
    cancellationToken,
    onCancel: () {
      _xhrs.remove(xhr);
      xhr.abort();
    },
  );

  unawaited(request.finalize().toBytes().then((bytes) async {
    // Don't continue if the request has been cancelled at this point
    if (cancellationToken?.isCancelled ?? false) return;

    // Prepare the request
    _xhrs.add(xhr);
    xhr
      ..open(request.method, '${request.url}', true)
      ..responseType = 'arraybuffer'
      ..withCredentials = withCredentials;
    for (var header in request.headers.entries) {
      xhr.setRequestHeader(header.key, header.value);
    }

    // Prepare the response handler
    unawaited(xhr.onLoad.first.then((_) {
      if (xhr.responseHeaders['content-length'] case final contentLengthHeader
          when contentLengthHeader != null &&
              !_digitRegex.hasMatch(contentLengthHeader)) {
        completer.completeError(ClientException(
          'Invalid content-length header [$contentLengthHeader].',
          request.url,
        ));
        return;
      }
      var body = (xhr.response as JSArrayBuffer).toDart.asUint8List();
      var responseUrl = xhr.responseURL;
      var url = responseUrl.isNotEmpty ? Uri.parse(responseUrl) : request.url;
      completer.complete(StreamedResponseV2(
        ByteStream.fromBytes(body),
        xhr.status,
        contentLength: body.length,
        request: request,
        url: url,
        headers: xhr.responseHeaders,
        reasonPhrase: xhr.statusText,
      ));
      _xhrs.remove(xhr);
    }));

    // Prepare the error handler
    unawaited(xhr.onError.first.then((_) {
      // Unfortunately, the underlying XMLHttpRequest API doesn't expose any
      // specific information about the error itself.
      completer.completeError(
        ClientException('XMLHttpRequest error.', request.url),
        StackTrace.current,
      );
      _xhrs.remove(xhr);
    }));

    // Send the request
    xhr.send(bytes.toJS);
  }));

  return completer.future;
}