You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@couchdb.apache.org by rn...@apache.org on 2015/07/29 17:54:50 UTC

[21/50] mochiweb commit: updated refs/heads/upstream to b66b68d

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 <dd...@gmail.com>
Authored: Fri Sep 5 18:20:31 2014 +0400
Committer: Daniil Churikov <dc...@alertlogic.com>
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('bob@mochimedia.com').
 -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], &lt;&lt;"Not found."&gt;&gt;})</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)