1% Licensed under the Apache License, Version 2.0 (the "License"); you may not
2% use this file except in compliance with the License. You may obtain a copy of
3% the License at
4%
5%   http://www.apache.org/licenses/LICENSE-2.0
6%
7% Unless required by applicable law or agreed to in writing, software
8% distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
9% WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
10% License for the specific language governing permissions and limitations under
11% the License.
12
13-module(chttpd_socket_buffer_size_test).
14
15-include_lib("couch/include/couch_eunit.hrl").
16-include_lib("couch/include/couch_db.hrl").
17
18-define(USER, "chttpd_db_socket_buffer_size_test_admin").
19-define(PASS, "pass").
20-define(AUTH, {basic_auth, {?USER, ?PASS}}).
21-define(CONTENT_JSON, {"Content-Type", "application/json"}).
22
23
24setup(SocketOpts) ->
25    StartCtx = start_couch_with_cfg(SocketOpts),
26    Db = ?tempdb(),
27    create_db(url(Db)),
28    {StartCtx, Db}.
29
30
31teardown(_, {StartCtx, Db}) ->
32    delete_db(url(Db)),
33    ok = config:delete("admins", ?USER, _Persist=false),
34    test_util:stop_couch(StartCtx).
35
36
37socket_buffer_size_test_() ->
38    {
39        "chttpd socket_buffer_size_test",
40        {
41            foreachx,
42            fun setup/1, fun teardown/2,
43            [
44                {"[{recbuf, undefined}]", fun default_buffer/2},
45                {"[{recbuf, 1024}]", fun small_recbuf/2},
46                {"[{buffer, 1024}]", fun small_buffer/2}
47            ]
48        }
49    }.
50
51
52small_recbuf(_, {_, Db}) ->
53    {timeout, 30, ?_test(begin
54        Id = data(2048),
55        Response = put_req(url(Db) ++ "/" ++ Id, "{}"),
56        ?assert(Response =:= 400 orelse Response =:= request_failed)
57    end)}.
58
59
60small_buffer(_, {_, Db}) ->
61    {timeout, 30, ?_test(begin
62        Id = data(2048),
63        Response = put_req(url(Db) ++ "/" ++ Id, "{}"),
64        ?assert(Response =:= 400 orelse Response =:= request_failed)
65    end)}.
66
67
68default_buffer(_, {_, Db}) ->
69    {timeout, 30, ?_test(begin
70        Id = data(7000),
71        Headers = [{"Blah", data(7000)}],
72        Status = put_req(url(Db) ++ "/" ++ Id, Headers, "{}"),
73        ?assert(Status =:= 201 orelse Status =:= 202)
74    end)}.
75
76
77% Helper functions
78
79url() ->
80    Addr = config:get("chttpd", "bind_address", "127.0.0.1"),
81    Port = integer_to_list(mochiweb_socket_server:get(chttpd, port)),
82    "http://" ++ Addr ++ ":" ++ Port.
83
84
85url(Db) ->
86    url() ++ "/" ++ ?b2l(Db).
87
88
89create_db(Url) ->
90    Status = put_req(Url ++ "?q=1&n=1", "{}"),
91    ?assert(Status =:= 201 orelse Status =:= 202).
92
93
94delete_db(Url) ->
95    {ok, 200, _, _} = test_request:delete(Url, [?AUTH]).
96
97
98put_req(Url, Body) ->
99    put_req(Url, [], Body).
100
101
102put_req(Url, Headers, Body) ->
103    AllHeaders = Headers ++ [?CONTENT_JSON, ?AUTH],
104    case test_request:put(Url, AllHeaders, Body) of
105        {ok, Status, _, _} -> Status;
106        {error, Error} -> Error
107    end.
108
109
110data(Size) ->
111    string:copies("x", Size).
112
113
114append_to_cfg_chain(Cfg) ->
115    CfgDir = filename:dirname(lists:last(?CONFIG_CHAIN)),
116    CfgFile = filename:join([CfgDir, "chttpd_socket_buffer_extra_cfg.ini"]),
117    CfgSect = io_lib:format("[chttpd]~nserver_options = ~s~n", [Cfg]),
118    ok = file:write_file(CfgFile, CfgSect),
119    ?CONFIG_CHAIN ++ [CfgFile].
120
121
122start_couch_with_cfg(Cfg) ->
123    CfgChain = append_to_cfg_chain(Cfg),
124    StartCtx = test_util:start_couch(CfgChain, [chttpd]),
125    Hashed = couch_passwords:hash_admin_password(?PASS),
126    ok = config:set("admins", ?USER, ?b2l(Hashed), _Persist=false),
127    StartCtx.
128