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]
|