0
Fork 0
mirror of https://github.com/ninenines/cowboy.git synced 2025-07-14 12:20:24 +00:00
Commit graph

137 commits

Author SHA1 Message Date
Loïc Hoguin
e5241620f5
Fix cowboy_http2 Dialyzer warnings with OTP-28 2025-06-20 11:50:38 +02:00
Loïc Hoguin
eef66e0928
Remove copyright years from all files except LICENSE 2025-02-17 15:00:02 +01:00
Loïc Hoguin
053e233c56
Provide better control over which HTTP protocols are enabled
Over cleartext TCP the `protocols` option lists the enabled
protocols. The default is to allow both HTTP/1.1 and HTTP/2.

Over TLS the default protocol to use when ALPN is not used
can now be configured via the `alpn_default_protocol` option.

Performing an HTTP/1.1 upgrade to HTTP/2 over TLS is now
rejected with an error as connecting to HTTP/2 over TLS
requires the use of ALPN (or that HTTP/2 be the default
when connecting over TLS).
2025-02-10 15:26:00 +01:00
Loïc Hoguin
0f257d06b6
Add hibernate option to cowboy_http and cowboy_http2
When enabled the connection process will automatically hibernate.
Because hibernation triggers GC, this can be used as a way to
keep memory usage lower, at the cost of performance.
2025-02-07 17:59:44 +01:00
Loïc Hoguin
49be0f57cf
Implement dynamic socket buffer sizes
Cowboy will set the socket's buffer size dynamically to
better fit the current workload. When the incoming data
is small, a low buffer size reduces the memory footprint
and improves responsiveness and therefore performance.
When the incoming data is large, such as large HTTP
request bodies, a larger buffer size helps us avoid
doing too many binary appends and related allocations.

Setting a large buffer size for all use cases is
sub-optimal because allocating more than needed
necessarily results in a performance hit (not just
increased memory usage).

By default Cowboy starts with a buffer size of 8192 bytes.
It then doubles or halves the buffer size depending on
the size of the data it receives from the socket. It
stops decreasing at 8192 and increasing at 131072 by
default.

To keep track of the size of the incoming data Cowboy
maintains a moving average. It allows Cowboy to avoid
changing the buffer too often but still react quickly
when necessary. Cowboy will increase the buffer size
when the moving average is above 90% of the current
buffer size, and decrease when the moving average is
below 40% of the current buffer size.

The current buffer size and moving average are
propagated when switching protocols. The dynamic buffer
is implemented in HTTP/1, HTTP/2 and HTTP/1 Websocket.
HTTP/2 Websocket has it disabled because it doesn't
interact directly with the socket; in that case it
is HTTP/2 that has a dynamic buffer.

The dynamic buffer provides a very large performance improvement
in many scenarios, at minimal cost for others. Because it largely
depend on the underlying protocol the improvements are no all equal.
TLS and compression also impact the results.

The improvement when reading a large request body, with the
requests repeated in a fast loop are:

* HTTP: 6x to 20x faster
* HTTPS: 2x to 6x faster
* H2: 4x to 5x faster
* H2C: 20x to 40x faster

I am not sure why H2C's performance was so bad, especially compared
to H2, when using default buffer sizes. Dynamic buffers make H2C a
lot more viable with default settings.

The performance impact on "hello world" type requests is minimal,
it goes from -5% to +5% roughly.

Websocket improvements vary again depending on the protocol, but
also depending on whether compression is enabled:

* HTTP echo: roughly 2x faster
* HTTP send: roughly 4x faster
* H2C echo: roughly 2x faster
* H2C send: 3x to 4x faster

In the echo test we reply back, and Gun doesn't have the dynamic
buffer optimisation, so that probably explains the x2 difference.

With compression however there isn't much improvement. The results
are roughly within -10% to +10% of each other. Zlib compression
seems to be a bottleneck, or at least to modify the performance
profile to such an extent that the size of the buffer does not
matter. This happens to randomly generated binary data as well
so it is probably not caused by the test data.
2025-02-05 14:29:58 +01:00
Loïc Hoguin
1724575b42
Avoid resetting HTTP/2 idle_timeout timer too often
Following the same strategy as Websocket described in
commit cbed21c383e4cebb7df5a0a8b81f18c1738bef3e

Gains are comparable as far as Websocket over HTTP/2
is concerned.
2025-01-15 13:31:19 +01:00
Loïc Hoguin
8cb9d242b0
Initial HTTP/3 implementation
This includes Websocket over HTTP/3.

Since quicer, which provides the QUIC implementation,
is a NIF, Cowboy cannot depend directly on it. In order
to enable QUIC and HTTP/3, users have to set the
COWBOY_QUICER environment variable:

  export COWBOY_QUICER=1

