From 1640ef93f3adc090d6f0faad43984e9722004b33 Mon Sep 17 00:00:00 2001 From: preciz Date: Sun, 14 Jun 2026 21:33:42 +0200 Subject: [PATCH 1/2] Optimize cookie parsing Use length tracking and binary_part/3 instead of repeated binary concatenation to avoid allocating new binaries during decoding. --- lib/plug/conn/cookies.ex | 51 ++++++++++++++++++++-------------------- 1 file changed, 26 insertions(+), 25 deletions(-) diff --git a/lib/plug/conn/cookies.ex b/lib/plug/conn/cookies.ex index 12257f16..ceaf2bcc 100644 --- a/lib/plug/conn/cookies.ex +++ b/lib/plug/conn/cookies.ex @@ -49,40 +49,41 @@ defmodule Plug.Conn.Cookies do defp decode_kv("", acc), do: acc defp decode_kv(<>, acc) when h in [?\s, ?\t], do: decode_kv(t, acc) - defp decode_kv(kv, acc) when is_binary(kv), do: decode_key(kv, "", acc) + defp decode_kv(kv, acc) when is_binary(kv), do: decode_key(kv, kv, 0, acc) - defp decode_key(<>, _key, acc) when h in [?\s, ?\t, ?\r, ?\n, ?\v, ?\f], + defp decode_key(<>, _key_rest, _len, acc) when h in [?\s, ?\t, ?\r, ?\n, ?\v, ?\f], do: skip_until_cc(t, acc) - defp decode_key(<>, _key, acc), do: decode_kv(t, acc) - defp decode_key(<>, "", acc), do: skip_until_cc(t, acc) - defp decode_key(<>, key, acc), do: decode_value(t, "", 0, key, acc) - defp decode_key(<>, key, acc), do: decode_key(t, <>, acc) - defp decode_key(<<>>, _key, acc), do: acc - - defp decode_value(<>, value, spaces, key, acc), - do: decode_kv(t, [{key, trim_spaces(value, spaces)} | acc]) - - defp decode_value(<>, value, spaces, key, acc), - do: decode_value(t, <>, spaces + 1, key, acc) - - defp decode_value(<>, _value, _spaces, _key, acc) - when h in [?\t, ?\r, ?\n, ?\v, ?\f], - do: skip_until_cc(t, acc) - - defp decode_value(<>, value, _spaces, key, acc), - do: decode_value(t, <>, 0, key, acc) + defp decode_key(<>, _key_rest, _len, acc), do: decode_kv(t, acc) + defp decode_key(<>, _key_rest, 0, acc), do: skip_until_cc(t, acc) + defp decode_key(<>, key_rest, len, acc) do + key = binary_part(key_rest, 0, len) + decode_value(t, t, 0, 0, key, acc) + end + defp decode_key(<<_, t::binary>>, key_rest, len, acc), do: decode_key(t, key_rest, len + 1, acc) + defp decode_key(<<>>, _key_rest, _len, acc), do: acc - defp decode_value(<<>>, value, spaces, key, acc), - do: [{key, trim_spaces(value, spaces)} | acc] + defp decode_value(<>, val_rest, len, spaces, key, acc) do + value = binary_part(val_rest, 0, len - spaces) + decode_kv(t, [{key, value} | acc]) + end + defp decode_value(<>, val_rest, len, spaces, key, acc) do + decode_value(t, val_rest, len + 1, spaces + 1, key, acc) + end + defp decode_value(<>, _val_rest, _len, _spaces, _key, acc) when h in [?\t, ?\r, ?\n, ?\v, ?\f], + do: skip_until_cc(t, acc) + defp decode_value(<<_, t::binary>>, val_rest, len, _spaces, key, acc) do + decode_value(t, val_rest, len + 1, 0, key, acc) + end + defp decode_value(<<>>, val_rest, len, spaces, key, acc) do + value = binary_part(val_rest, 0, len - spaces) + [{key, value} | acc] + end defp skip_until_cc(<>, acc), do: decode_kv(t, acc) defp skip_until_cc(<<_, t::binary>>, acc), do: skip_until_cc(t, acc) defp skip_until_cc(<<>>, acc), do: acc - defp trim_spaces(value, 0), do: value - defp trim_spaces(value, spaces), do: binary_part(value, 0, byte_size(value) - spaces) - @doc """ Encodes the given cookies as expected in a response header. From af4f49fc41e1722d6afaecf9b8fc2e677f2dcee2 Mon Sep 17 00:00:00 2001 From: preciz Date: Mon, 15 Jun 2026 10:02:21 +0200 Subject: [PATCH 2/2] Run formatter --- lib/plug/conn/cookies.ex | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/lib/plug/conn/cookies.ex b/lib/plug/conn/cookies.ex index ceaf2bcc..525f987d 100644 --- a/lib/plug/conn/cookies.ex +++ b/lib/plug/conn/cookies.ex @@ -51,15 +51,18 @@ defmodule Plug.Conn.Cookies do defp decode_kv(<>, acc) when h in [?\s, ?\t], do: decode_kv(t, acc) defp decode_kv(kv, acc) when is_binary(kv), do: decode_key(kv, kv, 0, acc) - defp decode_key(<>, _key_rest, _len, acc) when h in [?\s, ?\t, ?\r, ?\n, ?\v, ?\f], - do: skip_until_cc(t, acc) + defp decode_key(<>, _key_rest, _len, acc) + when h in [?\s, ?\t, ?\r, ?\n, ?\v, ?\f], + do: skip_until_cc(t, acc) defp decode_key(<>, _key_rest, _len, acc), do: decode_kv(t, acc) defp decode_key(<>, _key_rest, 0, acc), do: skip_until_cc(t, acc) + defp decode_key(<>, key_rest, len, acc) do key = binary_part(key_rest, 0, len) decode_value(t, t, 0, 0, key, acc) end + defp decode_key(<<_, t::binary>>, key_rest, len, acc), do: decode_key(t, key_rest, len + 1, acc) defp decode_key(<<>>, _key_rest, _len, acc), do: acc @@ -67,14 +70,19 @@ defmodule Plug.Conn.Cookies do value = binary_part(val_rest, 0, len - spaces) decode_kv(t, [{key, value} | acc]) end + defp decode_value(<>, val_rest, len, spaces, key, acc) do decode_value(t, val_rest, len + 1, spaces + 1, key, acc) end - defp decode_value(<>, _val_rest, _len, _spaces, _key, acc) when h in [?\t, ?\r, ?\n, ?\v, ?\f], - do: skip_until_cc(t, acc) + + defp decode_value(<>, _val_rest, _len, _spaces, _key, acc) + when h in [?\t, ?\r, ?\n, ?\v, ?\f], + do: skip_until_cc(t, acc) + defp decode_value(<<_, t::binary>>, val_rest, len, _spaces, key, acc) do decode_value(t, val_rest, len + 1, 0, key, acc) end + defp decode_value(<<>>, val_rest, len, spaces, key, acc) do value = binary_part(val_rest, 0, len - spaces) [{key, value} | acc]