Add recbuf config option.
Project: http://git-wip-us.apache.org/repos/asf/couchdb-mochiweb/repo Commit: http://git-wip-us.apache.org/repos/asf/couchdb-mochiweb/commit/ae0b9aad Tree: http://git-wip-us.apache.org/repos/asf/couchdb-mochiweb/tree/ae0b9aad Diff: http://git-wip-us.apache.org/repos/asf/couchdb-mochiweb/diff/ae0b9aad Branch: refs/heads/upstream Commit: ae0b9aadead08b87a59e83bd8cce62ea40ab5bb2 Parents: 66a6535 Author: Churikov Daniil <[email protected]> Authored: Fri Sep 5 18:20:31 2014 +0400 Committer: Daniil Churikov <[email protected]> Committed: Tue Nov 18 14:01:59 2014 +0300 ---------------------------------------------------------------------- src/mochiweb.erl | 7 +- src/mochiweb_acceptor.erl | 29 ++++--- src/mochiweb_http.erl | 54 ++++++------ src/mochiweb_multipart.erl | 18 ++-- src/mochiweb_request.erl | 133 +++++++++++++++-------------- src/mochiweb_socket.erl | 7 +- src/mochiweb_socket_server.erl | 40 +++++++-- test/mochiweb_socket_server_tests.erl | 2 +- 8 files changed, 169 insertions(+), 121 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/couchdb-mochiweb/blob/ae0b9aad/src/mochiweb.erl ---------------------------------------------------------------------- diff --git a/src/mochiweb.erl b/src/mochiweb.erl index 927322d..5c4201c 100644 --- a/src/mochiweb.erl +++ b/src/mochiweb.erl @@ -51,10 +51,15 @@ uri({scheme, Hostname, Port}) -> uri(HttpString) when is_list(HttpString) -> HttpString. -%% @spec new_request({Socket, Request, Headers}) -> MochiWebRequest +%% @spec new_request( {Socket, Request, Headers} +%% | {Socket, Opts, Request, Headers} ) -> MochiWebRequest %% @doc Return a mochiweb_request data structure. new_request({Socket, {Method, HttpUri, Version}, Headers}) -> + new_request({Socket, [], {Method, HttpUri, Version}, Headers}); + +new_request({Socket, Opts, {Method, HttpUri, Version}, Headers}) -> mochiweb_request:new(Socket, + Opts, Method, uri(HttpUri), Version, http://git-wip-us.apache.org/repos/asf/couchdb-mochiweb/blob/ae0b9aad/src/mochiweb_acceptor.erl ---------------------------------------------------------------------- diff --git a/src/mochiweb_acceptor.erl b/src/mochiweb_acceptor.erl index ebbaf45..208f861 100644 --- a/src/mochiweb_acceptor.erl +++ b/src/mochiweb_acceptor.erl @@ -8,21 +8,24 @@ -include("internal.hrl"). --export([start_link/3, init/3]). +-export([start_link/3, start_link/4, init/4]). start_link(Server, Listen, Loop) -> - proc_lib:spawn_link(?MODULE, init, [Server, Listen, Loop]). + start_link(Server, Listen, Loop, []). -init(Server, Listen, Loop) -> +start_link(Server, Listen, Loop, Opts) -> + proc_lib:spawn_link(?MODULE, init, [Server, Listen, Loop, Opts]). + +init(Server, Listen, Loop, Opts) -> T1 = os:timestamp(), case catch mochiweb_socket:accept(Listen) of {ok, Socket} -> gen_server:cast(Server, {accepted, self(), timer:now_diff(os:timestamp(), T1)}), - call_loop(Loop, Socket); + call_loop(Loop, Socket, Opts); {error, closed} -> exit(normal); {error, timeout} -> - init(Server, Listen, Loop); + init(Server, Listen, Loop, Opts); {error, esslaccept} -> exit(normal); Other -> @@ -33,14 +36,14 @@ init(Server, Listen, Loop) -> exit({error, accept_failed}) end. -call_loop({M, F}, Socket) -> - M:F(Socket); -call_loop({M, F, [A1]}, Socket) -> - M:F(Socket, A1); -call_loop({M, F, A}, Socket) -> - erlang:apply(M, F, [Socket | A]); -call_loop(Loop, Socket) -> - Loop(Socket). +call_loop({M, F}, Socket, Opts) -> + M:F(Socket, Opts); +call_loop({M, F, [A1]}, Socket, Opts) -> + M:F(Socket, Opts, A1); +call_loop({M, F, A}, Socket, Opts) -> + erlang:apply(M, F, [Socket, Opts | A]); +call_loop(Loop, Socket, Opts) -> + Loop(Socket, Opts). %% %% Tests http://git-wip-us.apache.org/repos/asf/couchdb-mochiweb/blob/ae0b9aad/src/mochiweb_http.erl ---------------------------------------------------------------------- diff --git a/src/mochiweb_http.erl b/src/mochiweb_http.erl index 38d51d4..e3c56fa 100644 --- a/src/mochiweb_http.erl +++ b/src/mochiweb_http.erl @@ -6,7 +6,7 @@ -module(mochiweb_http). -author('[email protected]'). -export([start/1, start_link/1, stop/0, stop/1]). --export([loop/2]). +-export([loop/3]). -export([after_response/2, reentry/1]). -export([parse_range_request/1, range_skip_length/2]). @@ -40,7 +40,7 @@ stop(Name) -> %% Option = {name, atom()} | {ip, string() | tuple()} | {backlog, integer()} %% | {nodelay, boolean()} | {acceptor_pool_size, integer()} %% | {ssl, boolean()} | {profile_fun, undefined | (Props) -> ok} -%% | {link, false} +%% | {link, false} | {recbuf, non_negative_integer()} %% @doc Start a mochiweb server. %% profile_fun is used to profile accept timing. %% After each accept, if defined, profile_fun is called with a proplist of a subset of the mochiweb_socket_server state and timing information. @@ -52,20 +52,20 @@ start(Options) -> start_link(Options) -> mochiweb_socket_server:start_link(parse_options(Options)). -loop(Socket, Body) -> +loop(Socket, Opts, Body) -> ok = mochiweb_socket:setopts(Socket, [{packet, http}]), - request(Socket, Body). + request(Socket, Opts, Body). -request(Socket, Body) -> +request(Socket, Opts, Body) -> ok = mochiweb_socket:setopts(Socket, [{active, once}]), receive {Protocol, _, {http_request, Method, Path, Version}} when Protocol == http orelse Protocol == ssl -> ok = mochiweb_socket:setopts(Socket, [{packet, httph}]), - headers(Socket, {Method, Path, Version}, [], Body, 0); + headers(Socket, Opts, {Method, Path, Version}, [], Body, 0); {Protocol, _, {http_error, "\r\n"}} when Protocol == http orelse Protocol == ssl -> - request(Socket, Body); + request(Socket, Opts, Body); {Protocol, _, {http_error, "\n"}} when Protocol == http orelse Protocol == ssl -> - request(Socket, Body); + request(Socket, Opts, Body); {tcp_closed, _} -> mochiweb_socket:close(Socket), exit(normal); @@ -73,7 +73,7 @@ request(Socket, Body) -> mochiweb_socket:close(Socket), exit(normal); Other -> - handle_invalid_msg_request(Other, Socket) + handle_invalid_msg_request(Other, Socket, Opts) after ?REQUEST_RECV_TIMEOUT -> mochiweb_socket:close(Socket), exit(normal) @@ -84,25 +84,25 @@ reentry(Body) -> ?MODULE:after_response(Body, Req) end. -headers(Socket, Request, Headers, _Body, ?MAX_HEADERS) -> +headers(Socket, Opts, Request, Headers, _Body, ?MAX_HEADERS) -> %% Too many headers sent, bad request. ok = mochiweb_socket:setopts(Socket, [{packet, raw}]), - handle_invalid_request(Socket, Request, Headers); -headers(Socket, Request, Headers, Body, HeaderCount) -> + handle_invalid_request(Socket, Opts, Request, Headers); +headers(Socket, Opts, Request, Headers, Body, HeaderCount) -> ok = mochiweb_socket:setopts(Socket, [{active, once}]), receive {Protocol, _, http_eoh} when Protocol == http orelse Protocol == ssl -> - Req = new_request(Socket, Request, Headers), + Req = new_request(Socket, Opts, Request, Headers), call_body(Body, Req), ?MODULE:after_response(Body, Req); {Protocol, _, {http_header, _, Name, _, Value}} when Protocol == http orelse Protocol == ssl -> - headers(Socket, Request, [{Name, Value} | Headers], Body, + headers(Socket, Opts, Request, [{Name, Value} | Headers], Body, 1 + HeaderCount); {tcp_closed, _} -> mochiweb_socket:close(Socket), exit(normal); Other -> - handle_invalid_msg_request(Other, Socket, Request, Headers) + handle_invalid_msg_request(Other, Socket, Opts, Request, Headers) after ?HEADERS_RECV_TIMEOUT -> mochiweb_socket:close(Socket), exit(normal) @@ -115,31 +115,31 @@ call_body({M, F}, Req) -> call_body(Body, Req) -> Body(Req). --spec handle_invalid_msg_request(term(), term()) -> no_return(). -handle_invalid_msg_request(Msg, Socket) -> - handle_invalid_msg_request(Msg, Socket, {'GET', {abs_path, "/"}, {0,9}}, []). +-spec handle_invalid_msg_request(term(), term(), term()) -> no_return(). +handle_invalid_msg_request(Msg, Socket, Opts) -> + handle_invalid_msg_request(Msg, Socket, Opts, {'GET', {abs_path, "/"}, {0,9}}, []). --spec handle_invalid_msg_request(term(), term(), term(), term()) -> no_return(). -handle_invalid_msg_request(Msg, Socket, Request, RevHeaders) -> +-spec handle_invalid_msg_request(term(), term(), term(), term(), term()) -> no_return(). +handle_invalid_msg_request(Msg, Socket, Opts, Request, RevHeaders) -> case {Msg, r15b_workaround()} of {{tcp_error,_,emsgsize}, true} -> %% R15B02 returns this then closes the socket, so close and exit mochiweb_socket:close(Socket), exit(normal); _ -> - handle_invalid_request(Socket, Request, RevHeaders) + handle_invalid_request(Socket, Opts, Request, RevHeaders) end. --spec handle_invalid_request(term(), term(), term()) -> no_return(). -handle_invalid_request(Socket, Request, RevHeaders) -> - Req = new_request(Socket, Request, RevHeaders), +-spec handle_invalid_request(term(), term(), term(), term()) -> no_return(). +handle_invalid_request(Socket, Opts, Request, RevHeaders) -> + Req = new_request(Socket, Opts, Request, RevHeaders), Req:respond({400, [], []}), mochiweb_socket:close(Socket), exit(normal). -new_request(Socket, Request, RevHeaders) -> +new_request(Socket, Opts, Request, RevHeaders) -> ok = mochiweb_socket:setopts(Socket, [{packet, raw}]), - mochiweb:new_request({Socket, Request, lists:reverse(RevHeaders)}). + mochiweb:new_request({Socket, Opts, Request, lists:reverse(RevHeaders)}). after_response(Body, Req) -> Socket = Req:get(socket), @@ -150,7 +150,7 @@ after_response(Body, Req) -> false -> Req:cleanup(), erlang:garbage_collect(), - ?MODULE:loop(Socket, Body) + ?MODULE:loop(Socket, mochiweb_request:get(opts, Req), Body) end. parse_range_request("bytes=0-") -> http://git-wip-us.apache.org/repos/asf/couchdb-mochiweb/blob/ae0b9aad/src/mochiweb_multipart.erl ---------------------------------------------------------------------- diff --git a/src/mochiweb_multipart.erl b/src/mochiweb_multipart.erl index a83a88c..90bc949 100644 --- a/src/mochiweb_multipart.erl +++ b/src/mochiweb_multipart.erl @@ -374,7 +374,7 @@ parse3(Transport) -> body_end, eof], TestCallback = fun (Next) -> test_callback(Next, Expect) end, - ServerFun = fun (Socket) -> + ServerFun = fun (Socket, _Opts) -> ok = mochiweb_socket:send(Socket, BinContent), exit(normal) end, @@ -410,7 +410,7 @@ parse2(Transport) -> body_end, eof], TestCallback = fun (Next) -> test_callback(Next, Expect) end, - ServerFun = fun (Socket) -> + ServerFun = fun (Socket, _Opts) -> ok = mochiweb_socket:send(Socket, BinContent), exit(normal) end, @@ -447,7 +447,7 @@ do_parse_form(Transport) -> "--AaB03x--", ""], "\r\n"), BinContent = iolist_to_binary(Content), - ServerFun = fun (Socket) -> + ServerFun = fun (Socket, _Opts) -> ok = mochiweb_socket:send(Socket, BinContent), exit(normal) end, @@ -500,7 +500,7 @@ do_parse(Transport) -> body_end, eof], TestCallback = fun (Next) -> test_callback(Next, Expect) end, - ServerFun = fun (Socket) -> + ServerFun = fun (Socket, _Opts) -> ok = mochiweb_socket:send(Socket, BinContent), exit(normal) end, @@ -552,7 +552,7 @@ parse_partial_body_boundary(Transport) -> body_end, eof], TestCallback = fun (Next) -> test_callback(Next, Expect) end, - ServerFun = fun (Socket) -> + ServerFun = fun (Socket, _Opts) -> ok = mochiweb_socket:send(Socket, BinContent), exit(normal) end, @@ -605,7 +605,7 @@ parse_large_header(Transport) -> body_end, eof], TestCallback = fun (Next) -> test_callback(Next, Expect) end, - ServerFun = fun (Socket) -> + ServerFun = fun (Socket, _Opts) -> ok = mochiweb_socket:send(Socket, BinContent), exit(normal) end, @@ -681,7 +681,7 @@ flash_parse(Transport) -> body_end, eof], TestCallback = fun (Next) -> test_callback(Next, Expect) end, - ServerFun = fun (Socket) -> + ServerFun = fun (Socket, _Opts) -> ok = mochiweb_socket:send(Socket, BinContent), exit(normal) end, @@ -729,7 +729,7 @@ flash_parse2(Transport) -> body_end, eof], TestCallback = fun (Next) -> test_callback(Next, Expect) end, - ServerFun = fun (Socket) -> + ServerFun = fun (Socket, _Opts) -> ok = mochiweb_socket:send(Socket, BinContent), exit(normal) end, @@ -856,7 +856,7 @@ multipart_parsing_benchmark() -> body_end, eof], TestCallback = fun (Next) -> test_callback(Next, Expect) end, - ServerFun = fun (Socket) -> + ServerFun = fun (Socket, _Opts) -> ok = mochiweb_socket:send(Socket, BinContent), exit(normal) end, http://git-wip-us.apache.org/repos/asf/couchdb-mochiweb/blob/ae0b9aad/src/mochiweb_request.erl ---------------------------------------------------------------------- diff --git a/src/mochiweb_request.erl b/src/mochiweb_request.erl index f8ba7a3..9622926 100644 --- a/src/mochiweb_request.erl +++ b/src/mochiweb_request.erl @@ -11,7 +11,7 @@ -define(QUIP, "Any of you quaids got a smint?"). --export([new/5]). +-export([new/5, new/6]). -export([get_header_value/2, get_primary_header_value/2, get_combined_header_value/2, get/2, dump/1]). -export([send/2, recv/2, recv/3, recv_body/1, recv_body/2, stream_body/4]). -export([start_response/2, start_response_length/2, start_raw_response/2]). @@ -49,17 +49,22 @@ %% @spec new(Socket, Method, RawPath, Version, headers()) -> request() %% @doc Create a new request instance. new(Socket, Method, RawPath, Version, Headers) -> - {?MODULE, [Socket, Method, RawPath, Version, Headers]}. + new(Socket, [], Method, RawPath, Version, Headers). + +%% @spec new(Socket, Opts, Method, RawPath, Version, headers()) -> request() +%% @doc Create a new request instance. +new(Socket, Opts, Method, RawPath, Version, Headers) -> + {?MODULE, [Socket, Opts, Method, RawPath, Version, Headers]}. %% @spec get_header_value(K, request()) -> undefined | Value %% @doc Get the value of a given request header. -get_header_value(K, {?MODULE, [_Socket, _Method, _RawPath, _Version, Headers]}) -> +get_header_value(K, {?MODULE, [_Socket, _Opts, _Method, _RawPath, _Version, Headers]}) -> mochiweb_headers:get_value(K, Headers). -get_primary_header_value(K, {?MODULE, [_Socket, _Method, _RawPath, _Version, Headers]}) -> +get_primary_header_value(K, {?MODULE, [_Socket, _Opts, _Method, _RawPath, _Version, Headers]}) -> mochiweb_headers:get_primary_value(K, Headers). -get_combined_header_value(K, {?MODULE, [_Socket, _Method, _RawPath, _Version, Headers]}) -> +get_combined_header_value(K, {?MODULE, [_Socket, _Opts, _Method, _RawPath, _Version, Headers]}) -> mochiweb_headers:get_combined_value(K, Headers). %% @type field() = socket | scheme | method | raw_path | version | headers | peer | path | body_length | range @@ -70,24 +75,24 @@ get_combined_header_value(K, {?MODULE, [_Socket, _Method, _RawPath, _Version, He %% an ssl socket will be returned as <code>{ssl, SslSocket}</code>. %% You can use <code>SslSocket</code> with the <code>ssl</code> %% application, eg: <code>ssl:peercert(SslSocket)</code>. -get(socket, {?MODULE, [Socket, _Method, _RawPath, _Version, _Headers]}) -> +get(socket, {?MODULE, [Socket, _Opts, _Method, _RawPath, _Version, _Headers]}) -> Socket; -get(scheme, {?MODULE, [Socket, _Method, _RawPath, _Version, _Headers]}) -> +get(scheme, {?MODULE, [Socket, _Opts, _Method, _RawPath, _Version, _Headers]}) -> case mochiweb_socket:type(Socket) of plain -> http; ssl -> https end; -get(method, {?MODULE, [_Socket, Method, _RawPath, _Version, _Headers]}) -> +get(method, {?MODULE, [_Socket, _Opts, Method, _RawPath, _Version, _Headers]}) -> Method; -get(raw_path, {?MODULE, [_Socket, _Method, RawPath, _Version, _Headers]}) -> +get(raw_path, {?MODULE, [_Socket, _Opts, _Method, RawPath, _Version, _Headers]}) -> RawPath; -get(version, {?MODULE, [_Socket, _Method, _RawPath, Version, _Headers]}) -> +get(version, {?MODULE, [_Socket, _Opts, _Method, _RawPath, Version, _Headers]}) -> Version; -get(headers, {?MODULE, [_Socket, _Method, _RawPath, _Version, Headers]}) -> +get(headers, {?MODULE, [_Socket, _Opts, _Method, _RawPath, _Version, Headers]}) -> Headers; -get(peer, {?MODULE, [Socket, _Method, _RawPath, _Version, _Headers]}=THIS) -> +get(peer, {?MODULE, [Socket, _Opts, _Method, _RawPath, _Version, _Headers]}=THIS) -> case mochiweb_socket:peername(Socket) of {ok, {Addr={10, _, _, _}, _Port}} -> case get_header_value("x-forwarded-for", THIS) of @@ -108,7 +113,7 @@ get(peer, {?MODULE, [Socket, _Method, _RawPath, _Version, _Headers]}=THIS) -> {error, enotconn} -> exit(normal) end; -get(path, {?MODULE, [_Socket, _Method, RawPath, _Version, _Headers]}) -> +get(path, {?MODULE, [_Socket, _Opts, _Method, RawPath, _Version, _Headers]}) -> case erlang:get(?SAVE_PATH) of undefined -> {Path0, _, _} = mochiweb_util:urlsplit_path(RawPath), @@ -118,7 +123,7 @@ get(path, {?MODULE, [_Socket, _Method, RawPath, _Version, _Headers]}) -> Cached -> Cached end; -get(body_length, {?MODULE, [_Socket, _Method, _RawPath, _Version, _Headers]}=THIS) -> +get(body_length, {?MODULE, [_Socket, _Opts, _Method, _RawPath, _Version, _Headers]}=THIS) -> case erlang:get(?SAVE_BODY_LENGTH) of undefined -> BodyLength = body_length(THIS), @@ -127,26 +132,29 @@ get(body_length, {?MODULE, [_Socket, _Method, _RawPath, _Version, _Headers]}=THI {cached, Cached} -> Cached end; -get(range, {?MODULE, [_Socket, _Method, _RawPath, _Version, _Headers]}=THIS) -> +get(range, {?MODULE, [_Socket, _Opts, _Method, _RawPath, _Version, _Headers]}=THIS) -> case get_header_value(range, THIS) of undefined -> undefined; RawRange -> mochiweb_http:parse_range_request(RawRange) - end. + end; +get(opts, {?MODULE, [_Socket, Opts, _Method, _RawPath, _Version, _Headers]}) -> + Opts. %% @spec dump(request()) -> {mochiweb_request, [{atom(), term()}]} %% @doc Dump the internal representation to a "human readable" set of terms %% for debugging/inspection purposes. -dump({?MODULE, [_Socket, Method, RawPath, Version, Headers]}) -> +dump({?MODULE, [_Socket, Opts, Method, RawPath, Version, Headers]}) -> {?MODULE, [{method, Method}, {version, Version}, {raw_path, RawPath}, + {opts, Opts}, {headers, mochiweb_headers:to_list(Headers)}]}. %% @spec send(iodata(), request()) -> ok %% @doc Send data over the socket. -send(Data, {?MODULE, [Socket, _Method, _RawPath, _Version, _Headers]}) -> +send(Data, {?MODULE, [Socket, _Opts, _Method, _RawPath, _Version, _Headers]}) -> case mochiweb_socket:send(Socket, Data) of ok -> ok; @@ -157,13 +165,13 @@ send(Data, {?MODULE, [Socket, _Method, _RawPath, _Version, _Headers]}) -> %% @spec recv(integer(), request()) -> binary() %% @doc Receive Length bytes from the client as a binary, with the default %% idle timeout. -recv(Length, {?MODULE, [_Socket, _Method, _RawPath, _Version, _Headers]}=THIS) -> +recv(Length, {?MODULE, [_Socket, _Opts, _Method, _RawPath, _Version, _Headers]}=THIS) -> recv(Length, ?IDLE_TIMEOUT, THIS). %% @spec recv(integer(), integer(), request()) -> binary() %% @doc Receive Length bytes from the client as a binary, with the given %% Timeout in msec. -recv(Length, Timeout, {?MODULE, [Socket, _Method, _RawPath, _Version, _Headers]}) -> +recv(Length, Timeout, {?MODULE, [Socket, _Opts, _Method, _RawPath, _Version, _Headers]}) -> case mochiweb_socket:recv(Socket, Length, Timeout) of {ok, Data} -> put(?SAVE_RECV, true), @@ -174,7 +182,7 @@ recv(Length, Timeout, {?MODULE, [Socket, _Method, _RawPath, _Version, _Headers]} %% @spec body_length(request()) -> undefined | chunked | unknown_transfer_encoding | integer() %% @doc Infer body length from transfer-encoding and content-length headers. -body_length({?MODULE, [_Socket, _Method, _RawPath, _Version, _Headers]}=THIS) -> +body_length({?MODULE, [_Socket, _Opts, _Method, _RawPath, _Version, _Headers]}=THIS) -> case get_header_value("transfer-encoding", THIS) of undefined -> case get_combined_header_value("content-length", THIS) of @@ -193,13 +201,13 @@ body_length({?MODULE, [_Socket, _Method, _RawPath, _Version, _Headers]}=THIS) -> %% @spec recv_body(request()) -> binary() %% @doc Receive the body of the HTTP request (defined by Content-Length). %% Will only receive up to the default max-body length of 1MB. -recv_body({?MODULE, [_Socket, _Method, _RawPath, _Version, _Headers]}=THIS) -> +recv_body({?MODULE, [_Socket, _Opts, _Method, _RawPath, _Version, _Headers]}=THIS) -> recv_body(?MAX_RECV_BODY, THIS). %% @spec recv_body(integer(), request()) -> binary() %% @doc Receive the body of the HTTP request (defined by Content-Length). %% Will receive up to MaxBody bytes. -recv_body(MaxBody, {?MODULE, [_Socket, _Method, _RawPath, _Version, _Headers]}=THIS) -> +recv_body(MaxBody, {?MODULE, [_Socket, _Opts, _Method, _RawPath, _Version, _Headers]}=THIS) -> case erlang:get(?SAVE_BODY) of undefined -> % we could use a sane constant for max chunk size @@ -219,11 +227,11 @@ recv_body(MaxBody, {?MODULE, [_Socket, _Method, _RawPath, _Version, _Headers]}=T Cached -> Cached end. -stream_body(MaxChunkSize, ChunkFun, FunState, {?MODULE,[_Socket,_Method,_RawPath,_Version,_Headers]}=THIS) -> +stream_body(MaxChunkSize, ChunkFun, FunState, {?MODULE,[_Socket,_Opts,_Method,_RawPath,_Version,_Headers]}=THIS) -> stream_body(MaxChunkSize, ChunkFun, FunState, undefined, THIS). stream_body(MaxChunkSize, ChunkFun, FunState, MaxBodyLength, - {?MODULE, [_Socket, _Method, _RawPath, _Version, _Headers]}=THIS) -> + {?MODULE, [_Socket, _Opts, _Method, _RawPath, _Version, _Headers]}=THIS) -> Expect = case get_header_value("expect", THIS) of undefined -> undefined; @@ -263,13 +271,13 @@ stream_body(MaxChunkSize, ChunkFun, FunState, MaxBodyLength, %% @doc Start the HTTP response by sending the Code HTTP response and %% ResponseHeaders. The server will set header defaults such as Server %% and Date if not present in ResponseHeaders. -start_response({Code, ResponseHeaders}, {?MODULE, [_Socket, _Method, _RawPath, _Version, _Headers]}=THIS) -> +start_response({Code, ResponseHeaders}, {?MODULE, [_Socket, _Opts, _Method, _RawPath, _Version, _Headers]}=THIS) -> start_raw_response({Code, ResponseHeaders}, THIS). %% @spec start_raw_response({integer(), headers()}, request()) -> response() %% @doc Start the HTTP response by sending the Code HTTP response and %% ResponseHeaders. -start_raw_response({Code, ResponseHeaders}, {?MODULE, [_Socket, _Method, _RawPath, _Version, _Headers]}=THIS) -> +start_raw_response({Code, ResponseHeaders}, {?MODULE, [_Socket, _Opts, _Method, _RawPath, _Version, _Headers]}=THIS) -> {Header, Response} = format_response_header({Code, ResponseHeaders}, THIS), send(Header, THIS), Response. @@ -281,7 +289,7 @@ start_raw_response({Code, ResponseHeaders}, {?MODULE, [_Socket, _Method, _RawPat %% will set header defaults such as Server %% and Date if not present in ResponseHeaders. start_response_length({Code, ResponseHeaders, Length}, - {?MODULE, [_Socket, _Method, _RawPath, _Version, _Headers]}=THIS) -> + {?MODULE, [_Socket, _Opts, _Method, _RawPath, _Version, _Headers]}=THIS) -> HResponse = mochiweb_headers:make(ResponseHeaders), HResponse1 = mochiweb_headers:enter("Content-Length", Length, HResponse), start_response({Code, HResponse1}, THIS). @@ -291,7 +299,7 @@ start_response_length({Code, ResponseHeaders, Length}, %% ResponseHeaders including an optional Content-Length of Length. The server %% will set header defaults such as Server %% and Date if not present in ResponseHeaders. -format_response_header({Code, ResponseHeaders}, {?MODULE, [_Socket, _Method, _RawPath, Version, _Headers]}=THIS) -> +format_response_header({Code, ResponseHeaders}, {?MODULE, [_Socket, _Opts, _Method, _RawPath, Version, _Headers]}=THIS) -> HResponse = mochiweb_headers:make(ResponseHeaders), HResponse1 = mochiweb_headers:default_from_list(server_headers(), HResponse), F = fun ({K, V}, Acc) -> @@ -301,7 +309,7 @@ format_response_header({Code, ResponseHeaders}, {?MODULE, [_Socket, _Method, _Ra Response = mochiweb:new_response({THIS, Code, HResponse1}), {[make_version(Version), make_code(Code), <<"\r\n">> | End], Response}; format_response_header({Code, ResponseHeaders, Length}, - {?MODULE, [_Socket, _Method, _RawPath, _Version, _Headers]}=THIS) -> + {?MODULE, [_Socket, _Opts, _Method, _RawPath, _Version, _Headers]}=THIS) -> HResponse = mochiweb_headers:make(ResponseHeaders), HResponse1 = mochiweb_headers:enter("Content-Length", Length, HResponse), format_response_header({Code, HResponse1}, THIS). @@ -312,7 +320,7 @@ format_response_header({Code, ResponseHeaders, Length}, %% will be set by the Body length, and the server will insert header %% defaults. respond({Code, ResponseHeaders, {file, IoDevice}}, - {?MODULE, [_Socket, Method, _RawPath, _Version, _Headers]}=THIS) -> + {?MODULE, [_Socket, _Opts, Method, _RawPath, _Version, _Headers]}=THIS) -> Length = mochiweb_io:iodevice_size(IoDevice), Response = start_response_length({Code, ResponseHeaders, Length}, THIS), case Method of @@ -324,7 +332,7 @@ respond({Code, ResponseHeaders, {file, IoDevice}}, IoDevice) end, Response; -respond({Code, ResponseHeaders, chunked}, {?MODULE, [_Socket, Method, _RawPath, Version, _Headers]}=THIS) -> +respond({Code, ResponseHeaders, chunked}, {?MODULE, [_Socket, _Opts, Method, _RawPath, Version, _Headers]}=THIS) -> HResponse = mochiweb_headers:make(ResponseHeaders), HResponse1 = case Method of 'HEAD' -> @@ -346,7 +354,7 @@ respond({Code, ResponseHeaders, chunked}, {?MODULE, [_Socket, Method, _RawPath, HResponse end, start_response({Code, HResponse1}, THIS); -respond({Code, ResponseHeaders, Body}, {?MODULE, [_Socket, Method, _RawPath, _Version, _Headers]}=THIS) -> +respond({Code, ResponseHeaders, Body}, {?MODULE, [_Socket, _Opts, Method, _RawPath, _Version, _Headers]}=THIS) -> {Header, Response} = format_response_header({Code, ResponseHeaders, iolist_size(Body)}, THIS), case Method of 'HEAD' -> send(Header, THIS); @@ -356,22 +364,22 @@ respond({Code, ResponseHeaders, Body}, {?MODULE, [_Socket, Method, _RawPath, _Ve %% @spec not_found(request()) -> response() %% @doc Alias for <code>not_found([])</code>. -not_found({?MODULE, [_Socket, _Method, _RawPath, _Version, _Headers]}=THIS) -> +not_found({?MODULE, [_Socket, _Opts, _Method, _RawPath, _Version, _Headers]}=THIS) -> not_found([], THIS). %% @spec not_found(ExtraHeaders, request()) -> response() %% @doc Alias for <code>respond({404, [{"Content-Type", "text/plain"} %% | ExtraHeaders], <<"Not found.">>})</code>. -not_found(ExtraHeaders, {?MODULE, [_Socket, _Method, _RawPath, _Version, _Headers]}=THIS) -> +not_found(ExtraHeaders, {?MODULE, [_Socket, _Opts, _Method, _RawPath, _Version, _Headers]}=THIS) -> respond({404, [{"Content-Type", "text/plain"} | ExtraHeaders], <<"Not found.">>}, THIS). %% @spec ok({value(), iodata()} | {value(), ioheaders(), iodata() | {file, IoDevice}}, request()) -> %% response() %% @doc respond({200, [{"Content-Type", ContentType} | Headers], Body}). -ok({ContentType, Body}, {?MODULE, [_Socket, _Method, _RawPath, _Version, _Headers]}=THIS) -> +ok({ContentType, Body}, {?MODULE, [_Socket, _Opts, _Method, _RawPath, _Version, _Headers]}=THIS) -> ok({ContentType, [], Body}, THIS); -ok({ContentType, ResponseHeaders, Body}, {?MODULE, [_Socket, _Method, _RawPath, _Version, _Headers]}=THIS) -> +ok({ContentType, ResponseHeaders, Body}, {?MODULE, [_Socket, _Opts, _Method, _RawPath, _Version, _Headers]}=THIS) -> HResponse = mochiweb_headers:make(ResponseHeaders), case THIS:get(range) of X when (X =:= undefined orelse X =:= fail) orelse Body =:= chunked -> @@ -404,7 +412,7 @@ ok({ContentType, ResponseHeaders, Body}, {?MODULE, [_Socket, _Method, _RawPath, %% @spec should_close(request()) -> bool() %% @doc Return true if the connection must be closed. If false, using %% Keep-Alive should be safe. -should_close({?MODULE, [_Socket, _Method, _RawPath, Version, _Headers]}=THIS) -> +should_close({?MODULE, [_Socket, _Opts, _Method, _RawPath, Version, _Headers]}=THIS) -> ForceClose = erlang:get(?SAVE_FORCE_CLOSE) =/= undefined, DidNotRecv = erlang:get(?SAVE_RECV) =:= undefined, ForceClose orelse Version < {1, 0} @@ -430,7 +438,7 @@ is_close(_) -> %% @spec cleanup(request()) -> ok %% @doc Clean up any junk in the process dictionary, required before continuing %% a Keep-Alive request. -cleanup({?MODULE, [_Socket, _Method, _RawPath, _Version, _Headers]}) -> +cleanup({?MODULE, [_Socket, _Opts, _Method, _RawPath, _Version, _Headers]}) -> L = [?SAVE_QS, ?SAVE_PATH, ?SAVE_RECV, ?SAVE_BODY, ?SAVE_BODY_LENGTH, ?SAVE_POST, ?SAVE_COOKIE, ?SAVE_FORCE_CLOSE], lists:foreach(fun(K) -> @@ -440,7 +448,7 @@ cleanup({?MODULE, [_Socket, _Method, _RawPath, _Version, _Headers]}) -> %% @spec parse_qs(request()) -> [{Key::string(), Value::string()}] %% @doc Parse the query string of the URL. -parse_qs({?MODULE, [_Socket, _Method, RawPath, _Version, _Headers]}) -> +parse_qs({?MODULE, [_Socket, _Opts, _Method, RawPath, _Version, _Headers]}) -> case erlang:get(?SAVE_QS) of undefined -> {_, QueryString, _} = mochiweb_util:urlsplit_path(RawPath), @@ -453,12 +461,12 @@ parse_qs({?MODULE, [_Socket, _Method, RawPath, _Version, _Headers]}) -> %% @spec get_cookie_value(Key::string, request()) -> string() | undefined %% @doc Get the value of the given cookie. -get_cookie_value(Key, {?MODULE, [_Socket, _Method, _RawPath, _Version, _Headers]}=THIS) -> +get_cookie_value(Key, {?MODULE, [_Socket, _Opts, _Method, _RawPath, _Version, _Headers]}=THIS) -> proplists:get_value(Key, parse_cookie(THIS)). %% @spec parse_cookie(request()) -> [{Key::string(), Value::string()}] %% @doc Parse the cookie header. -parse_cookie({?MODULE, [_Socket, _Method, _RawPath, _Version, _Headers]}=THIS) -> +parse_cookie({?MODULE, [_Socket, _Opts, _Method, _RawPath, _Version, _Headers]}=THIS) -> case erlang:get(?SAVE_COOKIE) of undefined -> Cookies = case get_header_value("cookie", THIS) of @@ -476,7 +484,7 @@ parse_cookie({?MODULE, [_Socket, _Method, _RawPath, _Version, _Headers]}=THIS) - %% @spec parse_post(request()) -> [{Key::string(), Value::string()}] %% @doc Parse an application/x-www-form-urlencoded form POST. This %% has the side-effect of calling recv_body(). -parse_post({?MODULE, [_Socket, _Method, _RawPath, _Version, _Headers]}=THIS) -> +parse_post({?MODULE, [_Socket, _Opts, _Method, _RawPath, _Version, _Headers]}=THIS) -> case erlang:get(?SAVE_POST) of undefined -> Parsed = case recv_body(THIS) of @@ -500,7 +508,7 @@ parse_post({?MODULE, [_Socket, _Method, _RawPath, _Version, _Headers]}=THIS) -> %% @doc The function is called for each chunk. %% Used internally by read_chunked_body. stream_chunked_body(MaxChunkSize, Fun, FunState, - {?MODULE, [_Socket, _Method, _RawPath, _Version, _Headers]}=THIS) -> + {?MODULE, [_Socket, _Opts, _Method, _RawPath, _Version, _Headers]}=THIS) -> case read_chunk_length(THIS) of 0 -> Fun({0, read_chunk(0, THIS)}, FunState); @@ -512,13 +520,14 @@ stream_chunked_body(MaxChunkSize, Fun, FunState, stream_chunked_body(MaxChunkSize, Fun, NewState, THIS) end. -stream_unchunked_body(0, Fun, FunState, {?MODULE, [_Socket, _Method, _RawPath, _Version, _Headers]}) -> +stream_unchunked_body(0, Fun, FunState, {?MODULE, [_Socket, _Opts, _Method, _RawPath, _Version, _Headers]}) -> Fun({0, <<>>}, FunState); stream_unchunked_body(Length, Fun, FunState, - {?MODULE, [_Socket, _Method, _RawPath, _Version, _Headers]}=THIS) when Length > 0 -> - PktSize = case Length > ?RECBUF_SIZE of + {?MODULE, [_Socket, Opts, _Method, _RawPath, _Version, _Headers]}=THIS) when Length > 0 -> + RecBuf = mochilists:get_value(recbuf, Opts, ?RECBUF_SIZE), + PktSize = case Length > RecBuf of true -> - ?RECBUF_SIZE; + RecBuf; false -> Length end, @@ -528,7 +537,7 @@ stream_unchunked_body(Length, Fun, FunState, %% @spec read_chunk_length(request()) -> integer() %% @doc Read the length of the next HTTP chunk. -read_chunk_length({?MODULE, [Socket, _Method, _RawPath, _Version, _Headers]}) -> +read_chunk_length({?MODULE, [Socket, _Opts, _Method, _RawPath, _Version, _Headers]}) -> ok = mochiweb_socket:setopts(Socket, [{packet, line}]), case mochiweb_socket:recv(Socket, 0, ?IDLE_TIMEOUT) of {ok, Header} -> @@ -545,7 +554,7 @@ read_chunk_length({?MODULE, [Socket, _Method, _RawPath, _Version, _Headers]}) -> %% @spec read_chunk(integer(), request()) -> Chunk::binary() | [Footer::binary()] %% @doc Read in a HTTP chunk of the given length. If Length is 0, then read the %% HTTP footers (as a list of binaries, since they're nominal). -read_chunk(0, {?MODULE, [Socket, _Method, _RawPath, _Version, _Headers]}) -> +read_chunk(0, {?MODULE, [Socket, _Opts, _Method, _RawPath, _Version, _Headers]}) -> ok = mochiweb_socket:setopts(Socket, [{packet, line}]), F = fun (F1, Acc) -> case mochiweb_socket:recv(Socket, 0, ?IDLE_TIMEOUT) of @@ -561,7 +570,7 @@ read_chunk(0, {?MODULE, [Socket, _Method, _RawPath, _Version, _Headers]}) -> ok = mochiweb_socket:setopts(Socket, [{packet, raw}]), put(?SAVE_RECV, true), Footers; -read_chunk(Length, {?MODULE, [Socket, _Method, _RawPath, _Version, _Headers]}) -> +read_chunk(Length, {?MODULE, [Socket, _Opts, _Method, _RawPath, _Version, _Headers]}) -> case mochiweb_socket:recv(Socket, 2 + Length, ?IDLE_TIMEOUT) of {ok, <<Chunk:Length/binary, "\r\n">>} -> Chunk; @@ -570,23 +579,23 @@ read_chunk(Length, {?MODULE, [Socket, _Method, _RawPath, _Version, _Headers]}) - end. read_sub_chunks(Length, MaxChunkSize, Fun, FunState, - {?MODULE, [_Socket, _Method, _RawPath, _Version, _Headers]}=THIS) when Length > MaxChunkSize -> + {?MODULE, [_Socket, _Opts, _Method, _RawPath, _Version, _Headers]}=THIS) when Length > MaxChunkSize -> Bin = recv(MaxChunkSize, THIS), NewState = Fun({size(Bin), Bin}, FunState), read_sub_chunks(Length - MaxChunkSize, MaxChunkSize, Fun, NewState, THIS); read_sub_chunks(Length, _MaxChunkSize, Fun, FunState, - {?MODULE, [_Socket, _Method, _RawPath, _Version, _Headers]}=THIS) -> + {?MODULE, [_Socket, _Opts, _Method, _RawPath, _Version, _Headers]}=THIS) -> Fun({Length, read_chunk(Length, THIS)}, FunState). %% @spec serve_file(Path, DocRoot, request()) -> Response %% @doc Serve a file relative to DocRoot. -serve_file(Path, DocRoot, {?MODULE, [_Socket, _Method, _RawPath, _Version, _Headers]}=THIS) -> +serve_file(Path, DocRoot, {?MODULE, [_Socket, _Opts, _Method, _RawPath, _Version, _Headers]}=THIS) -> serve_file(Path, DocRoot, [], THIS). %% @spec serve_file(Path, DocRoot, ExtraHeaders, request()) -> Response %% @doc Serve a file relative to DocRoot. -serve_file(Path, DocRoot, ExtraHeaders, {?MODULE, [_Socket, _Method, _RawPath, _Version, _Headers]}=THIS) -> +serve_file(Path, DocRoot, ExtraHeaders, {?MODULE, [_Socket, _Opts, _Method, _RawPath, _Version, _Headers]}=THIS) -> case mochiweb_util:safe_relative_path(Path) of undefined -> not_found(ExtraHeaders, THIS); @@ -606,11 +615,11 @@ serve_file(Path, DocRoot, ExtraHeaders, {?MODULE, [_Socket, _Method, _RawPath, _ directory_index(FullPath) -> filename:join([FullPath, "index.html"]). -maybe_redirect([], FullPath, ExtraHeaders, {?MODULE, [_Socket, _Method, _RawPath, _Version, _Headers]}=THIS) -> +maybe_redirect([], FullPath, ExtraHeaders, {?MODULE, [_Socket, _Opts, _Method, _RawPath, _Version, _Headers]}=THIS) -> maybe_serve_file(directory_index(FullPath), ExtraHeaders, THIS); maybe_redirect(RelPath, FullPath, ExtraHeaders, - {?MODULE, [_Socket, _Method, _RawPath, _Version, Headers]}=THIS) -> + {?MODULE, [_Socket, _Opts, _Method, _RawPath, _Version, Headers]}=THIS) -> case string:right(RelPath, 1) of "/" -> maybe_serve_file(directory_index(FullPath), ExtraHeaders, THIS); @@ -631,7 +640,7 @@ maybe_redirect(RelPath, FullPath, ExtraHeaders, respond({301, MoreHeaders, Body}, THIS) end. -maybe_serve_file(File, ExtraHeaders, {?MODULE, [_Socket, _Method, _RawPath, _Version, _Headers]}=THIS) -> +maybe_serve_file(File, ExtraHeaders, {?MODULE, [_Socket, _Opts, _Method, _RawPath, _Version, _Headers]}=THIS) -> case file:read_file_info(File) of {ok, FileInfo} -> LastModified = httpd_util:rfc1123_date(FileInfo#file_info.mtime), @@ -730,7 +739,7 @@ range_parts(Body0, Ranges) -> %% accepted_encodings(["gzip", "deflate", "identity"]) -> %% ["deflate", "gzip", "identity"] %% -accepted_encodings(SupportedEncodings, {?MODULE, [_Socket, _Method, _RawPath, _Version, _Headers]}=THIS) -> +accepted_encodings(SupportedEncodings, {?MODULE, [_Socket, _Opts, _Method, _RawPath, _Version, _Headers]}=THIS) -> AcceptEncodingHeader = case get_header_value("Accept-Encoding", THIS) of undefined -> ""; @@ -768,7 +777,7 @@ accepted_encodings(SupportedEncodings, {?MODULE, [_Socket, _Method, _RawPath, _V %% 5) For an "Accept" header with value "text/*; q=0.0, */*": %% accepts_content_type("text/plain") -> false %% -accepts_content_type(ContentType1, {?MODULE, [_Socket, _Method, _RawPath, _Version, _Headers]}=THIS) -> +accepts_content_type(ContentType1, {?MODULE, [_Socket, _Opts, _Method, _RawPath, _Version, _Headers]}=THIS) -> ContentType = re:replace(ContentType1, "\\s", "", [global, {return, list}]), AcceptHeader = accept_header(THIS), case mochiweb_util:parse_qvalues(AcceptHeader) of @@ -817,7 +826,7 @@ accepts_content_type(ContentType1, {?MODULE, [_Socket, _Method, _RawPath, _Versi %% accepts_content_types(["application/json", "text/html"]) -> %% ["text/html", "application/json"] %% -accepted_content_types(Types1, {?MODULE, [_Socket, _Method, _RawPath, _Version, _Headers]}=THIS) -> +accepted_content_types(Types1, {?MODULE, [_Socket, _Opts, _Method, _RawPath, _Version, _Headers]}=THIS) -> Types = lists:map( fun(T) -> re:replace(T, "\\s", "", [global, {return, list}]) end, Types1), @@ -857,7 +866,7 @@ accepted_content_types(Types1, {?MODULE, [_Socket, _Method, _RawPath, _Version, [Type || {_Q, Type} <- lists:sort(SortFun, TypesQ)] end. -accept_header({?MODULE, [_Socket, _Method, _RawPath, _Version, _Headers]}=THIS) -> +accept_header({?MODULE, [_Socket, _Opts, _Method, _RawPath, _Version, _Headers]}=THIS) -> case get_header_value("Accept", THIS) of undefined -> "*/*"; http://git-wip-us.apache.org/repos/asf/couchdb-mochiweb/blob/ae0b9aad/src/mochiweb_socket.erl ---------------------------------------------------------------------- diff --git a/src/mochiweb_socket.erl b/src/mochiweb_socket.erl index 8cd074f..f4b8bfb 100644 --- a/src/mochiweb_socket.erl +++ b/src/mochiweb_socket.erl @@ -5,7 +5,7 @@ -module(mochiweb_socket). -export([listen/4, accept/1, recv/3, send/2, close/1, port/1, peername/1, - setopts/2, type/1]). + setopts/2, getopts/2, type/1]). -define(ACCEPT_TIMEOUT, 2000). -define(SSL_TIMEOUT, 10000). @@ -122,6 +122,11 @@ setopts({ssl, Socket}, Opts) -> setopts(Socket, Opts) -> inet:setopts(Socket, Opts). +getopts({ssl, Socket}, Opts) -> + ssl:getopts(Socket, Opts); +getopts(Socket, Opts) -> + inet:getopts(Socket, Opts). + type({ssl, _}) -> ssl; type(_) -> http://git-wip-us.apache.org/repos/asf/couchdb-mochiweb/blob/ae0b9aad/src/mochiweb_socket_server.erl ---------------------------------------------------------------------- diff --git a/src/mochiweb_socket_server.erl b/src/mochiweb_socket_server.erl index 3b7b3da..7f8587e 100644 --- a/src/mochiweb_socket_server.erl +++ b/src/mochiweb_socket_server.erl @@ -22,6 +22,7 @@ ip=any, listen=null, nodelay=false, + recbuf=?RECBUF_SIZE, backlog=128, active_sockets=0, acceptor_pool_size=16, @@ -116,6 +117,8 @@ parse_options([{backlog, Backlog} | Rest], State) -> parse_options(Rest, State#mochiweb_socket_server{backlog=Backlog}); parse_options([{nodelay, NoDelay} | Rest], State) -> parse_options(Rest, State#mochiweb_socket_server{nodelay=NoDelay}); +parse_options([{recbuf, RecBuf} | Rest], State) when is_integer(RecBuf) -> + parse_options(Rest, State#mochiweb_socket_server{recbuf=RecBuf}); parse_options([{acceptor_pool_size, Max} | Rest], State) -> MaxInt = ensure_int(Max), parse_options(Rest, @@ -162,13 +165,15 @@ ipv6_supported() -> false end. -init(State=#mochiweb_socket_server{ip=Ip, port=Port, backlog=Backlog, nodelay=NoDelay}) -> +init(State=#mochiweb_socket_server{ip=Ip, port=Port, backlog=Backlog, + nodelay=NoDelay, recbuf=RecBuf}) -> process_flag(trap_exit, true), + BaseOpts = [binary, {reuseaddr, true}, {packet, 0}, {backlog, Backlog}, - {recbuf, ?RECBUF_SIZE}, + {recbuf, RecBuf}, {exit_on_close, false}, {active, false}, {nodelay, NoDelay}], @@ -188,22 +193,37 @@ init(State=#mochiweb_socket_server{ip=Ip, port=Port, backlog=Backlog, nodelay=No new_acceptor_pool(Listen, State=#mochiweb_socket_server{acceptor_pool=Pool, acceptor_pool_size=Size, + recbuf=RecBuf, loop=Loop}) -> + LoopOpts = [{recbuf, RecBuf}], F = fun (_, S) -> - Pid = mochiweb_acceptor:start_link(self(), Listen, Loop), + Pid = mochiweb_acceptor:start_link( + self(), Listen, Loop, LoopOpts + ), sets:add_element(Pid, S) end, Pool1 = lists:foldl(F, Pool, lists:seq(1, Size)), State#mochiweb_socket_server{acceptor_pool=Pool1}. -listen(Port, Opts, State=#mochiweb_socket_server{ssl=Ssl, ssl_opts=SslOpts}) -> +listen(Port, Opts, State=#mochiweb_socket_server{ssl=Ssl, ssl_opts=SslOpts, + recbuf=RecBuf}) -> case mochiweb_socket:listen(Ssl, Port, Opts, SslOpts) of {ok, Listen} -> + %% XXX: `recbuf' value which is passed to `gen_tcp' + %% and value reported by `inet:getopts(P, [recbuf])' may + %% differ. They depends on underlying OS. From linux mans: + %% + %% The kernel doubles this value (to allow space for + %% bookkeeping overhead) when it is set using setsockopt(2), + %% and this doubled value is returned by getsockopt(2). + %% + %% See: man 7 socket | grep SO_RCVBUF {ok, ListenPort} = mochiweb_socket:port(Listen), {ok, new_acceptor_pool( Listen, State#mochiweb_socket_server{listen=Listen, - port=ListenPort})}; + port=ListenPort, + recbuf=RecBuf})}; {error, Reason} -> {stop, Reason} end. @@ -283,13 +303,17 @@ recycle_acceptor(Pid, State=#mochiweb_socket_server{ listen=Listen, loop=Loop, max=Max, + recbuf=RecBuf, active_sockets=ActiveSockets}) -> + LoopOpts = [{recbuf, RecBuf}], case sets:is_element(Pid, Pool) of true -> Pool1 = sets:del_element(Pid, Pool), case ActiveSockets + sets:size(Pool1) < Max of true -> - Acceptor = mochiweb_acceptor:start_link(self(), Listen, Loop), + Acceptor = mochiweb_acceptor:start_link( + self(), Listen, Loop, LoopOpts + ), Pool2 = sets:add_element(Acceptor, Pool1), State#mochiweb_socket_server{acceptor_pool=Pool2}; false -> @@ -298,7 +322,9 @@ recycle_acceptor(Pid, State=#mochiweb_socket_server{ false -> case sets:size(Pool) < PoolSize of true -> - Acceptor = mochiweb_acceptor:start_link(self(), Listen, Loop), + Acceptor = mochiweb_acceptor:start_link( + self(), Listen, Loop, LoopOpts + ), Pool1 = sets:add_element(Acceptor, Pool), State#mochiweb_socket_server{active_sockets=ActiveSockets, acceptor_pool=Pool1}; http://git-wip-us.apache.org/repos/asf/couchdb-mochiweb/blob/ae0b9aad/test/mochiweb_socket_server_tests.erl ---------------------------------------------------------------------- diff --git a/test/mochiweb_socket_server_tests.erl b/test/mochiweb_socket_server_tests.erl index 0dad09d..c64f5b7 100644 --- a/test/mochiweb_socket_server_tests.erl +++ b/test/mochiweb_socket_server_tests.erl @@ -63,7 +63,7 @@ test_basic_accept(Max, PoolSize, NumClients, ReportTo) -> ServerOpts = [{max, Max}, {acceptor_pool_size, PoolSize}], ServerLoop = - fun (Socket) -> + fun (Socket, _Opts) -> Tester ! {server_accepted, self()}, mochiweb_socket:setopts(Socket, [{packet, 1}]), echo_loop(Socket)