In order to run the test suites, the same must be done
for Gun:

  export GUN_QUICER=1

HTTP/3 support is currently not available on Windows
due to compilation issues of quicer which have yet to
be looked at or resolved.

HTTP/3 support is also unavailable on the upcoming
OTP-27 due to compilation errors in quicer dependencies.
Once resolved HTTP/3 should work on OTP-27.

Because of how QUIC currently works, it's possible
that streams that get reset after sending a response
do not receive that response. The test suite was
modified to accomodate for that. A future extension
to QUIC will allow us to gracefully reset streams.

This also updates Erlang.mk.
2024-03-26 15:53:48 +01:00
Loïc Hoguin
cf71c742d6
Add max_fragmented_header_block_size HTTP/2 option 2024-03-14 12:56:33 +01:00
Loïc Hoguin
b36f064a91
Refresh copyright lines 2024-01-25 11:22:54 +01:00
Loïc Hoguin
ffbcdf534c
Don't update an HTTP/2 stream's window if stream stopped 2023-12-21 15:38:51 +01:00
Robert J. Macomber
f74b69c3ed
Optionally reset the idle timeout when sending data
A new option reset_idle_timeout_on_send has been added.
When set to 'true', the idle timeout is reset not only
when data is received, but also when data is sent.

This allows sending large responses without having to
worry about timeouts triggering.

The default is currently unchanged but might change in
a future release.

LH: Greatly reworked the implementation so that the
    timeout gets reset on almost all socket writes.
	This essentially completely supersets the original
	work. Tests are mostly the same although I
	refactored a bit to avoid test code duplication.

This commit also changes HTTP/2 behavior a little when
data is received: Cowboy will not attempt to update the
window before running stream handler commands to avoid
sending WINDOW_UPDATE frames twice. Now it has some
small heuristic to ensure they can only be sent once
at most.
2023-12-21 14:03:07 +01:00
Loïc Hoguin
8fdb74a510
Shave off a few more seconds from rfc7540_SUITE 2023-12-19 11:09:54 +01:00
Loïc Hoguin
efb681d749
Handle socket errors in HTTP/1.1 and HTTP/2
Doing so will let us notice when the connection is gone instead
of waiting for timeouts, at least in the cases where the remote
socket was closed properly. Timeouts are still needed in case
of TCP half-open problems.

This change means that the order of stream handler commands is
more important than before because socket errors may occur
during the processing of commands.
2023-12-12 15:05:33 +01:00
Viktor Söderqvist
42d87dd776
Add 'max_cancel_stream_rate' config for the rapid reset attack
Co-authored-by: Björn Svensson <bjorn.a.svensson@est.tech>
2023-12-06 12:41:58 +01:00
Loïc Hoguin
105edf1d6e
Fix data sent after RST_STREAM in HTTP/2 in rare cases 2022-09-19 14:10:32 +02:00
Simon Johansson
f6049b85a3
Use functions for inititalizing rate limiting
... to ensure that the same values are used in all places.
2020-11-27 15:51:09 +01:00
Viktor Söderqvist
059d58d39f
Graceful shutdown
Note: This commit makes cowboy depend on cowlib master.

Graceful shutdown for HTTP/2:

1. A GOAWAY frame with the last stream id set to 2^31-1 is sent and a
   timer is started (goaway_initial_timeout, default 1000ms), to wait
   for any in-flight requests sent by the client, and the status is set
   to 'closing_initiated'. If the client responds with GOAWAY and closes
   the connection, we're done.
2. A second GOAWAY frame is sent with the actual last stream id and the
   status is set to 'closing'. If no streams exist, the connection
   terminates. Otherwise a second timer (goaway_complete_timeout,
   default 3000ms) is started, to wait for the streams to complete. New
   streams are not accepted when status is 'closing'.
3. If all streams haven't completed after the second timeout, the
   connection is forcefully terminated.

Graceful shutdown for HTTP/1.x:

1. If a request is currently being handled, it is waited for and the
   response is sent back to the client with the header "Connection:
   close". Then, the connection is closed.
2. If the current request handler is not finished within the time
   configured in transport option 'shutdown' (default 5000ms), the
   connection process is killed by its supervisor (ranch).

Implemented for HTTP/1.x and HTTP/2 in the following scenarios:

* When receiving exit signal 'shutdown' from the supervisor (e.g. when
  cowboy:stop_listener/3 is called).
* When a connection process is requested to terminate using
  sys:terminate/2,3.

