changed CHANGELOG.md
 
@@ -1,5 +1,11 @@
1
1
# Changelog
2
2
3
+ ## v1.16.1 (2024-06-20)
4
+
5
+ ### Enhancements
6
+
7
+ * Optimize cookie parsing by 10x (10x faster, 10x less memory) on Erlang/OTP 26+
8
+
3
9
## v1.16.0 (2024-05-18)
4
10
5
11
### Enhancements
changed hex_metadata.config
 
@@ -1,6 +1,6 @@
1
1
{<<"links">>,[{<<"GitHub">>,<<"https://github.com/elixir-plug/plug">>}]}.
2
2
{<<"name">>,<<"plug">>}.
3
- {<<"version">>,<<"1.16.0">>}.
3
+ {<<"version">>,<<"1.16.1">>}.
4
4
{<<"description">>,<<"Compose web applications with functions">>}.
5
5
{<<"elixir">>,<<"~> 1.10">>}.
6
6
{<<"app">>,<<"plug">>}.
changed lib/plug.ex
 
@@ -40,6 +40,8 @@ defmodule Plug do
40
40
Here's an example of a module plug:
41
41
42
42
defmodule JSONHeaderPlug do
43
+ @behaviour Plug
44
+
43
45
import Plug.Conn
44
46
45
47
def init(opts) do
 
@@ -143,6 +145,8 @@ defmodule Plug do
143
145
## Example
144
146
145
147
defmodule Router do
148
+ @behaviour Plug
149
+
146
150
def init(opts), do: opts
147
151
148
152
def call(conn, opts) do
changed lib/plug/conn/cookies.ex
 
@@ -14,39 +14,45 @@ defmodule Plug.Conn.Cookies do
14
14
%{"key1" => "value1", "key2" => "value2"}
15
15
16
16
"""
17
- def decode(cookie) do
18
- do_decode(:binary.split(cookie, ";", [:global]), %{})
17
+ def decode(cookie) when is_binary(cookie) do
18
+ Map.new(decode_kv(cookie, []))
19
19
end
20
20
21
- defp do_decode([], acc), do: acc
21
+ defp decode_kv("", acc), do: acc
22
+ defp decode_kv(<<h, t::binary>>, acc) when h in [?\s, ?\t], do: decode_kv(t, acc)
23
+ defp decode_kv(kv, acc) when is_binary(kv), do: decode_key(kv, "", acc)
22
24
23
- defp do_decode([h | t], acc) do
24
- case decode_kv(h) do
25
- {k, v} -> do_decode(t, Map.put(acc, k, v))
26
- false -> do_decode(t, acc)
27
- end
28
- end
25
+ defp decode_key(<<h, t::binary>>, _key, acc) when h in [?\s, ?\t, ?\r, ?\n, ?\v, ?\f],
26
+ do: skip_until_cc(t, acc)
29
27
30
- defp decode_kv(""), do: false
31
- defp decode_kv(<<h, t::binary>>) when h in [?\s, ?\t], do: decode_kv(t)
32
- defp decode_kv(kv), do: decode_key(kv, "")
28
+ defp decode_key(<<?;, t::binary>>, _key, acc), do: decode_kv(t, acc)
29
+ defp decode_key(<<?=, t::binary>>, "", acc), do: skip_until_cc(t, acc)
30
+ defp decode_key(<<?=, t::binary>>, key, acc), do: decode_value(t, "", 0, key, acc)
31
+ defp decode_key(<<h, t::binary>>, key, acc), do: decode_key(t, <<key::binary, h>>, acc)
32
+ defp decode_key(<<>>, _key, acc), do: acc
33
33
34
- defp decode_key("", _key), do: false
35
- defp decode_key(<<?=, _::binary>>, ""), do: false
36
- defp decode_key(<<?=, t::binary>>, key), do: decode_value(t, "", key, "")
37
- defp decode_key(<<h, _::binary>>, _key) when h in [?\s, ?\t, ?\r, ?\n, ?\v, ?\f], do: false
38
- defp decode_key(<<h, t::binary>>, key), do: decode_key(t, <<key::binary, h>>)
34
+ defp decode_value(<<?;, t::binary>>, value, spaces, key, acc),
35
+ do: decode_kv(t, [{key, trim_spaces(value, spaces)} | acc])
39
36
40
- defp decode_value("", _spaces, key, value), do: {key, value}
37
+ defp decode_value(<<?\s, t::binary>>, value, spaces, key, acc),
38
+ do: decode_value(t, <<value::binary, ?\s>>, spaces + 1, key, acc)
41
39
42
- defp decode_value(<<?\s, t::binary>>, spaces, key, value),
43
- do: decode_value(t, <<spaces::binary, ?\s>>, key, value)
40
+ defp decode_value(<<h, t::binary>>, _value, _spaces, _key, acc)
41
+ when h in [?\t, ?\r, ?\n, ?\v, ?\f],
42
+ do: skip_until_cc(t, acc)
44
43
45
- defp decode_value(<<h, _::binary>>, _spaces, _key, _value) when h in [?\t, ?\r, ?\n, ?\v, ?\f],
46
- do: false
44
+ defp decode_value(<<h, t::binary>>, value, _spaces, key, acc),
45
+ do: decode_value(t, <<value::binary, h>>, 0, key, acc)
47
46
48
- defp decode_value(<<h, t::binary>>, spaces, key, value),
49
- do: decode_value(t, "", key, <<value::binary, spaces::binary, h>>)
47
+ defp decode_value(<<>>, value, spaces, key, acc),
48
+ do: [{key, trim_spaces(value, spaces)} | acc]
49
+
50
+ defp skip_until_cc(<<?;, t::binary>>, acc), do: decode_kv(t, acc)
51
+ defp skip_until_cc(<<_, t::binary>>, acc), do: skip_until_cc(t, acc)
52
+ defp skip_until_cc(<<>>, acc), do: acc
53
+
54
+ defp trim_spaces(value, 0), do: value
55
+ defp trim_spaces(value, spaces), do: binary_part(value, 0, byte_size(value) - spaces)
50
56
51
57
@doc """
52
58
Encodes the given cookies as expected in a response header.
changed lib/plug/rewrite_on.ex
 
