send method
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;
}
}