mirror of
https://github.com/foss42/apidash.git
synced 2025-12-03 03:17:00 +08:00
better_networking: streaming implementation (streamHttpRequest)
This commit is contained in:
@@ -74,8 +74,9 @@ Future<(HttpResponse?, Duration?, String?)> sendHttpRequest(
|
|||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
http.StreamedResponse multiPartResponse =
|
http.StreamedResponse multiPartResponse = await client.send(
|
||||||
await client.send(multiPartRequest);
|
multiPartRequest,
|
||||||
|
);
|
||||||
|
|
||||||
stopwatch.stop();
|
stopwatch.stop();
|
||||||
http.Response convertedMultiPartResponse =
|
http.Response convertedMultiPartResponse =
|
||||||
@@ -119,11 +120,7 @@ Future<(HttpResponse?, Duration?, String?)> sendHttpRequest(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
response = await client.post(
|
response = await client.post(requestUrl, headers: headers, body: body);
|
||||||
requestUrl,
|
|
||||||
headers: headers,
|
|
||||||
body: body,
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
stopwatch.stop();
|
stopwatch.stop();
|
||||||
return (response, stopwatch.elapsed, null);
|
return (response, stopwatch.elapsed, null);
|
||||||
@@ -153,17 +150,145 @@ http.Request prepareHttpRequest({
|
|||||||
}) {
|
}) {
|
||||||
var request = http.Request(method, url);
|
var request = http.Request(method, url);
|
||||||
if (headers.getValueContentType() != null) {
|
if (headers.getValueContentType() != null) {
|
||||||
request.headers[HttpHeaders.contentTypeHeader] =
|
request.headers[HttpHeaders.contentTypeHeader] = headers
|
||||||
headers.getValueContentType()!;
|
.getValueContentType()!;
|
||||||
if (!overrideContentType) {
|
if (!overrideContentType) {
|
||||||
headers.removeKeyContentType();
|
headers.removeKeyContentType();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (body != null) {
|
if (body != null) {
|
||||||
request.body = body;
|
request.body = body;
|
||||||
headers[HttpHeaders.contentLengthHeader] =
|
headers[HttpHeaders.contentLengthHeader] = request.bodyBytes.length
|
||||||
request.bodyBytes.length.toString();
|
.toString();
|
||||||
}
|
}
|
||||||
request.headers.addAll(headers);
|
request.headers.addAll(headers);
|
||||||
return request;
|
return request;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Future<Stream<(String?, Duration?, String?)?>> streamHttpRequest(
|
||||||
|
String requestId,
|
||||||
|
APIType apiType,
|
||||||
|
HttpRequestModel requestModel, {
|
||||||
|
SupportedUriSchemes defaultUriScheme = kDefaultUriScheme,
|
||||||
|
bool noSSL = false,
|
||||||
|
}) async {
|
||||||
|
final controller = StreamController<(String?, Duration?, String?)?>();
|
||||||
|
StreamSubscription<String?>? subscription;
|
||||||
|
final stopwatch = Stopwatch()..start();
|
||||||
|
|
||||||
|
_cleanup() async {
|
||||||
|
stopwatch.stop();
|
||||||
|
httpClientManager.closeClient(requestId);
|
||||||
|
await Future.microtask(() {});
|
||||||
|
controller.close();
|
||||||
|
}
|
||||||
|
|
||||||
|
Future<void> _handleError(dynamic error) async {
|
||||||
|
await Future.microtask(() {});
|
||||||
|
if (httpClientManager.wasRequestCancelled(requestId)) {
|
||||||
|
controller.add((null, null, kMsgRequestCancelled));
|
||||||
|
httpClientManager.removeCancelledRequest(requestId);
|
||||||
|
} else {
|
||||||
|
controller.add((null, null, error.toString()));
|
||||||
|
}
|
||||||
|
await _cleanup();
|
||||||
|
}
|
||||||
|
|
||||||
|
controller.onCancel = () async {
|
||||||
|
await subscription?.cancel();
|
||||||
|
httpClientManager.cancelRequest(requestId);
|
||||||
|
};
|
||||||
|
|
||||||
|
if (httpClientManager.wasRequestCancelled(requestId)) {
|
||||||
|
controller.add((null, null, kMsgRequestCancelled));
|
||||||
|
httpClientManager.removeCancelledRequest(requestId);
|
||||||
|
controller.close();
|
||||||
|
return controller.stream;
|
||||||
|
}
|
||||||
|
|
||||||
|
final client = httpClientManager.createClient(requestId, noSSL: noSSL);
|
||||||
|
final (uri, uriError) = getValidRequestUri(
|
||||||
|
requestModel.url,
|
||||||
|
requestModel.enabledParams,
|
||||||
|
defaultUriScheme: defaultUriScheme,
|
||||||
|
);
|
||||||
|
|
||||||
|
if (uri == null) {
|
||||||
|
await _handleError(uriError ?? 'Invalid URL');
|
||||||
|
return controller.stream;
|
||||||
|
}
|
||||||
|
|
||||||
|
final headers = requestModel.enabledHeadersMap;
|
||||||
|
final hasBody = kMethodsWithBody.contains(requestModel.method);
|
||||||
|
final isMultipart = requestModel.bodyContentType == ContentType.formdata;
|
||||||
|
|
||||||
|
try {
|
||||||
|
//HANDLE MULTI-PART
|
||||||
|
if (apiType == APIType.rest && isMultipart && hasBody) {
|
||||||
|
final multipart = http.MultipartRequest(
|
||||||
|
requestModel.method.name.toUpperCase(),
|
||||||
|
uri,
|
||||||
|
)..headers.addAll(headers);
|
||||||
|
|
||||||
|
for (final data in requestModel.formDataList) {
|
||||||
|
if (data.type == FormDataType.text) {
|
||||||
|
multipart.fields[data.name] = data.value;
|
||||||
|
} else {
|
||||||
|
multipart.files.add(
|
||||||
|
await http.MultipartFile.fromPath(data.name, data.value),
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final streamedResponse = await client.send(multipart);
|
||||||
|
final stream = streamTextResponse(streamedResponse);
|
||||||
|
|
||||||
|
subscription = stream.listen(
|
||||||
|
(data) => controller.add((data, stopwatch.elapsed, null)),
|
||||||
|
onDone: () => _cleanup(),
|
||||||
|
onError: _handleError,
|
||||||
|
);
|
||||||
|
|
||||||
|
return controller.stream;
|
||||||
|
}
|
||||||
|
|
||||||
|
String? body;
|
||||||
|
bool overrideContentType = false;
|
||||||
|
|
||||||
|
if (hasBody && requestModel.body?.isNotEmpty == true) {
|
||||||
|
body = requestModel.body;
|
||||||
|
if (!requestModel.hasContentTypeHeader) {
|
||||||
|
headers[HttpHeaders.contentTypeHeader] =
|
||||||
|
requestModel.bodyContentType.header;
|
||||||
|
} else {
|
||||||
|
overrideContentType = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
final request = prepareHttpRequest(
|
||||||
|
url: uri,
|
||||||
|
method: requestModel.method.name.toUpperCase(),
|
||||||
|
headers: headers,
|
||||||
|
body: body,
|
||||||
|
overrideContentType: overrideContentType,
|
||||||
|
);
|
||||||
|
|
||||||
|
final streamedResponse = await client.send(request);
|
||||||
|
final stream = streamTextResponse(streamedResponse);
|
||||||
|
|
||||||
|
subscription = stream.listen(
|
||||||
|
(data) {
|
||||||
|
if (!controller.isClosed) {
|
||||||
|
controller.add((data, stopwatch.elapsed, null));
|
||||||
|
}
|
||||||
|
},
|
||||||
|
onDone: () => _cleanup(),
|
||||||
|
onError: _handleError,
|
||||||
|
);
|
||||||
|
|
||||||
|
return controller.stream;
|
||||||
|
} catch (e) {
|
||||||
|
await _handleError(e);
|
||||||
|
return controller.stream;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -50,3 +50,20 @@ Future<http.Response> convertStreamedResponse(
|
|||||||
|
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Stream<String?> streamTextResponse(
|
||||||
|
http.StreamedResponse streamedResponse,
|
||||||
|
) async* {
|
||||||
|
try {
|
||||||
|
if (streamedResponse.statusCode != 200) {
|
||||||
|
final errorText = await streamedResponse.stream.bytesToString();
|
||||||
|
throw Exception('${streamedResponse.statusCode}\n$errorText');
|
||||||
|
}
|
||||||
|
final utf8Stream = streamedResponse.stream.transform(utf8.decoder);
|
||||||
|
await for (final chunk in utf8Stream) {
|
||||||
|
yield chunk;
|
||||||
|
}
|
||||||
|
} catch (e) {
|
||||||
|
rethrow;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user