From 96e7863cdd332267f21713d2492c1d2e9015b79f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Klaudio=20Ku=C3=A7aj?= Date: Fri, 20 Dec 2024 11:06:08 +0100 Subject: [PATCH] fix: Remove manual body encoding and remove transfer encoding when content length is set --- lib/src/body/body.dart | 67 +++++++++++++------------------ lib/src/message/response.dart | 5 ++- test/message/body_test.dart | 2 +- test/relic_server_serve_test.dart | 3 +- 4 files changed, 35 insertions(+), 42 deletions(-) diff --git a/lib/src/body/body.dart b/lib/src/body/body.dart index 728a99c..10d2076 100644 --- a/lib/src/body/body.dart +++ b/lib/src/body/body.dart @@ -3,7 +3,6 @@ import 'dart:convert'; import 'dart:io'; import 'dart:typed_data'; -import 'package:http_parser/http_parser.dart'; import 'package:relic/relic.dart'; import 'package:relic/src/body/types/mime_type.dart'; @@ -125,64 +124,54 @@ class Body { return stream; } - /// Applies the headers to the response and encodes the body if transfer encoding is chunked. - void applyHeadersAndEncodeBody( + /// Applies transfer encoding headers and content length to the response. + void applyHeaders( HttpResponse response, { TransferEncodingHeader? transferEncoding, }) { - // If the body is empty (contentLength == 0), explicitly set Content-Length to 0 - // and remove any Transfer-Encoding header since no encoding is needed. - if (contentLength == 0) { - response.headers.contentLength = 0; - response.headers.removeAll(Headers.transferEncodingHeader); - return; - } - - // Set the Content-Type header based on the MIME type of the body, if available. + // Set the Content-Type header based on the MIME type of the body. response.headers.contentType = _getContentType(); - // Retrieve the status code for further validation. - int statusCode = response.statusCode; + // If the content length is known, set it and remove the Transfer-Encoding header. + if (contentLength != null) { + response.headers + ..contentLength = contentLength! + ..removeAll(Headers.transferEncodingHeader); + return; + } // Determine if chunked encoding should be applied. - // Chunked encoding is enabled if: - // - The status code is in the 200 range but not 204 (No Content) or 304 (Not Modified). - // - The content length is unknown (contentLength == null). - // - The content type is not "multipart/byteranges" (excluded as per HTTP spec). - bool shouldEnableChunkedEncoding = statusCode >= 200 && - statusCode != 204 && - statusCode != 304 && + bool shouldEnableChunkedEncoding = response.statusCode >= 200 && + // 204 is no content + response.statusCode != 204 && + // 304 is not modified + response.statusCode != 304 && + // If the content length is not known, chunked encoding is applied. contentLength == null && - (contentType?.mimeType.isNotMultipartByteranges ?? false); + // If the content type is not multipart/byteranges, chunked encoding is applied. + (contentType?.mimeType.isNotMultipartByteranges ?? true); - // Check if chunked encoding is already enabled by inspecting the Transfer-Encoding header. + // Prepare transfer encodings. + var encodings = transferEncoding?.encodings ?? []; bool isChunked = transferEncoding?.isChunked ?? false; - var encodings = transferEncoding?.encodings ?? []; - // If chunked encoding should be enabled but is not already, update the Transfer-Encoding header. if (shouldEnableChunkedEncoding && !isChunked) { - // Add 'chunked' to the Transfer-Encoding header. encodings.add(TransferEncoding.chunked); - - // Apply chunked encoding to the response stream to encode the body in chunks. - _stream = chunkedCoding.encoder.bind(_stream!).cast(); - - // Mark the response as chunked for further processing. isChunked = true; } if (isChunked) { - // Remove any existing 'identity' transfer encoding, as it conflicts with 'chunked'. + // Remove conflicting 'identity' encoding if present. encodings.removeWhere((e) => e.name == TransferEncoding.identity.name); - // Set the Transfer-Encoding header with the updated encodings. - response.headers.set(Headers.transferEncodingHeader, encodings); - // If the response is already chunked, remove the Content-Length header. - // Chunked encoding does not require Content-Length because chunk sizes define the body length. - response.headers.removeAll(Headers.contentLengthHeader); + // Set Transfer-Encoding header and remove Content-Length as it is not needed for chunked encoding. + response.headers + ..set(Headers.transferEncodingHeader, + encodings.map((e) => e.name).toList()) + ..removeAll(Headers.contentLengthHeader); } else { - // If chunked encoding is not enabled, set the Content-Length header to the known body length. - response.headers.contentLength = contentLength ?? 0; + // Set Content-Length to 0 if chunked encoding is not enabled. + response.headers.contentLength = 0; } } diff --git a/lib/src/message/response.dart b/lib/src/message/response.dart index d731416..9049088 100644 --- a/lib/src/message/response.dart +++ b/lib/src/message/response.dart @@ -323,11 +323,14 @@ class Response extends Message { httpResponse.bufferOutput = context['relic_server.buffer_output'] as bool; } + // Set the status code. httpResponse.statusCode = statusCode; + // Apply all headers to the response. headers.applyHeaders(httpResponse); - body.applyHeadersAndEncodeBody( + // Apply transfer encoding headers and content length to the response. + body.applyHeaders( httpResponse, transferEncoding: headers.transferEncoding, ); diff --git a/test/message/body_test.dart b/test/message/body_test.dart index fbff5bf..50a4ce1 100644 --- a/test/message/body_test.dart +++ b/test/message/body_test.dart @@ -126,7 +126,7 @@ void main() { var response = await _get(); expect(response.headers['transfer-encoding'], contains('chunked')); - expect(response.body, equals('Relic')); + expect(response.body, equals('5\r\nRelic\r\n0\r\n\r\n')); }); test( diff --git a/test/relic_server_serve_test.dart b/test/relic_server_serve_test.dart index e112ebb..be8c325 100644 --- a/test/relic_server_serve_test.dart +++ b/test/relic_server_serve_test.dart @@ -499,7 +499,7 @@ void main() { response.headers, containsPair(HttpHeaders.transferEncodingHeader, 'chunked'), ); - expect(response.body, equals('hi')); + expect(response.body, equals('2\r\nhi\r\n0\r\n\r\n')); }); group('is not added when', () { @@ -508,6 +508,7 @@ void main() { return Response.ok( body: Body.fromDataStream( Stream.value(Uint8List.fromList([1, 2, 3, 4])), + contentLength: 4, ), ); });