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

635 commits

Author SHA1 Message Date
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
2531b26acf
Add initial http_perf_SUITE 2025-01-24 13:05:45 +01:00
Björn Svensson
662f6af982
Correcting TransOpts in send_timeout_close tests 2025-01-23 13:29:47 +01:00
Jose M Perez
49061587be
Add WS compression test where only server sets client_max_window_bits
When the server has a non-default value configured and the client
doesn't send one the extension negotiation should fail.
2025-01-23 11:39:48 +01:00
Loïc Hoguin
7f739cad6d
Websocket: Also apply max_frame_size limit to decompressed data
Before this commit frames could "cheat" by compressing data
below the limit which would get expanded above the limit.
Now Cowboy will stop decompressing data when the limit is
reached.
2025-01-22 15:20:20 +01:00
Loïc Hoguin
9d385fa909
Add compressed Websocket to ws_perf_SUITE 2025-01-22 11:32:56 +01:00
Loïc Hoguin
81de580aee
Websocket: Allow setting the max_frame_size option dynamically
This can be used to limit the maximum frame size before
some authentication or other validation is completed.
2025-01-16 14:47:20 +01:00
Loïc Hoguin
818b448ae9
Switch to running autobahntestsuite via Docker 2025-01-15 13:31:19 +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
086f60cca4
Avoid resetting Websocket idle_timeout timer too often
`perf` has shown that Cowboy spends a lot of time
cancelling and starting this timer. Instead of resetting
for every data received, we now only reset a field in the
state.

Before it was working like this:

- start idle timeout timer
- on trigger, close the connection
- on data, cancel and start again

Now it's working like this:

- start idle timeout timer for a tenth of its duration, with tick number = 0
- on trigger, if tick number != 10
  - start the timer again, again for a tenth of its duration
  - increment tick number
- on trigger, if tick number = 10
  - close the connection
- on data, set tick number to 0
2025-01-15 13:31:19 +01:00
Loïc Hoguin
643b335ba8
Add ws_perf_SUITE to measure Websocket performance
It benchmarks binary, ascii, mixed and japanese data
using Websocket and Websocket over HTTP/2.

HTTP/2 options get set to ensure that performance is
better than the default HTTP/2 options.

It switches to Gun and Ranch branches that include
fixes that are required for tests to complete successfully.
2025-01-15 13:28:57 +01:00
Loïc Hoguin
b77dd29133
Add VU#421644 to the HTTP/2 CONTINUATION Flood test 2024-04-05 22:08:59 +02: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
geeksilva97
08c2be058a
Fix match_qs with constraints when key is not present
Original fix by Ali Farhadi <a.farhadi@gmail.com>.
2024-01-23 14:11:29 +01:00
Loïc Hoguin
e8b4715a9f
Reduce sleep in chunked_one_byte_at_a_time
To avoid having the connection get closed due to us taking
too long on unreliable environments like GitHub Actions.
2024-01-18 15:19:23 +01:00
Loïc Hoguin
992ee6241d
Retry the read_urlencoded_body_too_large if timeout triggers
This is caused by the timeout being 1s after the period.
When the CI environment is overloaded, sometimes the
timeout will trigger. We retry, knowing that the
timetrap will catch us if we retry too much.
2024-01-18 12:15:11 +01:00
Loïc Hoguin
ecf3d43613
Improve reliability of a few tests
GitHub Actions runners are not as good as self-hosted BuildKite
so some adjustments need to be made to timeouts and such.
2024-01-17 20:56:46 +01:00
Loïc Hoguin
defce46fdf
REST: Allow generate_etag to return undefined
This allows conditionally generating an etag.
2024-01-16 16:28:52 +01:00
Loïc Hoguin
08a8d7b9e9
Confirm Websocket pong frames are received by handlers 2024-01-16 12:20:38 +01:00
Loïc Hoguin
920adb9b82
Fix an intermittent test failure 2024-01-16 11:21:30 +01:00
geeksilva97
308045fd67
Reject responses with explicit set-cookie header
LH: The tests received a lot of fixes and tweaking.
    I also reworded the error message to be more concise.
2024-01-15 17:43:14 +01:00
Loïc Hoguin
1a175e7b56
Fix wrong HTTP/1 timeout being used in some cases
Added many tests to ensure the right timeout is picked in
the appropriate situation. Should there be any issues
remaining we can add more tests.
2024-01-15 15:18:40 +01:00
Loïc Hoguin
906a7ffc3c
Better error message when trying to reply twice
Also crash if trying to push after a reply was sent.
2024-01-09 13:06:11 +01:00
Loïc Hoguin
a40bab8fb3
Improve the error when trying to send a 204/304 with a body 2024-01-09 10:59:40 +01:00
Loïc Hoguin
e4a78aaeb1
Document body reading in auto mode
It is now tested both via cowboy_req:read_body and
via cowboy_req:cast.

