send method

  1. @override
Future<StreamedResponse> send(
  1. BaseRequest request
)

Sends an HTTP request and asynchronously returns the response.

Implementers should call BaseRequest.finalize to get the body of the request as a ByteStream. They shouldn't make any assumptions about the state of the stream; it could have data written to it asynchronously at a later point, or it could already be closed when it's returned. Any internal HTTP errors should be wrapped as ClientExceptions.

Implementation

@override
Future<StreamedResponse> send(BaseRequest request) async {
  var id = _id++;
  dynamic requestBody = '<unknown>';
  if (request.method.toUpperCase() == 'GET') {
    requestBody = '';
  } else if (request.headers['content-type']?.contains('json') ?? false) {
    var bodyBytes = await request.finalize().toBytes();
    var bodyString = requestBody = utf8.decode(bodyBytes);
    if (bodyString.isNotEmpty) {
      requestBody = jsonDecode(bodyString);
    }
    // Recreate the request since the body was read
    request = Request(request.method, request.url)
      ..headers.addAll(request.headers)
      ..bodyBytes = bodyBytes;
  }

  for (var devbar in _instances) {
    devbar.maybeNetwork?.request(
      id,
      apiName: apiName,
      method: request.method,
      body: requestBody,
      path: request.url.toString(),
      parameters: {
        ...request.url.queryParameters,
        for (var header in request.headers.entries)
          if (header.key == 'Authorization')
            header.key: _ellipsisCenter(header.value, maxLength: 15)
          else
            header.key: header.value,
      },
    );
  }
  try {
    var response = await inner.send(request);
    dynamic body = '<unknown>';
    if (response.headers['content-type']?.contains('json') ?? false) {
      var responseBytes = await response.stream.toBytes();
      if (responseBytes.isNotEmpty) {
        body = Utf8Decoder().fuse(JsonDecoder()).convert(responseBytes);
      }
      // Recreate the response since the body was read
      response = StreamedResponse(
        ByteStream.fromBytes(responseBytes),
        response.statusCode,
        contentLength: response.contentLength,
        request: response.request,
        headers: response.headers,
        isRedirect: response.isRedirect,
        persistentConnection: response.persistentConnection,
        reasonPhrase: response.reasonPhrase,
      );
    }

    for (var devbar in _instances) {
      if (response.statusCode < 300) {
        devbar.maybeNetwork?.response(id, body: body);
      } else {
        devbar.maybeNetwork?.responseError(
          id,
          code: response.statusCode,
          reason: response.reasonPhrase ?? '',
          message: '$body',
        );
      }
    }

    recordAppEvent(
      AppEvent.request(
        method: request.method,
        url: request.url.toString(),
        status: response.statusCode,
        body: body == '<unknown>' ? null : jsonEncode(body),
      ),
      source: devbarHttpClientSource,
    );

    return response;
  } catch (e) {
    for (var devbar in _instances) {
      devbar.maybeNetwork?.responseError(
        id,
        code: -1,
        reason: '${e.runtimeType}',
        message: '$e',
      );
    }

    // No status: the exchange never got one. `error` still has to be set,
    // which is what the explicit constructor is for — `.request` infers it
    // from a status code that does not exist here.
    recordAppEvent(
      AppEvent.custom(
        channel: AppChannel.network,
        title: '${request.method} ${request.url}',
        detail: '${e.runtimeType}',
        body: '$e',
        error: true,
      ),
      source: devbarHttpClientSource,
    );

    rethrow;
  }
}