From 9db6ec619aab955e0ad1a6b9e6add090b42890b5 Mon Sep 17 00:00:00 2001 From: Aaron Renner Date: Tue, 11 Nov 2025 13:08:08 -0600 Subject: [PATCH 1/4] Fixes crash when path is too long --- lib/plug/cowboy.ex | 28 +++++++++++++++++++++++++++- test/plug/cowboy/conn_test.exs | 27 +++++++++++++++++++++++++++ 2 files changed, 54 insertions(+), 1 deletion(-) diff --git a/lib/plug/cowboy.ex b/lib/plug/cowboy.ex index 41d859e..2ed1d0f 100644 --- a/lib/plug/cowboy.ex +++ b/lib/plug/cowboy.ex @@ -429,10 +429,36 @@ defmodule Plug.Cowboy do end @doc false + def handle_event( + [:cowboy, :request, :early_error], + _, + %{resp_status: 414, reason: {:connection_error, :limit_reached, specific_reason}, partial_req: partial_req}, + _ + ) do + Logger.error(""" + Cowboy returned 414 because the request path was too long. + + The more specific reason is: + + #{inspect(specific_reason)} + + You can customize those limits when configuring your http/https + server. The configuration option and default values are shown below: + + protocol_options: [ + max_request_line_length: 50_000 + ] + + Request info: + + peer: #{format_peer(partial_req.peer)} + """) + end + def handle_event( [:cowboy, :request, :early_error], _, - %{reason: {:connection_error, :limit_reached, specific_reason}, partial_req: partial_req}, + %{reason: {:connection_error, :limit_reached, specific_reason}, partial_req: partial_req} = meta, _ ) do Logger.error(""" diff --git a/test/plug/cowboy/conn_test.exs b/test/plug/cowboy/conn_test.exs index f1742ea..7d836e8 100644 --- a/test/plug/cowboy/conn_test.exs +++ b/test/plug/cowboy/conn_test.exs @@ -245,6 +245,33 @@ defmodule Plug.Cowboy.ConnTest do :telemetry.detach(:early_error_test) end + test "emits telemetry events for cowboy early_error for paths that are too long" do + :telemetry.attach( + :early_error_test, + [:cowboy, :request, :early_error], + &__MODULE__.telemetry_send/4, + self() + ) + + assert capture_log(fn -> + # Send a request line that's too long (exceeds max_request_line_length) + long_path = String.duplicate("a", 10_000) + response = request(:get, "/#{long_path}") + assert match?({414, _, _}, response) or match?({:error, :closed}, response) + end) =~ "Cowboy returned 414 because the request path was too long" + + assert_receive {:telemetry, [:cowboy, :request, :early_error], + %{ + system_time: _ + }, + %{ + reason: {:connection_error, :limit_reached, _}, + partial_req: %{} + }} + + :telemetry.detach(:early_error_test) + end + def send_200(conn) do assert conn.state == :unset assert conn.resp_body == nil From 001363e45c82e12b5b8980a8ce6aa24f4bf164d1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Tue, 11 Nov 2025 21:49:19 +0100 Subject: [PATCH 2/4] format --- lib/plug/cowboy.ex | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/lib/plug/cowboy.ex b/lib/plug/cowboy.ex index 2ed1d0f..8d46310 100644 --- a/lib/plug/cowboy.ex +++ b/lib/plug/cowboy.ex @@ -430,11 +430,15 @@ defmodule Plug.Cowboy do @doc false def handle_event( - [:cowboy, :request, :early_error], - _, - %{resp_status: 414, reason: {:connection_error, :limit_reached, specific_reason}, partial_req: partial_req}, - _ - ) do + [:cowboy, :request, :early_error], + _, + %{ + resp_status: 414, + reason: {:connection_error, :limit_reached, specific_reason}, + partial_req: partial_req + }, + _ + ) do Logger.error(""" Cowboy returned 414 because the request path was too long. @@ -458,7 +462,8 @@ defmodule Plug.Cowboy do def handle_event( [:cowboy, :request, :early_error], _, - %{reason: {:connection_error, :limit_reached, specific_reason}, partial_req: partial_req} = meta, + %{reason: {:connection_error, :limit_reached, specific_reason}, partial_req: partial_req} = + meta, _ ) do Logger.error(""" From 15c2a620f034dbe93889fe6972a713b3be39cae6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Tue, 11 Nov 2025 21:58:32 +0100 Subject: [PATCH 3/4] Update test/plug/cowboy/conn_test.exs --- test/plug/cowboy/conn_test.exs | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/test/plug/cowboy/conn_test.exs b/test/plug/cowboy/conn_test.exs index 7d836e8..f86ceda 100644 --- a/test/plug/cowboy/conn_test.exs +++ b/test/plug/cowboy/conn_test.exs @@ -254,10 +254,10 @@ defmodule Plug.Cowboy.ConnTest do ) assert capture_log(fn -> - # Send a request line that's too long (exceeds max_request_line_length) - long_path = String.duplicate("a", 10_000) - response = request(:get, "/#{long_path}") - assert match?({414, _, _}, response) or match?({:error, :closed}, response) + # Send a request line that's too long (exceeds max_request_line_length) + long_path = String.duplicate("a", 10_000) + response = request(:get, "/#{long_path}") + assert match?({414, _, _}, response) or match?({:error, :closed}, response) end) =~ "Cowboy returned 414 because the request path was too long" assert_receive {:telemetry, [:cowboy, :request, :early_error], From 4cbec6b97db4f25ef875cd8258569fcea5abcb34 Mon Sep 17 00:00:00 2001 From: Aaron Renner Date: Tue, 11 Nov 2025 15:36:10 -0600 Subject: [PATCH 4/4] Remove unnecessary meta variable --- lib/plug/cowboy.ex | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/plug/cowboy.ex b/lib/plug/cowboy.ex index 8d46310..4ed13e4 100644 --- a/lib/plug/cowboy.ex +++ b/lib/plug/cowboy.ex @@ -462,8 +462,7 @@ defmodule Plug.Cowboy do def handle_event( [:cowboy, :request, :early_error], _, - %{reason: {:connection_error, :limit_reached, specific_reason}, partial_req: partial_req} = - meta, + %{reason: {:connection_error, :limit_reached, specific_reason}, partial_req: partial_req}, _ ) do Logger.error("""