LH: Edited tests a bit and added todos for useful tests to add.
2020-11-27 15:38:21 +01:00
Loïc Hoguin
775091134d
Experiment with a linger_timeout for HTTP/2
This is mostly to ensure that the GOAWAY frame is properly
received on Windows in some tests, but should be benefitial
also in production in particular when clients are slower.
2020-04-06 14:50:35 +02:00
Loïc Hoguin
6ad842a742
Increase the default max_received_frame_rate
Allow 10000 frames every 10 seconds instead of just 1000,
as the limit was too quickly reached in some deployments.
2020-03-29 13:51:21 +02:00
Loïc Hoguin
db0d6f8d25
Use active,N
This reduces the number of times we need to ask for more packets,
and as a result we get a fairly large boost in performance,
especially with HTTP/1.1.

Unfortunately this makes Cowboy require at least Erlang/OTP 21.3+
because the ssl application did not have active,N. For simplicity
the version required will be Erlang/OTP 22+.

In addition this change improves hibernate handling in
cowboy_websocket. Hibernate will now work for HTTP/2 transport
as well, and stray or unrelated messages will no longer cancel
hibernate (the process will handle the message and go back into
hibernation).

Thanks go to Stressgrid for benchmarking an early version of this
commit: https://stressgrid.com/blog/cowboy_performance_part_2/
2020-01-06 12:58:14 +01:00
Loïc Hoguin
592029070d
Reduce number of Transport:send/2 calls for HTTP/2
When sending a complete response it is far more efficient
to send the headers and the body in one Transport:send/2
call instead of two or more, at least for small responses.

This is the HTTP/2 counterpart to what was done for HTTP/1.1
many years ago in bfab8d4b22.

In HTTP/2's case however the implementation is a little
more difficult due to flow control. On the other hand the
optimization will apply not only for headers/body but also
for the body of multiple separate responses, which may need
to be sent all at the same time when we receive a WINDOW_UPDATE
frame.

When a body is sent using sendfile however a separate call
is still made.
2020-01-02 13:29:56 +01:00
Loïc Hoguin
3a7232b019
No longer use erlang:get_stacktrace/0
It has been deprecated in OTP and the new way is available
on all supported OTP versions.
2019-12-31 15:10:38 +01:00
Loïc Hoguin
3ae228897a
Don't log stray messages for lingering HTTP/2 streams 2019-10-10 17:06:24 +02:00
Loïc Hoguin
0342866c2e
Document cowboy_tracer_h 2019-10-07 10:43:22 +02:00
Loïc Hoguin
eaed063702
Document cowboy_metrics_h 2019-10-07 09:59:36 +02:00
Loïc Hoguin
1ba48c58b1
Make stream_error early_error reasons consistent
Now both HTTP/1.1 and HTTP/2 follow the documented format.
HTTP/1.1 was including an extra element containing the
StreamID before, which was unnecessary because it is also
given as argument to the callback.

HTTP/2 early_error will now include headers in its PartialReq.
2019-10-03 16:04:17 +02:00
Loïc Hoguin
8e31548597
Fix a Dialyzer warning and improve some types 2019-10-02 12:01:40 +02:00
Loïc Hoguin
ab44985a9e
Fix HTTP/2 CVEs
A number of HTTP/2 CVEs were documented recently:

  https://www.kb.cert.org/vuls/id/605641/

This commit, along with a few changes and additions in Cowlib,
fix or improve protection against all of them.

For CVE-2019-9511, also known as Data Dribble, the new option
stream_window_data_threshold can be used to control how little
the DATA frames that Cowboy sends can get.

For CVE-2019-9516, also known as 0-Length Headers Leak, Cowboy
will now simply reject streams containing 0-length header names.

For CVE-2019-9517, also known as Internal Data Buffering, the
backpressure changes were already pretty good at preventing this
issue, but a new option max_connection_buffer_size was added for
even better control over how much memory we are willing to allocate.

For CVE-2019-9512, also known as Ping Flood; CVE-2019-9515, also
known as Settings Flood; CVE-2019-9518, also known as Empty Frame
Flooding; and similar undocumented scenarios, a frame rate limiting
mechanism was added. By default Cowboy will now allow 1000 frames
every 10 seconds. This can be configured via max_received_frame_rate.

For CVE-2019-9514, also known as Reset Flood, another rate limiting
mechanism was added and can be configured via max_reset_stream_rate.
By default Cowboy will do up to 10 stream resets every 10 seconds.

Finally, nothing was done for CVE-2019-9513, also known as Resource
Loop, because Cowboy does not currently implement the HTTP/2
priority mechanism (in parts because these issues were well known
from the start).