Removes a bad example from the guide of body reading
with period of infinity, which does not work.
2024-01-08 15:13:18 +01:00
Loïc Hoguin
c1490d7d55
Ensure HTTP/1.1 Websocket resets the trap_exit flag
While we are identified as a supervisor in the tree,
we no longer manage children processes at that point,
so do not need to trap exit signals. Users can still
enable trap_exit if they prefer to.
2024-01-08 11:47:59 +01:00
Loïc Hoguin
9784179498
Always add vary: accept-encoding in cowboy_compress_h
We must add it even if we don't end up compressing because
it indicates that we might. This indication doesn't mean
that the user agent's accept-encoding values will ever
result in content encoding being applied.
2024-01-08 10:22:24 +01:00
Loïc Hoguin
6ef79ae410
Reject HTTP/1 requests with both content-length and transfer-encoding
The previous behavior was to accept them and drop the
content-length header as per the RFC recommendation.
But since this behavior is not normal it is safer to
just reject such requests than risk security issues.
2024-01-05 16:32:59 +01:00
Loïc Hoguin
5b2f600036
Don't automatically compress when response has etag
In the cowboy_compress_h stream handler.

Otherwise this could cause issues with caching, with the
etag being the same for compressed/uncompressed content.

Users that wish to send etags AND compress will have to
do it manually for the time being.
2024-01-05 15:53:42 +01:00
Loïc Hoguin
67df6fedae
Add cowboy:get_env/2,3 2024-01-05 12:31:48 +01:00
Loïc Hoguin
fd9711d949
Rework and improve the decompress stream handler
The read buffer was changed into an iovec to avoid doing
too many binary concatenations and allocations.

Decompression happens transparently: when decoding gzip,
the content-encoding header is removed (we only decode
when "gzip" is the only encoding so nothing remains).

We always add a content_decoded key to the Req object.
This key contains a list of codings that were decoded,
in the reverse order in which they were. Currently it
can only be empty or contain <<"gzip">> but future
improvements or user handlers may see it contain more
values.

The option to disable decompression was renamed to
decompress_enabled and defaults to true.

It is no longer possible to enable/disable decompression
in the middle of reading the body: this ensures that the
data we pass forward is always valid.

Various smaller improvements were made to the code,
tests and manual pages.
2024-01-04 15:50:12 +01:00
jdamanalo
3ed1b24dd6
Add cowboy_decompress_h stream handler 2023-12-21 15:39:08 +01:00
Nelson Vides
5ef64557b5
Exit gracefully on {error,closed} when reading the PROXY header
LH: Simplified the test a little.
2023-12-21 15:01:33 +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
627a4508b5
Explicitly close the socket in some tests for speed ups
The socket staying open meant that the graceful shut down
of the Cowboy listeners were waiting for the connections
to be closed gracefully (or a timeout). Closing explicitly
where it makes sense ensures we don't unnecessarily wait.

This commit removes a full minute in the run time of all
Cowboy test suites (minus examples).
2023-12-18 18:17:09 +01:00
Dmitri Vereshchagin
2558ba65ad
Fix shutdown for HTTP/1.1 pipeline
Sending extra response prevented by terminating all streams except
the one currently executing.

LH: Reworded some variables to make what happens more obvious.
2023-12-18 15:39:39 +01:00
Boris Pozdnyakov
e200272178
Reject invalid Connection header
LH: Small tweaks and added an HTTP/1.0 test.
2023-12-15 17:12:37 +01:00
Loïc Hoguin
1547e9b93e
Increase loop_handler_timeout timeouts
It seems that macOS GH runners don't do timeouts well.
2023-12-15 16:22:06 +01:00
jdamanalo
a81dc8af9d
Add timeout to cowboy_loop
LH: I have added a test that does both hibernate and timeout
    and fixed a related issue. I also tweaked the docs and tests.
2023-12-15 15:37:34 +01:00
Loïc Hoguin
a72bf4105f
Fix static_handler suite code path
A future OTP release will use 'strict' code path by default.
This change ensures it works both for old and new OTP.
2023-12-15 10:35:37 +01:00
Loïc Hoguin
67bd791dcc
Change send_timeout_close test to accomodate macOS 2023-12-14 15:25:25 +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
Sergei Shuvatov
3f5f326b73
Add test for send_timeout_close
LH: I reworked the test a little and added the same test
for HTTP/2 so that both HTTP/1.1 and HTTP/2 get the issue
fixed.
2023-12-12 15:05:01 +01:00
Loïc Hoguin
0ce9696e5e
Note that we won't implement the HTTP/2 PRIORITY mechanism 2023-12-07 16:45:30 +01:00
lin
4f26d6a573
Add UTF-8 support to example file_server
LH: I have fixed issues in the PR and incorporated changes
from a sister PR by @djankovic (git author: Dom J). I also
made sure the UTF-8 files were readable without Chinese
fonts and added their downloading to the examples test suite.
2023-12-07 15:31:11 +01:00
Kian-Meng, Ang
b12b4300ba
Fix typos in documentation 2023-12-06 18:46:56 +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