-module(mochiweb_tests). -include_lib("eunit/include/eunit.hrl"). -include("mochiweb_test_util.hrl"). with_server(Transport, ServerFun, ClientFun) -> mochiweb_test_util:with_server(Transport, ServerFun, ClientFun). request_test() -> R = mochiweb_request:new(z, z, "//foo///bar/baz%20wibble+quux?qs=2", z, []), "/foo/bar/baz wibble quux" = mochiweb_request:get(path, R), ok. -define(LARGE_TIMEOUT, 60). single_http_GET_test() -> do_GET(plain, 1). single_https_GET_test() -> do_GET(ssl, 1). multiple_http_GET_test() -> do_GET(plain, 3). multiple_https_GET_test() -> do_GET(ssl, 3). hundred_http_GET_test_() -> % note the underscore {timeout, ?LARGE_TIMEOUT, fun () -> ?assertEqual(ok, (do_GET(plain, 100))) end}. hundred_https_GET_test_() -> % note the underscore {timeout, ?LARGE_TIMEOUT, fun () -> ?assertEqual(ok, (do_GET(ssl, 100))) end}. single_128_http_POST_test() -> do_POST(plain, 128, 1). single_128_https_POST_test() -> do_POST(ssl, 128, 1). single_2k_http_POST_test() -> do_POST(plain, 2048, 1). single_2k_https_POST_test() -> do_POST(ssl, 2048, 1). single_100k_http_POST_test_() -> % note the underscore {timeout, ?LARGE_TIMEOUT, fun () -> ?assertEqual(ok, (do_POST(plain, 102400, 1))) end}. single_100k_https_POST_test_() -> % note the underscore {timeout, ?LARGE_TIMEOUT, fun () -> ?assertEqual(ok, (do_POST(ssl, 102400, 1))) end}. multiple_100k_http_POST_test() -> {timeout, ?LARGE_TIMEOUT, fun () -> ?assertEqual(ok, (do_POST(plain, 102400, 3))) end}. multiple_100K_https_POST_test() -> {timeout, ?LARGE_TIMEOUT, fun () -> ?assertEqual(ok, (do_POST(ssl, 102400, 3))) end}. hundred_128_http_POST_test_() -> % note the underscore {timeout, ?LARGE_TIMEOUT, fun () -> ?assertEqual(ok, (do_POST(plain, 128, 100))) end}. hundred_128_https_POST_test_() -> % note the underscore {timeout, ?LARGE_TIMEOUT, fun () -> ?assertEqual(ok, (do_POST(ssl, 128, 100))) end}. single_GET_scheme_test_() -> [{"ssl", ?_assertEqual(ok, (do_GET("derp", ssl, 1)))}, {"plain", ?_assertEqual(ok, (do_GET("derp", plain, 1)))}]. single_GET_absoluteURI_test_() -> Uri = "https://example.com:123/x/", ServerFun = fun (Req) -> mochiweb_request:ok({"text/plain", mochiweb_request:get(path, Req)}, Req) end, %% Note that all the scheme/host/port information is discarded from path ClientFun = new_client_fun('GET', [#treq{path = Uri, xreply = <<"/x/">>}]), [{atom_to_list(Transport), ?_assertEqual(ok, (with_server(Transport, ServerFun, ClientFun)))} || Transport <- [ssl, plain]]. single_CONNECT_test_() -> [{"ssl", ?_assertEqual(ok, (do_CONNECT(ssl, 1)))}, {"plain", ?_assertEqual(ok, (do_CONNECT(plain, 1)))}]. single_GET_any_test_() -> ServerFun = fun (Req) -> mochiweb_request:ok({"text/plain", mochiweb_request:get(path, Req)}, Req) end, ClientFun = new_client_fun('GET', [#treq{path = "*", xreply = <<"*">>}]), [{atom_to_list(Transport), ?_assertEqual(ok, (with_server(Transport, ServerFun, ClientFun)))} || Transport <- [ssl, plain]]. cookie_header_test() -> ReplyPrefix = "You requested: ", ExHeaders = [{"Set-Cookie", "foo=bar"}, {"Set-Cookie", "foo=baz"}], ServerFun = fun (Req) -> Reply = ReplyPrefix ++ mochiweb_request:get(path, Req), mochiweb_request:ok({"text/plain", ExHeaders, Reply}, Req) end, Path = "cookie_header", ExpectedReply = list_to_binary(ReplyPrefix ++ Path), TestReqs = [#treq{path = Path, xreply = ExpectedReply, xheaders = ExHeaders}], ClientFun = new_client_fun('GET', TestReqs), ok = with_server(plain, ServerFun, ClientFun), ok. do_CONNECT(Transport, Times) -> PathPrefix = "example.com:", ReplyPrefix = "You requested: ", ServerFun = fun (Req) -> Reply = ReplyPrefix ++ mochiweb_request:get(path, Req), mochiweb_request:ok({"text/plain", Reply}, Req) end, TestReqs = [begin Path = PathPrefix ++ integer_to_list(N), ExpectedReply = list_to_binary(ReplyPrefix ++ Path), #treq{path = Path, xreply = ExpectedReply} end || N <- lists:seq(1, Times)], ClientFun = new_client_fun('CONNECT', TestReqs), ok = with_server(Transport, ServerFun, ClientFun), ok. do_GET(Transport, Times) -> do_GET("/whatever/", Transport, Times). do_GET(PathPrefix, Transport, Times) -> ReplyPrefix = "You requested: ", ServerFun = fun (Req) -> Reply = ReplyPrefix ++ mochiweb_request:get(path, Req), mochiweb_request:ok({"text/plain", Reply}, Req) end, TestReqs = [begin Path = PathPrefix ++ integer_to_list(N), ExpectedReply = list_to_binary(ReplyPrefix ++ Path), #treq{path = Path, xreply = ExpectedReply} end || N <- lists:seq(1, Times)], ClientFun = new_client_fun('GET', TestReqs), ok = with_server(Transport, ServerFun, ClientFun), ok. do_POST(Transport, Size, Times) -> ServerFun = fun (Req) -> Body = mochiweb_request:recv_body(Req), Headers = [{"Content-Type", "application/octet-stream"}], mochiweb_request:respond({201, Headers, Body}, Req) end, TestReqs = [begin Path = "/stuff/" ++ integer_to_list(N), Body = crypto:strong_rand_bytes(Size), #treq{path = Path, body = Body, xreply = Body} end || N <- lists:seq(1, Times)], ClientFun = new_client_fun('POST', TestReqs), ok = with_server(Transport, ServerFun, ClientFun), ok. new_client_fun(Method, TestReqs) -> fun (Transport, Port) -> mochiweb_test_util:client_request(Transport, Port, Method, TestReqs) end. close_on_unread_data_test() -> ok = with_server(plain, fun mochiweb_request:not_found/1, fun close_on_unread_data_client/2). close_on_unread_data_client(Transport, Port) -> SockFun = mochiweb_test_util:sock_fun(Transport, Port), %% A normal GET request should not trigger this behavior Request0 = string:join(["GET / HTTP/1.1", "Host: localhost", "", ""], "\r\n"), ok = SockFun({setopts, [{packet, http}]}), ok = SockFun({send, Request0}), ?assertMatch({ok, {http_response, {1, 1}, 404, _}}, (SockFun(recv))), Headers0 = mochiweb_test_util:read_server_headers(SockFun), ?assertEqual(undefined, (mochiweb_headers:get_value("Connection", Headers0))), Len0 = list_to_integer(mochiweb_headers:get_value("Content-Length", Headers0)), _Body0 = mochiweb_test_util:drain_reply(SockFun, Len0, <<>>), %% Re-use same socket Request = string:join(["POST / HTTP/1.1", "Host: localhost", "Content-Type: application/json", "Content-Length: 2", "", "{}"], "\r\n"), ok = SockFun({setopts, [{packet, http}]}), ok = SockFun({send, Request}), ?assertMatch({ok, {http_response, {1, 1}, 404, _}}, (SockFun(recv))), Headers = mochiweb_test_util:read_server_headers(SockFun), %% Expect to see a Connection: close header when we know the %% server will close the connection re #146 ?assertEqual("close", (mochiweb_headers:get_value("Connection", Headers))), Len = list_to_integer(mochiweb_headers:get_value("Content-Length", Headers)), _Body = mochiweb_test_util:drain_reply(SockFun, Len, <<>>), ?assertEqual({error, closed}, (SockFun(recv))), ok.