12 |
7u83 |
1 |
-module(mochiweb_tests).
|
|
|
2 |
|
|
|
3 |
-include_lib("eunit/include/eunit.hrl").
|
|
|
4 |
|
|
|
5 |
-include("mochiweb_test_util.hrl").
|
|
|
6 |
|
|
|
7 |
with_server(Transport, ServerFun, ClientFun) ->
|
|
|
8 |
mochiweb_test_util:with_server(Transport, ServerFun,
|
|
|
9 |
ClientFun).
|
|
|
10 |
|
|
|
11 |
request_test() ->
|
|
|
12 |
R = mochiweb_request:new(z, z,
|
|
|
13 |
"//foo///bar/baz%20wibble+quux?qs=2", z, []),
|
|
|
14 |
"/foo/bar/baz wibble quux" = mochiweb_request:get(path,
|
|
|
15 |
R),
|
|
|
16 |
ok.
|
|
|
17 |
|
|
|
18 |
-define(LARGE_TIMEOUT, 60).
|
|
|
19 |
|
|
|
20 |
single_http_GET_test() -> do_GET(plain, 1).
|
|
|
21 |
|
|
|
22 |
single_https_GET_test() -> do_GET(ssl, 1).
|
|
|
23 |
|
|
|
24 |
multiple_http_GET_test() -> do_GET(plain, 3).
|
|
|
25 |
|
|
|
26 |
multiple_https_GET_test() -> do_GET(ssl, 3).
|
|
|
27 |
|
|
|
28 |
hundred_http_GET_test_() ->
|
|
|
29 |
% note the underscore
|
|
|
30 |
{timeout, ?LARGE_TIMEOUT,
|
|
|
31 |
fun () -> ?assertEqual(ok, (do_GET(plain, 100))) end}.
|
|
|
32 |
|
|
|
33 |
hundred_https_GET_test_() ->
|
|
|
34 |
% note the underscore
|
|
|
35 |
{timeout, ?LARGE_TIMEOUT,
|
|
|
36 |
fun () -> ?assertEqual(ok, (do_GET(ssl, 100))) end}.
|
|
|
37 |
|
|
|
38 |
single_128_http_POST_test() -> do_POST(plain, 128, 1).
|
|
|
39 |
|
|
|
40 |
single_128_https_POST_test() -> do_POST(ssl, 128, 1).
|
|
|
41 |
|
|
|
42 |
single_2k_http_POST_test() -> do_POST(plain, 2048, 1).
|
|
|
43 |
|
|
|
44 |
single_2k_https_POST_test() -> do_POST(ssl, 2048, 1).
|
|
|
45 |
|
|
|
46 |
single_100k_http_POST_test_() ->
|
|
|
47 |
% note the underscore
|
|
|
48 |
{timeout, ?LARGE_TIMEOUT,
|
|
|
49 |
fun () -> ?assertEqual(ok, (do_POST(plain, 102400, 1)))
|
|
|
50 |
end}.
|
|
|
51 |
|
|
|
52 |
single_100k_https_POST_test_() ->
|
|
|
53 |
% note the underscore
|
|
|
54 |
{timeout, ?LARGE_TIMEOUT,
|
|
|
55 |
fun () -> ?assertEqual(ok, (do_POST(ssl, 102400, 1)))
|
|
|
56 |
end}.
|
|
|
57 |
|
|
|
58 |
multiple_100k_http_POST_test() ->
|
|
|
59 |
{timeout, ?LARGE_TIMEOUT,
|
|
|
60 |
fun () -> ?assertEqual(ok, (do_POST(plain, 102400, 3)))
|
|
|
61 |
end}.
|
|
|
62 |
|
|
|
63 |
multiple_100K_https_POST_test() ->
|
|
|
64 |
{timeout, ?LARGE_TIMEOUT,
|
|
|
65 |
fun () -> ?assertEqual(ok, (do_POST(ssl, 102400, 3)))
|
|
|
66 |
end}.
|
|
|
67 |
|
|
|
68 |
hundred_128_http_POST_test_() ->
|
|
|
69 |
% note the underscore
|
|
|
70 |
{timeout, ?LARGE_TIMEOUT,
|
|
|
71 |
fun () -> ?assertEqual(ok, (do_POST(plain, 128, 100)))
|
|
|
72 |
end}.
|
|
|
73 |
|
|
|
74 |
hundred_128_https_POST_test_() ->
|
|
|
75 |
% note the underscore
|
|
|
76 |
{timeout, ?LARGE_TIMEOUT,
|
|
|
77 |
fun () -> ?assertEqual(ok, (do_POST(ssl, 128, 100)))
|
|
|
78 |
end}.
|
|
|
79 |
|
|
|
80 |
single_GET_scheme_test_() ->
|
|
|
81 |
[{"ssl", ?_assertEqual(ok, (do_GET("derp", ssl, 1)))},
|
|
|
82 |
{"plain",
|
|
|
83 |
?_assertEqual(ok, (do_GET("derp", plain, 1)))}].
|
|
|
84 |
|
|
|
85 |
single_GET_absoluteURI_test_() ->
|
|
|
86 |
Uri = "https://example.com:123/x/",
|
|
|
87 |
ServerFun = fun (Req) ->
|
|
|
88 |
mochiweb_request:ok({"text/plain",
|
|
|
89 |
mochiweb_request:get(path, Req)},
|
|
|
90 |
Req)
|
|
|
91 |
end,
|
|
|
92 |
%% Note that all the scheme/host/port information is discarded from path
|
|
|
93 |
ClientFun = new_client_fun('GET',
|
|
|
94 |
[#treq{path = Uri, xreply = <<"/x/">>}]),
|
|
|
95 |
[{atom_to_list(Transport),
|
|
|
96 |
?_assertEqual(ok,
|
|
|
97 |
(with_server(Transport, ServerFun, ClientFun)))}
|
|
|
98 |
|| Transport <- [ssl, plain]].
|
|
|
99 |
|
|
|
100 |
single_CONNECT_test_() ->
|
|
|
101 |
[{"ssl", ?_assertEqual(ok, (do_CONNECT(ssl, 1)))},
|
|
|
102 |
{"plain", ?_assertEqual(ok, (do_CONNECT(plain, 1)))}].
|
|
|
103 |
|
|
|
104 |
single_GET_any_test_() ->
|
|
|
105 |
ServerFun = fun (Req) ->
|
|
|
106 |
mochiweb_request:ok({"text/plain",
|
|
|
107 |
mochiweb_request:get(path, Req)},
|
|
|
108 |
Req)
|
|
|
109 |
end,
|
|
|
110 |
ClientFun = new_client_fun('GET',
|
|
|
111 |
[#treq{path = "*", xreply = <<"*">>}]),
|
|
|
112 |
[{atom_to_list(Transport),
|
|
|
113 |
?_assertEqual(ok,
|
|
|
114 |
(with_server(Transport, ServerFun, ClientFun)))}
|
|
|
115 |
|| Transport <- [ssl, plain]].
|
|
|
116 |
|
|
|
117 |
cookie_header_test() ->
|
|
|
118 |
ReplyPrefix = "You requested: ",
|
|
|
119 |
ExHeaders = [{"Set-Cookie", "foo=bar"},
|
|
|
120 |
{"Set-Cookie", "foo=baz"}],
|
|
|
121 |
ServerFun = fun (Req) ->
|
|
|
122 |
Reply = ReplyPrefix ++ mochiweb_request:get(path, Req),
|
|
|
123 |
mochiweb_request:ok({"text/plain", ExHeaders, Reply},
|
|
|
124 |
Req)
|
|
|
125 |
end,
|
|
|
126 |
Path = "cookie_header",
|
|
|
127 |
ExpectedReply = list_to_binary(ReplyPrefix ++ Path),
|
|
|
128 |
TestReqs = [#treq{path = Path, xreply = ExpectedReply,
|
|
|
129 |
xheaders = ExHeaders}],
|
|
|
130 |
ClientFun = new_client_fun('GET', TestReqs),
|
|
|
131 |
ok = with_server(plain, ServerFun, ClientFun),
|
|
|
132 |
ok.
|
|
|
133 |
|
|
|
134 |
do_CONNECT(Transport, Times) ->
|
|
|
135 |
PathPrefix = "example.com:",
|
|
|
136 |
ReplyPrefix = "You requested: ",
|
|
|
137 |
ServerFun = fun (Req) ->
|
|
|
138 |
Reply = ReplyPrefix ++ mochiweb_request:get(path, Req),
|
|
|
139 |
mochiweb_request:ok({"text/plain", Reply}, Req)
|
|
|
140 |
end,
|
|
|
141 |
TestReqs = [begin
|
|
|
142 |
Path = PathPrefix ++ integer_to_list(N),
|
|
|
143 |
ExpectedReply = list_to_binary(ReplyPrefix ++ Path),
|
|
|
144 |
#treq{path = Path, xreply = ExpectedReply}
|
|
|
145 |
end
|
|
|
146 |
|| N <- lists:seq(1, Times)],
|
|
|
147 |
ClientFun = new_client_fun('CONNECT', TestReqs),
|
|
|
148 |
ok = with_server(Transport, ServerFun, ClientFun),
|
|
|
149 |
ok.
|
|
|
150 |
|
|
|
151 |
do_GET(Transport, Times) ->
|
|
|
152 |
do_GET("/whatever/", Transport, Times).
|
|
|
153 |
|
|
|
154 |
do_GET(PathPrefix, Transport, Times) ->
|
|
|
155 |
ReplyPrefix = "You requested: ",
|
|
|
156 |
ServerFun = fun (Req) ->
|
|
|
157 |
Reply = ReplyPrefix ++ mochiweb_request:get(path, Req),
|
|
|
158 |
mochiweb_request:ok({"text/plain", Reply}, Req)
|
|
|
159 |
end,
|
|
|
160 |
TestReqs = [begin
|
|
|
161 |
Path = PathPrefix ++ integer_to_list(N),
|
|
|
162 |
ExpectedReply = list_to_binary(ReplyPrefix ++ Path),
|
|
|
163 |
#treq{path = Path, xreply = ExpectedReply}
|
|
|
164 |
end
|
|
|
165 |
|| N <- lists:seq(1, Times)],
|
|
|
166 |
ClientFun = new_client_fun('GET', TestReqs),
|
|
|
167 |
ok = with_server(Transport, ServerFun, ClientFun),
|
|
|
168 |
ok.
|
|
|
169 |
|
|
|
170 |
do_POST(Transport, Size, Times) ->
|
|
|
171 |
ServerFun = fun (Req) ->
|
|
|
172 |
Body = mochiweb_request:recv_body(Req),
|
|
|
173 |
Headers = [{"Content-Type",
|
|
|
174 |
"application/octet-stream"}],
|
|
|
175 |
mochiweb_request:respond({201, Headers, Body}, Req)
|
|
|
176 |
end,
|
|
|
177 |
TestReqs = [begin
|
|
|
178 |
Path = "/stuff/" ++ integer_to_list(N),
|
|
|
179 |
Body = crypto:strong_rand_bytes(Size),
|
|
|
180 |
#treq{path = Path, body = Body, xreply = Body}
|
|
|
181 |
end
|
|
|
182 |
|| N <- lists:seq(1, Times)],
|
|
|
183 |
ClientFun = new_client_fun('POST', TestReqs),
|
|
|
184 |
ok = with_server(Transport, ServerFun, ClientFun),
|
|
|
185 |
ok.
|
|
|
186 |
|
|
|
187 |
new_client_fun(Method, TestReqs) ->
|
|
|
188 |
fun (Transport, Port) ->
|
|
|
189 |
mochiweb_test_util:client_request(Transport, Port,
|
|
|
190 |
Method, TestReqs)
|
|
|
191 |
end.
|
|
|
192 |
|
|
|
193 |
close_on_unread_data_test() ->
|
|
|
194 |
ok = with_server(plain,
|
|
|
195 |
fun mochiweb_request:not_found/1,
|
|
|
196 |
fun close_on_unread_data_client/2).
|
|
|
197 |
|
|
|
198 |
close_on_unread_data_client(Transport, Port) ->
|
|
|
199 |
SockFun = mochiweb_test_util:sock_fun(Transport, Port),
|
|
|
200 |
%% A normal GET request should not trigger this behavior
|
|
|
201 |
Request0 = string:join(["GET / HTTP/1.1",
|
|
|
202 |
"Host: localhost", "", ""],
|
|
|
203 |
"\r\n"),
|
|
|
204 |
ok = SockFun({setopts, [{packet, http}]}),
|
|
|
205 |
ok = SockFun({send, Request0}),
|
|
|
206 |
?assertMatch({ok, {http_response, {1, 1}, 404, _}},
|
|
|
207 |
(SockFun(recv))),
|
|
|
208 |
Headers0 =
|
|
|
209 |
mochiweb_test_util:read_server_headers(SockFun),
|
|
|
210 |
?assertEqual(undefined,
|
|
|
211 |
(mochiweb_headers:get_value("Connection", Headers0))),
|
|
|
212 |
Len0 =
|
|
|
213 |
list_to_integer(mochiweb_headers:get_value("Content-Length",
|
|
|
214 |
Headers0)),
|
|
|
215 |
_Body0 = mochiweb_test_util:drain_reply(SockFun, Len0,
|
|
|
216 |
<<>>),
|
|
|
217 |
%% Re-use same socket
|
|
|
218 |
Request = string:join(["POST / HTTP/1.1",
|
|
|
219 |
"Host: localhost", "Content-Type: application/json",
|
|
|
220 |
"Content-Length: 2", "", "{}"],
|
|
|
221 |
"\r\n"),
|
|
|
222 |
ok = SockFun({setopts, [{packet, http}]}),
|
|
|
223 |
ok = SockFun({send, Request}),
|
|
|
224 |
?assertMatch({ok, {http_response, {1, 1}, 404, _}},
|
|
|
225 |
(SockFun(recv))),
|
|
|
226 |
Headers =
|
|
|
227 |
mochiweb_test_util:read_server_headers(SockFun),
|
|
|
228 |
%% Expect to see a Connection: close header when we know the
|
|
|
229 |
%% server will close the connection re #146
|
|
|
230 |
?assertEqual("close",
|
|
|
231 |
(mochiweb_headers:get_value("Connection", Headers))),
|
|
|
232 |
Len =
|
|
|
233 |
list_to_integer(mochiweb_headers:get_value("Content-Length",
|
|
|
234 |
Headers)),
|
|
|
235 |
_Body = mochiweb_test_util:drain_reply(SockFun, Len,
|
|
|
236 |
<<>>),
|
|
|
237 |
?assertEqual({error, closed}, (SockFun(recv))),
|
|
|
238 |
ok.
|