···11# Changelog
2233+## Unreleased
44+55+- Sanitize CRLF sequences in outgoing HTTP response headers.
66+- Eliminate `string.lowercase` in a codebase: validate and lowercase header field names in a single pass, validate and lowercase important protocol header values (like `transfer-encoding`, `connection`, `upgrade` and more) at parse time.
77+- Include validation of trailer header field names and values during chunked body parsing.
88+39# v3.0.5 - 15.03.2026
410511- Remove all usage of `string.inspect` as it is an anti-pattern for logging.
+15-6
src/ewe/internal/encoder.gleam
···44import gleam/list
5566/// Encodes an HTTP response into bytes.
77-///
77+///
88pub fn encode_response(
99 response: response.Response(BitArray),
1010) -> bytes_tree.BytesTree {
···1515}
16161717/// Encodes the HTTP status line and headers of an HTTP response.
1818-///
1818+///
1919pub fn encode_response_partially(
2020 response: response.Response(a),
2121) -> bytes_tree.BytesTree {
···2525}
26262727/// Encodes the HTTP status line.
2828-///
2828+///
2929fn encode_status_line(status: Int) -> BitArray {
3030 let status_name = status_to_bit_array(status)
3131 let status = int.to_string(status)
···3434}
35353636/// Encodes HTTP headers into bytes.
3737-///
3737+///
3838fn encode_headers(headers: List(#(String, String))) -> BitArray {
3939 let headers =
4040 list.fold(headers, <<>>, fn(acc, headers) {
4141 let #(key, value) = headers
42424343- <<acc:bits, key:utf8, ": ", value:utf8, "\r\n">>
4343+ <<
4444+ acc:bits,
4545+ sanitize_header_value(key):utf8,
4646+ ": ",
4747+ sanitize_header_value(value):utf8,
4848+ "\r\n",
4949+ >>
4450 })
45514652 <<headers:bits, "\r\n">>
4753}
48545555+@external(erlang, "ewe_ffi", "sanitize_header_value")
5656+fn sanitize_header_value(value: String) -> String
5757+4958/// Maps HTTP status codes to their text descriptions.
5050-///
5959+///
5160fn status_to_bit_array(status: Int) -> BitArray {
5261 case status {
5362 100 -> <<"Continue">>