Tests were added for all cases except Internal Data Buffering,
which I'm not sure how to test, and Resource Loop, which is not
currently relevant.
2019-10-02 10:44:45 +02:00
Loïc Hoguin
6ddabc2c21
Remove lingering_data tuple handling 2019-10-02 10:09:30 +02:00
Loïc Hoguin
49af57d546
Implement backpressure on cowboy_req:stream_body
This should limit the amount of memory that Cowboy is using
when a handler is sending data much faster than the network.

The new max_stream_buffer_size is a soft limit and only has
an effect when the cowboy_stream_h handler is used.
2019-09-14 18:21:05 +02:00
Loïc Hoguin
48f417ac8f
Fix and optimize sending of WINDOW_UPDATE frames
For long-running connections it was possible for the connection
window to become larger than allowed by the protocol because the
window increases claimed by stream handlers were never reclaimed
even if no data was consumed.

The new code applies heuristics to fix this and reduce the number
of WINDOW_UPDATE frames that are sent. It includes six new options
to control that behavior: margin, max and threshold for both the
connection and stream windows. The margin is some extra space
added on top of the requested read size. The max is the maximum
window size at any given time. The threshold is a minimum window
size that must be reached before we even consider sending more
WINDOW_UPDATE frames. We also avoid sending WINDOW_UPDATE frames
when there is already enough space in the window, or when the
read size is 0.

Cowlib is set to master until a new tag is done.
2019-09-05 14:07:38 +02:00
Loïc Hoguin
a0a752ab10
Gracefully shut down HTTP/2 connections on GOAWAY 2019-08-16 16:37:04 +02:00
juhlig
5b4e78fac4
Make Cowboy compatible with upcoming Ranch 2.0 2019-07-16 15:35:45 +02:00
Tony Han
7708fc77cd
Data received after RST_STREAM counts toward window 2019-07-16 15:32:58 +02:00
Fredrik Enestad
7ff9e963b8
Fallback to host header if authority is missing 2019-04-01 15:04:33 +02:00
Loïc Hoguin
8185d356c5
Add the idle_timeout option to HTTP/2 2018-11-16 16:30:57 +01:00
Loïc Hoguin
75045637fc
Ensure unknown options are ignored in set_options command 2018-11-16 13:09:01 +01:00
Loïc Hoguin
fbfec873f6
Add a compress_buffering option to cowboy_compress_h
Also changes the behavior to disable buffering by default, so
that the default works in all cases, including server-sent events.
2018-11-15 10:11:36 +01:00
Loïc Hoguin
d7b7580b39
Add sendfile support to cowboy_req:stream_body
It is now possible to stream one or more sendfile tuples.
A simple example of what can now be done would be for
example to build a tar file on the fly using the sendfile
syscall for sending the files, or to support Range requests
with more than one range with the sendfile syscall.

When using cowboy_compress_h unfortunately we have to read
the file in order to send it. More options will be added
at a later time to make sure users don't read too much
into memory. This is a new feature however so existing
code is not affected.

Also rework cowboy_http's data sending to be flatter.
2018-11-09 17:42:37 +01:00
Loïc Hoguin
be09711687
Add an option to disable sendfile for a listener 2018-11-03 18:55:40 +01:00
Steve Domin
09bf1199aa
Add compress_threshold protocol option
Currently the compression threshold is set to 300 and hardcoded in the
codebase. There are cases where it make sense to allow this to be
configured, for instance when you want to enforce all responses to be
compressed regarldess of their size.
2018-10-31 17:05:11 +01:00
Loïc Hoguin
473e3fb82b
Improve a few types, including cowboy_req:req() 2018-10-31 14:11:45 +01:00
Loïc Hoguin
122faedc25
Initial support for the PROXY protocol header
Depend on Ranch master for now since it isn't in any release yet.
2018-10-30 23:30:54 +01:00
Loïc Hoguin
e11279e0d2
Remove a useless todo 2018-10-28 13:12:17 +01:00
Loïc Hoguin
a764075b89
Rename a few functions and shuffle arguments 2018-10-28 11:47:49 +01:00
Loïc Hoguin
266178b6bc
Remove an unnecessary function 2018-10-28 11:42:18 +01:00
Loïc Hoguin
d4129e6305
Exit gracefully on parent exit/sys:terminate/2,3 2018-10-28 10:20:43 +01:00
Loïc Hoguin
21ff3ff256
Fix small issues introduced in the previous commit 2018-10-27 10:07:34 +02:00
Loïc Hoguin
f1018fd1c1
Use cow_http2_machine's timer handling 2018-10-27 00:16:13 +02:00
Loïc Hoguin
7d118b547f
Use cow_http2:parse_sequence/1 2018-10-26 18:51:49 +02:00