Subversion Repositories SE.SVN

Rev

Blame | Last modification | View Log | RSS feed

-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.