You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@couchdb.apache.org by wi...@apache.org on 2020/01/13 19:42:01 UTC
[couchdb-mochiweb] 02/37: Allow setting {buffer,
Buffer} socket server option
This is an automated email from the ASF dual-hosted git repository.
willholley pushed a commit to branch upstream
in repository https://gitbox.apache.org/repos/asf/couchdb-mochiweb.git
commit 7e6f4ef7782f2e7403768732225673df820aa456
Author: Nick Vatamaniuc <va...@apache.org>
AuthorDate: Thu Dec 13 17:48:08 2018 -0500
Allow setting {buffer, Buffer} socket server option
If `recbuf` is `undefined` then buffer size is set explicitly to the
previous `recbuf` default of 8192.
The recent option `{recbuf, undefined}` to allow sockets to pick up optimal OS
default kernel buffer sizes inadvertently reset Erlang's userland buffer size
to a default value of 1460. This buffer size, due to a longstanding bug in
Erlang http parser, limits the maximum URL line that can be parsed by the
{packet, http} socket option, and so breaks existing code.
For example this shows how recbuf also sets buffer size:
```
> f(), SockInfo = fun(S) -> inet:getopts(S, [recbuf, buffer]) end, {ok, LS} = gen_tcp:listen(0, [{recbuf, 8192}]), LRes = SockInfo(LS).
{ok,[{recbuf,8192},{buffer,8192}]}
```
Not setting recbuf resets buffer size to 1460:
```
f(), SockInfo = fun(S) -> inet:getopts(S, [recbuf, buffer]) end, {ok, LS} = gen_tcp:listen(0, []), LRes = SockInfo(LS).
{ok,[{recbuf,131072},{buffer,1460}]}
```
References:
https://github.com/apache/couchdb/issues/1810
http://erlang.org/pipermail/erlang-questions/2011-June/059571.html
http://erlang.org/doc/man/inet.html#setopts-2
---
src/mochiweb_socket_server.erl | 46 +++++++++++++++++++++++++++++++++++-------
1 file changed, 39 insertions(+), 7 deletions(-)
diff --git a/src/mochiweb_socket_server.erl b/src/mochiweb_socket_server.erl
index 7a9b0d3..56c1243 100644
--- a/src/mochiweb_socket_server.erl
+++ b/src/mochiweb_socket_server.erl
@@ -23,6 +23,7 @@
listen=null,
nodelay=false,
recbuf=?RECBUF_SIZE,
+ buffer=undefined,
backlog=128,
active_sockets=0,
acceptor_pool_size=16,
@@ -132,6 +133,15 @@ parse_options([{recbuf, RecBuf} | Rest], State) when is_integer(RecBuf) orelse
%% In case undefined is passed instead of the default buffer
%% size ?RECBUF_SIZE, no size is set and the OS can control it dynamically
parse_options(Rest, State#mochiweb_socket_server{recbuf=RecBuf});
+parse_options([{buffer, Buffer} | Rest], State) when is_integer(Buffer) orelse
+ Buffer == undefined ->
+ %% `buffer` sets Erlang's userland socket buffer size. The size of this
+ %% buffer affects the maximum URL path that can be parsed. URL sizes that
+ %% are larger than this plus the size of the HTTP verb and some whitespace
+ %% will result in an `emsgsize` TCP error.
+ %%
+ %% If this value is not set Erlang sets it to 1460 which might be too low.
+ parse_options(Rest, State#mochiweb_socket_server{buffer=Buffer});
parse_options([{acceptor_pool_size, Max} | Rest], State) ->
MaxInt = ensure_int(Max),
parse_options(Rest,
@@ -179,7 +189,8 @@ ipv6_supported() ->
end.
init(State=#mochiweb_socket_server{ip=Ip, port=Port, backlog=Backlog,
- nodelay=NoDelay, recbuf=RecBuf}) ->
+ nodelay=NoDelay, recbuf=RecBuf,
+ buffer=Buffer}) ->
process_flag(trap_exit, true),
BaseOpts = [binary,
@@ -200,14 +211,28 @@ init(State=#mochiweb_socket_server{ip=Ip, port=Port, backlog=Backlog,
{_, _, _, _, _, _, _, _} -> % IPv6
[inet6, {ip, Ip} | BaseOpts]
end,
- OptsBuf=case RecBuf of
- undefined ->
- Opts;
- _ ->
- [{recbuf, RecBuf}|Opts]
- end,
+ OptsBuf = set_buffer_opts(RecBuf, Buffer, Opts),
listen(Port, OptsBuf, State).
+
+set_buffer_opts(undefined, undefined, Opts) ->
+ % If recbuf is undefined, user space buffer is set to the default 1460
+ % value. That unexpectedly break the {packet, http} parser and any URL
+ % lines longer than 1460 would error out with emsgsize. So when recbuf is
+ % undefined, use previous value of recbuf for buffer in order to keep older
+ % code from breaking.
+ [{buffer, ?RECBUF_SIZE} | Opts];
+set_buffer_opts(RecBuf, undefined, Opts) ->
+ [{recbuf, RecBuf} | Opts];
+set_buffer_opts(undefined, Buffer, Opts) ->
+ [{buffer, Buffer} | Opts];
+set_buffer_opts(RecBuf, Buffer, Opts) ->
+ % Note: order matters, recbuf will override buffer unless buffer value
+ % comes first, except on older versions of Erlang (ex. 17.0) where it works
+ % exactly the opposite.
+ [{buffer, Buffer}, {recbuf, RecBuf} | Opts].
+
+
new_acceptor_pool(State=#mochiweb_socket_server{acceptor_pool_size=Size}) ->
lists:foldl(fun (_, S) -> new_acceptor(S) end, State, lists:seq(1, Size)).
@@ -391,4 +416,11 @@ upgrade_state_test() ->
profile_fun=undefined},
?assertEqual(CmpState, State).
+
+set_buffer_opts_test() ->
+ ?assertEqual([{buffer, 8192}], set_buffer_opts(undefined, undefined, [])),
+ ?assertEqual([{recbuf, 5}], set_buffer_opts(5, undefined, [])),
+ ?assertEqual([{buffer, 6}], set_buffer_opts(undefined, 6, [])),
+ ?assertEqual([{buffer, 6}, {recbuf, 5}], set_buffer_opts(5, 6, [])).
+
-endif.