From 69cb33ea1de357e88e11632466de7a5282e7e249 Mon Sep 17 00:00:00 2001 From: Affan Shaikhsurab <51104750+AffanShaikhsurab@users.noreply.github.com> Date: Sun, 2 Mar 2025 23:18:17 +0530 Subject: [PATCH 1/4] Fix: Prevent automatic charset=utf-8 addition to Content-Type headers This change prevents the HTTP package from automatically appending 'charset=utf-8' to Content-Type headers by: Using low-level http.Request instead of high-level client methods Setting request.bodyBytes directly with UTF-8 encoded content Preserving exact Content-Type headers as specified by users This ensures users have full control over Content-Type headers sent to servers, addressing issues where some APIs reject requests with charset parameters. --- .../lib/services/http_service.dart | 42 ++++++++----------- 1 file changed, 18 insertions(+), 24 deletions(-) diff --git a/packages/apidash_core/lib/services/http_service.dart b/packages/apidash_core/lib/services/http_service.dart index ad06a21d4..a16d9f223 100644 --- a/packages/apidash_core/lib/services/http_service.dart +++ b/packages/apidash_core/lib/services/http_service.dart @@ -71,37 +71,31 @@ Future<(HttpResponse?, Duration?, String?)> sendHttpRequest( } } http.StreamedResponse multiPartResponse = - await multiPartRequest.send(); + 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: - response = - await client.post(requestUrl, headers: headers, body: body); - break; - case HTTPVerb.put: - response = - await client.put(requestUrl, headers: headers, body: body); - break; - case HTTPVerb.patch: - response = - await client.patch(requestUrl, headers: headers, body: body); - break; - case HTTPVerb.delete: - response = - await client.delete(requestUrl, headers: headers, body: body); - break; + + // Replace the switch statement with manual Request creation + final request = http.Request( + requestModel.method.name.toUpperCase(), + requestUrl, + ); + + // Set headers + request.headers.addAll(headers); + + // Set body if needed + if (body != null && kMethodsWithBody.contains(requestModel.method)) { + request.bodyBytes = utf8.encode(body); } + + // Use the client to send the request + final streamedResponse = await client.send(request); + response = await http.Response.fromStream(streamedResponse); } if (apiType == APIType.graphql) { var requestBody = getGraphQLBody(requestModel); From 6769c3cb15828afb15da93974b5e67f1558e33d1 Mon Sep 17 00:00:00 2001 From: Affan Shaikhsurab <51104750+AffanShaikhsurab@users.noreply.github.com> Date: Tue, 4 Mar 2025 13:04:32 +0530 Subject: [PATCH 2/4] feat: add comprehensive charset support for HTTP requests - Add support for multiple character encodings using charset package - Implement mapping for all IANA standard charsets to their Dart equivalents - Fix mismatch between Content-Type header charset and actual body encoding - Support UTF-8, UTF-16, UTF-32, ISO-8859 variants, Windows code pages, and Asian encodings - Provide appropriate fallbacks for less common encodings - Add proper content type detection and encoding for request bodies - Improve error handling for unsupported encodings --- .../lib/services/http_service.dart | 203 +++++++++++++++++- 1 file changed, 201 insertions(+), 2 deletions(-) diff --git a/packages/apidash_core/lib/services/http_service.dart b/packages/apidash_core/lib/services/http_service.dart index a16d9f223..fd1aa042e 100644 --- a/packages/apidash_core/lib/services/http_service.dart +++ b/packages/apidash_core/lib/services/http_service.dart @@ -1,17 +1,198 @@ import 'dart:async'; import 'dart:convert'; import 'dart:io'; +import 'dart:typed_data'; +import 'package:charset/charset.dart'; import 'package:http/http.dart' as http; import 'package:seed/seed.dart'; import '../consts.dart'; import '../models/models.dart'; import '../utils/utils.dart'; import 'http_client_manager.dart'; +import 'dart:collection'; typedef HttpResponse = http.Response; final httpClientManager = HttpClientManager(); +// Function to get encoding by name, supporting both built-in and charset package +Encoding getEncodingFromCharset(String? charsetName) { + if (charsetName == null) { + return utf8; + } + + // Normalize to lowercase for case-insensitive matching + final normalizedName = charsetName.toLowerCase().trim(); + + // Direct codec mappings - built-in encodings first + switch (normalizedName) { + // Built-in encodings + case 'utf-8': + case 'utf8': + case 'utf_8': + case 'csutf8': + return Utf8Codec(); + + case 'ascii': + case 'us-ascii': + case 'us': + case 'iso-ir-6': + case 'ansi_x3.4-1968': + case 'ansi_x3.4-1986': + case 'iso_646.irv:1991': + case 'iso646-us': + case 'ibm367': + case 'cp367': + case 'csascii': + return AsciiCodec(); + + case 'iso-8859-1': + case 'iso8859-1': + case 'iso_8859-1': + case 'latin1': + case 'iso_8859-1:1987': + case 'iso-ir-100': + case 'l1': + case 'ibm819': + case 'cp819': + case 'csisolatin1': + return Latin1Codec(); + + // UTF-16 variants + case 'utf-16': + case 'utf16': + case 'utf_16': + return Utf16Codec(); + + case 'utf-16be': + case 'utf16be': + case 'utf_16be': + return const Utf16Codec(); + + case 'utf-16le': + case 'utf16le': + case 'utf_16le': + return const Utf16Codec(); + + // UTF-32 variants + case 'utf-32': + case 'utf32': + case 'utf_32': + return utf32; + + // Japanese encodings + case 'shift_jis': + case 'shift-jis': + case 'sjis': + return shiftJis; + + case 'euc-jp': + case 'euc_jp': + return eucJp; + + // Korean encoding + case 'euc-kr': + case 'euc_kr': + return eucKr; + + // Chinese encoding + case 'gbk': + case 'gb2312': + return gbk; + + // Windows code pages + case 'windows-1250': + case 'cp1250': + return windows1250; + + case 'windows-1251': + case 'cp1251': + return windows1251; + + case 'windows-1252': + case 'cp1252': + return windows1252; + + case 'windows-1253': + case 'cp1253': + return windows1253; + + // More Windows code pages and ISO encodings + // For other ISO Latin encodings, use a more generic approach + case 'iso-8859-2': + case 'latin2': + return latin2; + + case 'iso-8859-3': + case 'latin3': + return latin3; + + case 'iso-8859-4': + case 'latin4': + return latin4; + + case 'iso-8859-5': + case 'latin-cyrillic': + // Return a fallback if latinCyrillic is not available + return windows1251; // Cyrillic encoding as fallback + + case 'iso-8859-6': + case 'latin-arabic': + // Return a fallback if latinArabic is not available + return windows1256; // Arabic encoding as fallback + + case 'iso-8859-7': + case 'latin-greek': + // Return a fallback if latinGreek is not available + return windows1253; // Greek encoding as fallback + + case 'iso-8859-8': + case 'latin-hebrew': + // Return a fallback if latinHebrew is not available + return windows1255; // Hebrew encoding as fallback + + case 'iso-8859-9': + case 'latin5': + // Return a fallback if latin5 is not available + return windows1254; // Turkish encoding as fallback + + case 'iso-8859-10': + case 'latin6': + // Return a fallback (no direct equivalent) + return utf8; + + case 'iso-8859-11': + case 'latin-thai': + // Return a fallback if latinThai is not available + return windows874; // Thai encoding as fallback + + case 'iso-8859-13': + case 'latin7': + // Return a fallback if latin7 is not available + return windows1257; // Baltic encoding as fallback + + case 'iso-8859-14': + case 'latin8': + // Return a fallback (no direct equivalent) + return latin1; + + case 'iso-8859-15': + case 'latin9': + // Return a fallback if latin9 is not available + return latin1; // Very similar to Latin-1 + + case 'iso-8859-16': + case 'latin10': + // Return a fallback if latin10 is not available + return utf8; + + // Default to UTF-8 for anything else + default: + print("Warning: Unsupported charset: $normalizedName, using UTF-8 as fallback"); + return utf8; + } +} + Future<(HttpResponse?, Duration?, String?)> sendHttpRequest( String requestId, APIType apiType, @@ -84,13 +265,31 @@ Future<(HttpResponse?, Duration?, String?)> sendHttpRequest( requestModel.method.name.toUpperCase(), requestUrl, ); - // Set headers request.headers.addAll(headers); // Set body if needed if (body != null && kMethodsWithBody.contains(requestModel.method)) { - request.bodyBytes = utf8.encode(body); + // Extract charset from Content-Type header if present + Encoding encoding = utf8; // Default to UTF-8 + + final contentTypeHeader = request.headers[HttpHeaders.contentTypeHeader]; + if (contentTypeHeader != null) { + final charsetMatch = RegExp(r'charset=([^\s;]+)', caseSensitive: false) + .firstMatch(contentTypeHeader); + + if (charsetMatch != null) { + final charsetName = charsetMatch.group(1); + + if (charsetName != null) { + // Get the encoding object using our helper function + encoding = getEncodingFromCharset(charsetName); + } + } + } + + // Encode the body using the determined encoding + request.bodyBytes = encoding.encode(body); } // Use the client to send the request From 147bc5597903db16740f740c416a4e86d677ce24 Mon Sep 17 00:00:00 2001 From: Affan Shaikhsurab <51104750+AffanShaikhsurab@users.noreply.github.com> Date: Wed, 5 Mar 2025 12:48:09 +0530 Subject: [PATCH 3/4] Improved http_service.dart --- .../lib/services/http_service.dart | 406 ++++++------------ 1 file changed, 135 insertions(+), 271 deletions(-) diff --git a/packages/apidash_core/lib/services/http_service.dart b/packages/apidash_core/lib/services/http_service.dart index fd1aa042e..ce636cc1c 100644 --- a/packages/apidash_core/lib/services/http_service.dart +++ b/packages/apidash_core/lib/services/http_service.dart @@ -2,197 +2,78 @@ import 'dart:async'; import 'dart:convert'; import 'dart:io'; import 'dart:typed_data'; -import 'package:charset/charset.dart'; +import 'package:charset/charset.dart'; // External package for additional encodings import 'package:http/http.dart' as http; -import 'package:seed/seed.dart'; import '../consts.dart'; import '../models/models.dart'; import '../utils/utils.dart'; import 'http_client_manager.dart'; -import 'dart:collection'; +// Alias for convenience typedef HttpResponse = http.Response; final httpClientManager = HttpClientManager(); -// Function to get encoding by name, supporting both built-in and charset package +// Cache for storing detected encodings +final Map _encodingCache = {}; + +/// Function to get encoding by charset name, supporting built-in and additional encodings Encoding getEncodingFromCharset(String? charsetName) { - if (charsetName == null) { - return utf8; - } - - // Normalize to lowercase for case-insensitive matching + if (charsetName == null) return utf8; // Default encoding + final normalizedName = charsetName.toLowerCase().trim(); - - // Direct codec mappings - built-in encodings first + + // Check cache first to avoid redundant lookups + if (_encodingCache.containsKey(normalizedName)) { + return _encodingCache[normalizedName]!; + } + + // Built-in Dart encodings + Encoding? encoding; switch (normalizedName) { - // Built-in encodings case 'utf-8': case 'utf8': - case 'utf_8': - case 'csutf8': - return Utf8Codec(); - + encoding = utf8; + break; + case 'ascii': - case 'us-ascii': - case 'us': - case 'iso-ir-6': - case 'ansi_x3.4-1968': - case 'ansi_x3.4-1986': - case 'iso_646.irv:1991': - case 'iso646-us': - case 'ibm367': - case 'cp367': - case 'csascii': - return AsciiCodec(); - + encoding = ascii; + break; + case 'iso-8859-1': - case 'iso8859-1': - case 'iso_8859-1': case 'latin1': - case 'iso_8859-1:1987': - case 'iso-ir-100': - case 'l1': - case 'ibm819': - case 'cp819': - case 'csisolatin1': - return Latin1Codec(); - - // UTF-16 variants + encoding = latin1; + break; + case 'utf-16': - case 'utf16': - case 'utf_16': - return Utf16Codec(); - case 'utf-16be': - case 'utf16be': - case 'utf_16be': - return const Utf16Codec(); - + encoding = Utf16Codec(bigEndian: true); + break; + case 'utf-16le': - case 'utf16le': - case 'utf_16le': - return const Utf16Codec(); - - // UTF-32 variants + encoding = Utf16Codec(bigEndian: false); + break; + case 'utf-32': - case 'utf32': - case 'utf_32': - return utf32; - - // Japanese encodings - case 'shift_jis': - case 'shift-jis': - case 'sjis': - return shiftJis; - - case 'euc-jp': - case 'euc_jp': - return eucJp; - - // Korean encoding - case 'euc-kr': - case 'euc_kr': - return eucKr; - - // Chinese encoding - case 'gbk': - case 'gb2312': - return gbk; - - // Windows code pages - case 'windows-1250': - case 'cp1250': - return windows1250; - - case 'windows-1251': - case 'cp1251': - return windows1251; - - case 'windows-1252': - case 'cp1252': - return windows1252; - - case 'windows-1253': - case 'cp1253': - return windows1253; - - // More Windows code pages and ISO encodings - // For other ISO Latin encodings, use a more generic approach - case 'iso-8859-2': - case 'latin2': - return latin2; - - case 'iso-8859-3': - case 'latin3': - return latin3; - - case 'iso-8859-4': - case 'latin4': - return latin4; - - case 'iso-8859-5': - case 'latin-cyrillic': - // Return a fallback if latinCyrillic is not available - return windows1251; // Cyrillic encoding as fallback - - case 'iso-8859-6': - case 'latin-arabic': - // Return a fallback if latinArabic is not available - return windows1256; // Arabic encoding as fallback - - case 'iso-8859-7': - case 'latin-greek': - // Return a fallback if latinGreek is not available - return windows1253; // Greek encoding as fallback - - case 'iso-8859-8': - case 'latin-hebrew': - // Return a fallback if latinHebrew is not available - return windows1255; // Hebrew encoding as fallback - - case 'iso-8859-9': - case 'latin5': - // Return a fallback if latin5 is not available - return windows1254; // Turkish encoding as fallback - - case 'iso-8859-10': - case 'latin6': - // Return a fallback (no direct equivalent) - return utf8; - - case 'iso-8859-11': - case 'latin-thai': - // Return a fallback if latinThai is not available - return windows874; // Thai encoding as fallback - - case 'iso-8859-13': - case 'latin7': - // Return a fallback if latin7 is not available - return windows1257; // Baltic encoding as fallback - - case 'iso-8859-14': - case 'latin8': - // Return a fallback (no direct equivalent) - return latin1; - - case 'iso-8859-15': - case 'latin9': - // Return a fallback if latin9 is not available - return latin1; // Very similar to Latin-1 - - case 'iso-8859-16': - case 'latin10': - // Return a fallback if latin10 is not available - return utf8; - - // Default to UTF-8 for anything else + encoding = utf32; + break; + default: - print("Warning: Unsupported charset: $normalizedName, using UTF-8 as fallback"); - return utf8; + // Attempt to retrieve encoding from `charset` package + try { + encoding = charset[normalizedName] ?? utf8; // Fallback to UTF-8 + } catch (e) { + print("Warning: Unsupported charset: $normalizedName, using UTF-8 as fallback"); + encoding = utf8; + } } + + // Cache result for future lookups + _encodingCache[normalizedName] = encoding!; + return encoding; } +/// Sends an HTTP request with proper encoding handling Future<(HttpResponse?, Duration?, String?)> sendHttpRequest( String requestId, APIType apiType, @@ -208,127 +89,110 @@ Future<(HttpResponse?, Duration?, String?)> sendHttpRequest( defaultUriScheme: defaultUriScheme, ); - if (uriRec.$1 != null) { - Uri requestUrl = uriRec.$1!; - Map headers = requestModel.enabledHeadersMap; - 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) { - var contentLength = utf8.encode(requestBody).length; - if (contentLength > 0) { - body = requestBody; - headers[HttpHeaders.contentLengthHeader] = - contentLength.toString(); - if (!requestModel.hasContentTypeHeader) { - 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, - ), - ); - } + if (uriRec.$1 == null) { + return (null, null, uriRec.$2); // Invalid URL case + } + + Uri requestUrl = uriRec.$1!; + Map headers = requestModel.enabledHeadersMap; + HttpResponse? response; + String? body; + + try { + Stopwatch stopwatch = Stopwatch()..start(); + + if (apiType == APIType.rest) { + bool isMultiPartRequest = requestModel.bodyContentType == ContentType.formdata; + + if (kMethodsWithBody.contains(requestModel.method)) { + var requestBody = requestModel.body; + if (requestBody != null && !isMultiPartRequest) { + var contentLength = utf8.encode(requestBody).length; + if (contentLength > 0) { + body = requestBody; + headers[HttpHeaders.contentLengthHeader] = contentLength.toString(); + if (!requestModel.hasContentTypeHeader) { + headers[HttpHeaders.contentTypeHeader] = requestModel.bodyContentType.header; } - http.StreamedResponse multiPartResponse = - await client.send(multiPartRequest); - stopwatch.stop(); - http.Response convertedMultiPartResponse = - await convertStreamedResponse(multiPartResponse); - return (convertedMultiPartResponse, stopwatch.elapsed, null); } } - - // Replace the switch statement with manual Request creation - final request = http.Request( - requestModel.method.name.toUpperCase(), - requestUrl, - ); - // Set headers - request.headers.addAll(headers); - - // Set body if needed - if (body != null && kMethodsWithBody.contains(requestModel.method)) { - // Extract charset from Content-Type header if present - Encoding encoding = utf8; // Default to UTF-8 - - final contentTypeHeader = request.headers[HttpHeaders.contentTypeHeader]; - if (contentTypeHeader != null) { - final charsetMatch = RegExp(r'charset=([^\s;]+)', caseSensitive: false) - .firstMatch(contentTypeHeader); - - if (charsetMatch != null) { - final charsetName = charsetMatch.group(1); - - if (charsetName != null) { - // Get the encoding object using our helper function - encoding = getEncodingFromCharset(charsetName); - } + + 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), + ); } } - - // Encode the body using the determined encoding - request.bodyBytes = encoding.encode(body); + + http.StreamedResponse multiPartResponse = await client.send(multiPartRequest); + stopwatch.stop(); + HttpResponse convertedMultiPartResponse = await http.Response.fromStream(multiPartResponse); + return (convertedMultiPartResponse, stopwatch.elapsed, null); } - - // Use the client to send the request - final streamedResponse = await client.send(request); - response = await http.Response.fromStream(streamedResponse); } - 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; - } + + // Prepare request + final request = http.Request(requestModel.method.name.toUpperCase(), requestUrl); + request.headers.addAll(headers); + + // Set body if applicable + if (body != null && kMethodsWithBody.contains(requestModel.method)) { + Encoding encoding = utf8; // Default encoding + + final contentTypeHeader = request.headers[HttpHeaders.contentTypeHeader]; + if (contentTypeHeader != null) { + final charsetMatch = RegExp(r'charset=([^\s;]+)', caseSensitive: false).firstMatch(contentTypeHeader); + if (charsetMatch != null) { + encoding = getEncodingFromCharset(charsetMatch.group(1)); } } - response = await client.post( - requestUrl, - headers: headers, - body: body, - ); + + request.bodyBytes = encoding.encode(body); } - stopwatch.stop(); - return (response, stopwatch.elapsed, null); - } catch (e) { - if (httpClientManager.wasRequestCancelled(requestId)) { - return (null, null, kMsgRequestCancelled); + + // Send request + final streamedResponse = await client.send(request); + response = await http.Response.fromStream(streamedResponse); + } + + 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; + } + } } - return (null, null, e.toString()); - } finally { - httpClientManager.closeClient(requestId); + 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); } - } else { - return (null, null, uriRec.$2); + return (null, null, e.toString()); + } finally { + httpClientManager.closeClient(requestId); } } +/// Cancels an HTTP request by ID void cancelHttpRequest(String? requestId) { httpClientManager.cancelRequest(requestId); } From 81d5f03d6ceea19c1fe27afb4bff50430ec4da60 Mon Sep 17 00:00:00 2001 From: Affan Shaikhsurab <51104750+AffanShaikhsurab@users.noreply.github.com> Date: Thu, 6 Mar 2025 12:06:13 +0530 Subject: [PATCH 4/4] Update http_service.dart --- .../lib/services/http_service.dart | 33 ++++++++++++------- 1 file changed, 22 insertions(+), 11 deletions(-) diff --git a/packages/apidash_core/lib/services/http_service.dart b/packages/apidash_core/lib/services/http_service.dart index ce636cc1c..616b81799 100644 --- a/packages/apidash_core/lib/services/http_service.dart +++ b/packages/apidash_core/lib/services/http_service.dart @@ -1,9 +1,9 @@ import 'dart:async'; import 'dart:convert'; import 'dart:io'; -import 'dart:typed_data'; import 'package:charset/charset.dart'; // External package for additional encodings import 'package:http/http.dart' as http; +import 'package:seed/consts.dart'; import '../consts.dart'; import '../models/models.dart'; import '../utils/utils.dart'; @@ -19,7 +19,7 @@ final Map _encodingCache = {}; /// Function to get encoding by charset name, supporting built-in and additional encodings Encoding getEncodingFromCharset(String? charsetName) { - if (charsetName == null) return utf8; // Default encoding + if (charsetName == null) return utf8; // Default encoding final normalizedName = charsetName.toLowerCase().trim(); @@ -47,11 +47,8 @@ Encoding getEncodingFromCharset(String? charsetName) { case 'utf-16': case 'utf-16be': - encoding = Utf16Codec(bigEndian: true); - break; - case 'utf-16le': - encoding = Utf16Codec(bigEndian: false); + encoding = Utf16Codec(); break; case 'utf-32': @@ -61,7 +58,7 @@ Encoding getEncodingFromCharset(String? charsetName) { default: // Attempt to retrieve encoding from `charset` package try { - encoding = charset[normalizedName] ?? utf8; // Fallback to UTF-8 + encoding = Charset.getByName(normalizedName) ?? utf8; // Fallback to UTF-8 } catch (e) { print("Warning: Unsupported charset: $normalizedName, using UTF-8 as fallback"); encoding = utf8; @@ -69,7 +66,8 @@ Encoding getEncodingFromCharset(String? charsetName) { } // Cache result for future lookups - _encodingCache[normalizedName] = encoding!; + _encodingCache[normalizedName] = encoding; + print(encoding.name); return encoding; } @@ -148,15 +146,28 @@ Future<(HttpResponse?, Duration?, String?)> sendHttpRequest( // Set body if applicable if (body != null && kMethodsWithBody.contains(requestModel.method)) { Encoding encoding = utf8; // Default encoding - + final contentTypeHeader = request.headers[HttpHeaders.contentTypeHeader]; if (contentTypeHeader != null) { + // First check for explicit charset parameter final charsetMatch = RegExp(r'charset=([^\s;]+)', caseSensitive: false).firstMatch(contentTypeHeader); if (charsetMatch != null) { - encoding = getEncodingFromCharset(charsetMatch.group(1)); + final charsetName = charsetMatch.group(1); + encoding = getEncodingFromCharset(charsetName); + } else { + // No explicit charset, check content type + final contentTypeBase = contentTypeHeader.split(';')[0].trim().toLowerCase(); + + // For text-based content types, ensure we apply proper encoding + if (contentTypeBase.startsWith('text/') || + contentTypeBase == 'application/json' || + contentTypeBase == 'application/xml' || + contentTypeBase == 'application/javascript') { + // Keep default UTF-8 encoding for text-based content + } } } - + request.bodyBytes = encoding.encode(body); }