diff --git a/lib/widgets/sse_display.dart b/lib/widgets/sse_display.dart index 0d73871e..0d189073 100644 --- a/lib/widgets/sse_display.dart +++ b/lib/widgets/sse_display.dart @@ -27,7 +27,7 @@ class _SSEDisplayState extends State { return SingleChildScrollView( child: Column( crossAxisAlignment: CrossAxisAlignment.stretch, - children: sse.reversed.map((chunk) { + children: sse.reversed.where((e) => e != '').map((chunk) { Map? parsedJson; try { parsedJson = jsonDecode(chunk); diff --git a/packages/better_networking/lib/models/http_response_model.dart b/packages/better_networking/lib/models/http_response_model.dart index 683cab87..18c835ab 100644 --- a/packages/better_networking/lib/models/http_response_model.dart +++ b/packages/better_networking/lib/models/http_response_model.dart @@ -67,9 +67,13 @@ class HttpResponseModel with _$HttpResponseModel { HttpHeaders.contentLengthHeader: response.contentLength.toString(), }, response.headers); MediaType? mediaType = getMediaTypeFromHeaders(responseHeaders); - final body = (mediaType?.subtype == kSubTypeJson) - ? utf8.decode(response.bodyBytes) - : response.body; + + //TODO: Review Effectiveness + final body = decodeBytes( + response.bodyBytes, + response.headers['content-type']!, + ); + return HttpResponseModel( statusCode: response.statusCode, headers: responseHeaders, diff --git a/packages/better_networking/lib/services/http_service.dart b/packages/better_networking/lib/services/http_service.dart index 72520ce1..aa17b775 100644 --- a/packages/better_networking/lib/services/http_service.dart +++ b/packages/better_networking/lib/services/http_service.dart @@ -20,121 +20,132 @@ Future<(HttpResponse?, Duration?, String?)> sendHttpRequest( SupportedUriSchemes defaultUriScheme = kDefaultUriScheme, bool noSSL = false, }) async { - if (httpClientManager.wasRequestCancelled(requestId)) { - httpClientManager.removeCancelledRequest(requestId); - } - final client = httpClientManager.createClient(requestId, noSSL: noSSL); - - (Uri?, String?) uriRec = getValidRequestUri( - requestModel.url, - requestModel.enabledParams, + final stream = await streamHttpRequest( + requestId, + apiType, + requestModel, defaultUriScheme: defaultUriScheme, + noSSL: noSSL, ); + final output = await stream.last; - if (uriRec.$1 != null) { - Uri requestUrl = uriRec.$1!; - Map headers = requestModel.enabledHeadersMap; - bool overrideContentType = false; - HttpResponse? response; - String? body; - try { - Stopwatch stopwatch = Stopwatch()..start(); - if (apiType == APIType.rest) { - var isMultiPartRequest = - requestModel.bodyContentType == ContentType.formdata; + return (output?.$2, output?.$3, output?.$4); - if (kMethodsWithBody.contains(requestModel.method)) { - var requestBody = requestModel.body; - if (requestBody != null && - !isMultiPartRequest && - requestBody.isNotEmpty) { - body = requestBody; - if (requestModel.hasContentTypeHeader) { - overrideContentType = true; - } else { - headers[HttpHeaders.contentTypeHeader] = - requestModel.bodyContentType.header; - } - } - if (isMultiPartRequest) { - var multiPartRequest = http.MultipartRequest( - requestModel.method.name.toUpperCase(), - requestUrl, - ); - multiPartRequest.headers.addAll(headers); - for (var formData in requestModel.formDataList) { - if (formData.type == FormDataType.text) { - multiPartRequest.fields.addAll({formData.name: formData.value}); - } else { - multiPartRequest.files.add( - await http.MultipartFile.fromPath( - formData.name, - formData.value, - ), - ); - } - } - http.StreamedResponse multiPartResponse = await client.send( - multiPartRequest, - ); + // if (httpClientManager.wasRequestCancelled(requestId)) { + // httpClientManager.removeCancelledRequest(requestId); + // } + // final client = httpClientManager.createClient(requestId, noSSL: noSSL); - stopwatch.stop(); - http.Response convertedMultiPartResponse = - await convertStreamedResponse(multiPartResponse); - return (convertedMultiPartResponse, stopwatch.elapsed, null); - } - } - switch (requestModel.method) { - case HTTPVerb.get: - response = await client.get(requestUrl, headers: headers); - break; - case HTTPVerb.head: - response = await client.head(requestUrl, headers: headers); - break; - case HTTPVerb.post: - case HTTPVerb.put: - case HTTPVerb.patch: - case HTTPVerb.delete: - case HTTPVerb.options: - final request = prepareHttpRequest( - url: requestUrl, - method: requestModel.method.name.toUpperCase(), - headers: headers, - body: body, - overrideContentType: overrideContentType, - ); - final streamed = await client.send(request); - response = await http.Response.fromStream(streamed); - break; - } - } - if (apiType == APIType.graphql) { - var requestBody = getGraphQLBody(requestModel); - if (requestBody != null) { - var contentLength = utf8.encode(requestBody).length; - if (contentLength > 0) { - body = requestBody; - headers[HttpHeaders.contentLengthHeader] = contentLength.toString(); - if (!requestModel.hasContentTypeHeader) { - headers[HttpHeaders.contentTypeHeader] = ContentType.json.header; - } - } - } - response = await client.post(requestUrl, headers: headers, body: body); - } - stopwatch.stop(); - return (response, stopwatch.elapsed, null); - } catch (e) { - if (httpClientManager.wasRequestCancelled(requestId)) { - return (null, null, kMsgRequestCancelled); - } - return (null, null, e.toString()); - } finally { - httpClientManager.closeClient(requestId); - } - } else { - return (null, null, uriRec.$2); - } + // (Uri?, String?) uriRec = getValidRequestUri( + // requestModel.url, + // requestModel.enabledParams, + // defaultUriScheme: defaultUriScheme, + // ); + + // if (uriRec.$1 != null) { + // Uri requestUrl = uriRec.$1!; + // Map headers = requestModel.enabledHeadersMap; + // bool overrideContentType = false; + // HttpResponse? response; + // String? body; + // try { + // Stopwatch stopwatch = Stopwatch()..start(); + // if (apiType == APIType.rest) { + // var isMultiPartRequest = + // requestModel.bodyContentType == ContentType.formdata; + + // if (kMethodsWithBody.contains(requestModel.method)) { + // var requestBody = requestModel.body; + // if (requestBody != null && + // !isMultiPartRequest && + // requestBody.isNotEmpty) { + // body = requestBody; + // if (requestModel.hasContentTypeHeader) { + // overrideContentType = true; + // } else { + // headers[HttpHeaders.contentTypeHeader] = + // requestModel.bodyContentType.header; + // } + // } + // if (isMultiPartRequest) { + // var multiPartRequest = http.MultipartRequest( + // requestModel.method.name.toUpperCase(), + // requestUrl, + // ); + // multiPartRequest.headers.addAll(headers); + // for (var formData in requestModel.formDataList) { + // if (formData.type == FormDataType.text) { + // multiPartRequest.fields.addAll({formData.name: formData.value}); + // } else { + // multiPartRequest.files.add( + // await http.MultipartFile.fromPath( + // formData.name, + // formData.value, + // ), + // ); + // } + // } + // http.StreamedResponse multiPartResponse = await client.send( + // multiPartRequest, + // ); + + // stopwatch.stop(); + // http.Response convertedMultiPartResponse = + // await convertStreamedResponse(multiPartResponse); + // return (convertedMultiPartResponse, stopwatch.elapsed, null); + // } + // } + // switch (requestModel.method) { + // case HTTPVerb.get: + // response = await client.get(requestUrl, headers: headers); + // break; + // case HTTPVerb.head: + // response = await client.head(requestUrl, headers: headers); + // break; + // case HTTPVerb.post: + // case HTTPVerb.put: + // case HTTPVerb.patch: + // case HTTPVerb.delete: + // case HTTPVerb.options: + // final request = prepareHttpRequest( + // url: requestUrl, + // method: requestModel.method.name.toUpperCase(), + // headers: headers, + // body: body, + // overrideContentType: overrideContentType, + // ); + // final streamed = await client.send(request); + // response = await http.Response.fromStream(streamed); + // break; + // } + // } + // if (apiType == APIType.graphql) { + // var requestBody = getGraphQLBody(requestModel); + // if (requestBody != null) { + // var contentLength = utf8.encode(requestBody).length; + // if (contentLength > 0) { + // body = requestBody; + // headers[HttpHeaders.contentLengthHeader] = contentLength.toString(); + // if (!requestModel.hasContentTypeHeader) { + // headers[HttpHeaders.contentTypeHeader] = ContentType.json.header; + // } + // } + // } + // response = await client.post(requestUrl, headers: headers, body: body); + // } + // stopwatch.stop(); + // return (response, stopwatch.elapsed, null); + // } catch (e) { + // if (httpClientManager.wasRequestCancelled(requestId)) { + // return (null, null, kMsgRequestCancelled); + // } + // return (null, null, e.toString()); + // } finally { + // httpClientManager.closeClient(requestId); + // } + // } else { + // return (null, null, uriRec.$2); + // } } void cancelHttpRequest(String? requestId) { @@ -230,71 +241,13 @@ Future> streamHttpRequest( return controller.stream; } - final headers = requestModel.enabledHeadersMap; - final hasBody = kMethodsWithBody.contains(requestModel.method); - final isMultipart = requestModel.bodyContentType == ContentType.formdata; - - http.StreamedResponse streamedResponse; - try { - //----------------- Request Creation --------------------- - //Handling HTTP Multipart Requests - 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), - ); - } - } - streamedResponse = await client.send(multipart); - } else if (apiType == APIType.graphql) { - // Handling GraphQL Requests - var requestBody = getGraphQLBody(requestModel); - String? body; - if (requestBody != null) { - var contentLength = utf8.encode(requestBody).length; - if (contentLength > 0) { - body = requestBody; - headers[HttpHeaders.contentLengthHeader] = contentLength.toString(); - if (!requestModel.hasContentTypeHeader) { - headers[HttpHeaders.contentTypeHeader] = ContentType.json.header; - } - } - } - final request = http.Request('POST', uri) - ..headers.addAll(headers) - ..body = body ?? ''; - streamedResponse = await client.send(request); - } else { - //Handling regular REST Requests - 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, - ); - streamedResponse = await client.send(request); - } - + final streamedResponse = await makeStreamedRequest( + client: client, + uri: uri, + requestModel: requestModel, + apiType: apiType, + ); //----------------- Response Handling --------------------- HttpResponse getResponseFromBytes(List bytes) { @@ -347,10 +300,8 @@ Future> streamHttpRequest( //handle cases where response is larger than a TCP packet and cuts mid-way if (!hasEmitted && !controller.isClosed) { final response = getResponseFromBytes(buffer.toString().codeUnits); - if (response.body.trim().isNotEmpty) { - final isStreaming = kStreamingResponseTypes.contains(contentType); - controller.add((isStreaming, response, stopwatch.elapsed, null)); - } + final isStreaming = kStreamingResponseTypes.contains(contentType); + controller.add((isStreaming, response, stopwatch.elapsed, null)); } cleanup(); }, @@ -362,3 +313,75 @@ Future> streamHttpRequest( return controller.stream; } } + +Future makeStreamedRequest({ + required http.Client client, + required Uri uri, + required HttpRequestModel requestModel, + required APIType apiType, +}) async { + final headers = requestModel.enabledHeadersMap; + final hasBody = kMethodsWithBody.contains(requestModel.method); + final isMultipart = requestModel.bodyContentType == ContentType.formdata; + + http.StreamedResponse streamedResponse; + + //----------------- Request Creation --------------------- + //Handling HTTP Multipart Requests + if (requestModel == 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), + ); + } + } + streamedResponse = await client.send(multipart); + } else if (apiType == APIType.graphql) { + // Handling GraphQL Requests + var requestBody = getGraphQLBody(requestModel); + String? body; + if (requestBody != null) { + var contentLength = utf8.encode(requestBody).length; + if (contentLength > 0) { + body = requestBody; + headers[HttpHeaders.contentLengthHeader] = contentLength.toString(); + if (!requestModel.hasContentTypeHeader) { + headers[HttpHeaders.contentTypeHeader] = ContentType.json.header; + } + } + } + final request = http.Request('POST', uri) + ..headers.addAll(headers) + ..body = body ?? ''; + streamedResponse = await client.send(request); + } else { + //Handling regular REST Requests + 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, + ); + streamedResponse = await client.send(request); + } + return streamedResponse; +} diff --git a/test/models/http_response_models.dart b/test/models/http_response_models.dart index 5b04ce3b..a9dff3af 100644 --- a/test/models/http_response_models.dart +++ b/test/models/http_response_models.dart @@ -41,4 +41,5 @@ Map responseModelJson = { "formattedBody": formattedBody, "bodyBytes": bodyBytes, "time": 516000, + 'sseOutput': null, };