Skip to content

Commit

Permalink
Support HTTP headers with a size up to 8k (#36)
Browse files Browse the repository at this point in the history
By default cowboy accepts request headers for a max size of
[4k](https://ninenines.eu/docs/en/cowboy/2.12/manual/cowboy_http/) (It
is not clear if is the max size of each http header, or the max size for
the sum of all headers).

With the default configuration, Neurow returns 431 HTTP errors when the
requests also contains very long cookies.

This PR extends the max value to 8k and make it customizable by an
environment variable.
  • Loading branch information
achouippe authored Oct 21, 2024
1 parent e38269e commit 176d30e
Show file tree
Hide file tree
Showing 4 changed files with 30 additions and 2 deletions.
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,7 @@ Available environment variables are:
| `PREFLIGHT_MAX_AGE` | 86400 | Value of the `access-control-max-age` headers on CROS preflight responses on the public API |
| `SSE_TIMEOUT` | 900000 | SSE deconnection delay in ms, after the last received message
| `SSE_KEEPALIVE` | 600000 | Neurow periodically send `ping` events on SSE connections to prevent connections from being closed by network devices. This variable defines the delay between two ping events in milliseconds. |
| `MAX_HEADER_VALUE_LENGTH`| 8192 | Max http request headers size, as expected by the cowboy HTTP server [here](https://ninenines.eu/docs/en/cowboy/2.12/manual/cowboy_http/) |
| `INTERNAL_API_PORT` | 3000 | TCP port fo the internal API |
| `INTERNAL_API_JWT_MAX_LIFETIME` | 1500 | Max lifetime in seconds allowed for JWT tokens issued on the internal API |
| `HISTORY_MIN_DURATION` | 30 | Messages are persisted in the Neurow cluster, so clients can re-fetch recent messages after a short term disconnection by using the `Last-Event-Id` on SSE connections. Messages are only persisted for a limited time. `HISTORY_MIN_DURATION` defines the minimum retention guaranteed by the Neurow server.
Expand Down
3 changes: 2 additions & 1 deletion neurow/config/runtime.exs
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,8 @@ config :neurow,
public_api_preflight_max_age: String.to_integer(System.get_env("PREFLIGHT_MAX_AGE") || "86400"),
public_api_context_path: System.get_env("PUBLIC_API_CONTEXT_PATH") || "",
sse_timeout: String.to_integer(System.get_env("SSE_TIMEOUT") || "900000"),
sse_keepalive: String.to_integer(System.get_env("SSE_KEEPALIVE") || "600000")
sse_keepalive: String.to_integer(System.get_env("SSE_KEEPALIVE") || "600000"),
max_header_value_length: String.to_integer(System.get_env("MAX_HEADER_VALUE_LENGTH") || "8192")

# Internal API configuration
config :neurow,
Expand Down
20 changes: 20 additions & 0 deletions neurow/integration_test/sse_livecycle_test.exs
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,26 @@ defmodule Neurow.IntegrationTest.SseLifecycleTest do
end
end

describe "header HTTP size" do
test "supports HTTP headers up to 8k with the default configuration", %{
cluster_state: %{
public_api_ports: [first_public_port | _other_ports]
}
} do
fake_cookie = String.duplicate("a", 8_000)

subscribe(
first_public_port,
"test_topic",
fn ->
assert_receive %HTTPoison.AsyncStatus{code: 200}
assert_receive %HTTPoison.AsyncHeaders{}
end,
cookie: fake_cookie
)
end
end

def override_timeout(timeout) do
{:ok, default_timeout} = Application.fetch_env(:neurow, :sse_timeout)
TestCluster.update_sse_timeout(timeout)
Expand Down
8 changes: 7 additions & 1 deletion neurow/lib/neurow/application.ex
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ defmodule Neurow.Application do
{:ok, ssl_keyfile} = Application.fetch_env(:neurow, :ssl_keyfile)
{:ok, ssl_certfile} = Application.fetch_env(:neurow, :ssl_certfile)
{:ok, history_min_duration} = Application.fetch_env(:neurow, :history_min_duration)
{:ok, max_header_value_length} = Application.fetch_env(:neurow, :max_header_value_length)

cluster_topologies =
Application.get_env(:neurow, :cluster_topologies, cluster_topologies_from_env_variables())
Expand All @@ -25,6 +26,7 @@ defmodule Neurow.Application do
internal_api_port: internal_api_port,
ssl_keyfile: ssl_keyfile,
ssl_certfile: ssl_certfile,
max_header_value_length: max_header_value_length,
history_min_duration: history_min_duration,
cluster_topologies: cluster_topologies
})
Expand All @@ -35,6 +37,7 @@ defmodule Neurow.Application do
internal_api_port: internal_api_port,
ssl_keyfile: ssl_keyfile,
ssl_certfile: ssl_certfile,
max_header_value_length: max_header_value_length,
history_min_duration: history_min_duration,
cluster_topologies: cluster_topologies
}) do
Expand All @@ -43,7 +46,10 @@ defmodule Neurow.Application do

base_public_api_http_config = [
port: public_api_port,
protocol_options: [idle_timeout: :infinity],
protocol_options: [
max_header_value_length: max_header_value_length,
idle_timeout: :infinity
],
transport_options: [max_connections: :infinity]
]

Expand Down

0 comments on commit 176d30e

Please sign in to comment.