@@ -10,10 +10,10 @@ defmodule Plug.RewriteOn do
10
10
11
11
The supported values are:
12
12
13
- * `:x_forwarded_for` - to override the remote ip based on on the "x-forwarded-for" header
14
- * `:x_forwarded_host` - to override the host based on on the "x-forwarded-host" header
15
- * `:x_forwarded_port` - to override the port based on on the "x-forwarded-port" header
16
- * `:x_forwarded_proto` - to override the protocol based on on the "x-forwarded-proto" header
13
+ * `:x_forwarded_for` - to override the remote ip based on the "x-forwarded-for" header
14
+ * `:x_forwarded_host` - to override the host based on the "x-forwarded-host" header
15
+ * `:x_forwarded_port` - to override the port based on the "x-forwarded-port" header
16
+ * `:x_forwarded_proto` - to override the protocol based on the "x-forwarded-proto" header
17
17
18
18
A tuple representing a Module-Function-Args can also be given as argument
19
19
instead of a list.
changed lib/plug/static.ex
 
@@ -167,7 +167,8 @@ defmodule Plug.Static do
167
167
%{
168
168
encodings: encodings,
169
169
only_rules: {Keyword.get(opts, :only, []), Keyword.get(opts, :only_matching, [])},
170
- qs_cache: Keyword.get(opts, :cache_control_for_vsn_requests, "public, max-age=31536000, immutable"),
170
+ qs_cache:
171
+ Keyword.get(opts, :cache_control_for_vsn_requests, "public, max-age=31536000, immutable"),
171
172
et_cache: Keyword.get(opts, :cache_control_for_etags, "public"),
172
173
et_generation: Keyword.get(opts, :etag_generation, nil),
173
174
headers: Keyword.get(opts, :headers, %{}),
changed lib/plug/templates/debugger.html.eex
 
@@ -844,6 +844,8 @@
844
844
on($toggle, 'click', toggleOnclick)
845
845
on($copyBtn, 'click', copyToClipboard)
846
846
847
+ restoreToggle()
848
+
847
849
function copyToClipboard () {
848
850
if(navigator.clipboard) {
849
851
// For those working on localhost or HTTPS
 
@@ -863,6 +865,8 @@
863
865
}
864
866
865
867
function toggleOnclick () {
868
+ localStorage.setItem('plugStackTrace', this.checked ? 'checked' : '');
869
+
866
870
if (this.checked) {
867
871
var $first = document.querySelector('[role~="stack-trace-item"].-app:first-of-type')
868
872
if ($first) itemOnclick.call($first)
 
@@ -872,6 +876,11 @@
872
876
}
873
877
}
874
878
879
+ function restoreToggle () {
880
+ $toggle.checked = localStorage.getItem('plugStackTrace') === 'checked'
881
+ toggleOnclick.call($toggle)
882
+ }
883
+
875
884
function itemOnclick () {
876
885
var idx = this.getAttribute('data-index')
changed mix.exs
 
@@ -1,7 +1,7 @@
1
1
defmodule Plug.MixProject do
2
2
use Mix.Project
3
3
4
- @version "1.16.0"
4
+ @version "1.16.1"
5
5
@description "Compose web applications with functions"
6
6
@xref_exclude [Plug.Cowboy, :ssl]