summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAlexander Shorin <kxepal@apache.org>2014-06-04 12:54:44 +0400
committerAlexander Shorin <kxepal@apache.org>2015-12-03 00:51:55 +0300
commit3e545431dc1bfb751a10a731514dd9d5dd0726d1 (patch)
tree1140f4597c70f6197eb29faa6682a1c63105b0fc
parent96b15f9e03a65696d4a359d996f6c652481fd202 (diff)
downloadcouchdb-3e545431dc1bfb751a10a731514dd9d5dd0726d1.tar.gz
Port 180-http-proxy.t etap test suite to eunit
-rw-r--r--test/couchdb/Makefile.am5
-rw-r--r--test/couchdb/couchdb_http_proxy_tests.erl554
-rw-r--r--test/couchdb/test_web.erl (renamed from test/etap/test_web.erl)35
-rw-r--r--test/etap/180-http-proxy.ini20
-rwxr-xr-xtest/etap/180-http-proxy.t376
-rw-r--r--test/etap/Makefile.am5
6 files changed, 583 insertions, 412 deletions
diff --git a/test/couchdb/Makefile.am b/test/couchdb/Makefile.am
index 95f1b7797..d664a58ea 100644
--- a/test/couchdb/Makefile.am
+++ b/test/couchdb/Makefile.am
@@ -19,7 +19,8 @@ all:
mkdir -p temp/
$(ERLC) -Wall -I$(top_srcdir)/src -I$(top_srcdir)/test/couchdb/include \
-o $(top_builddir)/test/couchdb/ebin/ $(ERLC_FLAGS) ${TEST} \
- $(top_srcdir)/test/couchdb/test_request.erl
+ $(top_srcdir)/test/couchdb/test_request.erl \
+ $(top_srcdir)/test/couchdb/test_web.erl
chmod +x run
chmod +x $(top_builddir)/test/couchdb/fixtures/os_daemon_configer.escript
@@ -41,12 +42,14 @@ eunit_files = \
couch_work_queue_tests.erl \
couchdb_attachments_tests.erl \
couchdb_file_compression_tests.erl \
+ couchdb_http_proxy_tests.erl \
couchdb_modules_load_tests.erl \
couchdb_os_daemons_tests.erl \
couchdb_update_conflicts_tests.erl \
couchdb_vhosts_tests.erl \
couchdb_views_tests.erl \
test_request.erl \
+ test_web.erl \
include/couch_eunit.hrl
fixture_files = \
diff --git a/test/couchdb/couchdb_http_proxy_tests.erl b/test/couchdb/couchdb_http_proxy_tests.erl
new file mode 100644
index 000000000..03ceca7c2
--- /dev/null
+++ b/test/couchdb/couchdb_http_proxy_tests.erl
@@ -0,0 +1,554 @@
+% Licensed under the Apache License, Version 2.0 (the "License"); you may not
+% use this file except in compliance with the License. You may obtain a copy of
+% the License at
+%
+% http://www.apache.org/licenses/LICENSE-2.0
+%
+% Unless required by applicable law or agreed to in writing, software
+% distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+% WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+% License for the specific language governing permissions and limitations under
+% the License.
+
+-module(couchdb_http_proxy_tests).
+
+-include("couch_eunit.hrl").
+
+-record(req, {method=get, path="", headers=[], body="", opts=[]}).
+
+-define(CONFIG_FIXTURE_TEMP,
+ begin
+ FileName = filename:join([?TEMPDIR, ?tempfile() ++ ".ini"]),
+ {ok, Fd} = file:open(FileName, write),
+ ok = file:truncate(Fd),
+ ok = file:close(Fd),
+ FileName
+ end).
+-define(TIMEOUT, 5000).
+
+
+start() ->
+ % we have to write any config changes to temp ini file to not loose them
+ % when supervisor will kill all children due to reaching restart threshold
+ % (each httpd_global_handlers changes causes couch_httpd restart)
+ couch_server_sup:start_link(?CONFIG_CHAIN ++ [?CONFIG_FIXTURE_TEMP]),
+ % 49151 is IANA Reserved, let's assume no one is listening there
+ with_process_restart(couch_httpd, fun() ->
+ couch_config:set("httpd_global_handlers", "_error",
+ "{couch_httpd_proxy, handle_proxy_req, <<\"http://127.0.0.1:49151/\">>}"
+ )
+ end),
+ ok.
+
+stop(_) ->
+ couch_server_sup:stop(),
+ ok.
+
+setup() ->
+ {ok, Pid} = test_web:start_link(),
+ Value = lists:flatten(io_lib:format(
+ "{couch_httpd_proxy, handle_proxy_req, ~p}",
+ [list_to_binary(proxy_url())])),
+ with_process_restart(couch_httpd, fun() ->
+ couch_config:set("httpd_global_handlers", "_test", Value)
+ end),
+ Pid.
+
+teardown(Pid) ->
+ erlang:monitor(process, Pid),
+ test_web:stop(),
+ receive
+ {'DOWN', _, _, Pid, _} ->
+ ok
+ after ?TIMEOUT ->
+ throw({timeout, test_web_stop})
+ end.
+
+
+http_proxy_test_() ->
+ {
+ "HTTP Proxy handler tests",
+ {
+ setup,
+ fun start/0, fun stop/1,
+ {
+ foreach,
+ fun setup/0, fun teardown/1,
+ [
+ fun should_proxy_basic_request/1,
+ fun should_return_alternative_status/1,
+ fun should_respect_trailing_slash/1,
+ fun should_proxy_headers/1,
+ fun should_proxy_host_header/1,
+ fun should_pass_headers_back/1,
+ fun should_use_same_protocol_version/1,
+ fun should_proxy_body/1,
+ fun should_proxy_body_back/1,
+ fun should_proxy_chunked_body/1,
+ fun should_proxy_chunked_body_back/1,
+ fun should_rewrite_location_header/1,
+ fun should_not_rewrite_external_locations/1,
+ fun should_rewrite_relative_location/1,
+ fun should_refuse_connection_to_backend/1
+ ]
+ }
+
+ }
+ }.
+
+
+should_proxy_basic_request(_) ->
+ Remote = fun(Req) ->
+ 'GET' = Req:get(method),
+ "/" = Req:get(path),
+ 0 = Req:get(body_length),
+ <<>> = Req:recv_body(),
+ {ok, {200, [{"Content-Type", "text/plain"}], "ok"}}
+ end,
+ Local = fun
+ ({ok, "200", _, "ok"}) ->
+ true;
+ (_) ->
+ false
+ end,
+ ?_test(check_request(#req{}, Remote, Local)).
+
+should_return_alternative_status(_) ->
+ Remote = fun(Req) ->
+ "/alternate_status" = Req:get(path),
+ {ok, {201, [], "ok"}}
+ end,
+ Local = fun
+ ({ok, "201", _, "ok"}) ->
+ true;
+ (_) ->
+ false
+ end,
+ Req = #req{path = "/alternate_status"},
+ ?_test(check_request(Req, Remote, Local)).
+
+should_respect_trailing_slash(_) ->
+ Remote = fun(Req) ->
+ "/trailing_slash/" = Req:get(path),
+ {ok, {200, [], "ok"}}
+ end,
+ Local = fun
+ ({ok, "200", _, "ok"}) ->
+ true;
+ (_) ->
+ false
+ end,
+ Req = #req{path="/trailing_slash/"},
+ ?_test(check_request(Req, Remote, Local)).
+
+should_proxy_headers(_) ->
+ Remote = fun(Req) ->
+ "/passes_header" = Req:get(path),
+ "plankton" = Req:get_header_value("X-CouchDB-Ralph"),
+ {ok, {200, [], "ok"}}
+ end,
+ Local = fun
+ ({ok, "200", _, "ok"}) ->
+ true;
+ (_) ->
+ false
+ end,
+ Req = #req{
+ path="/passes_header",
+ headers=[{"X-CouchDB-Ralph", "plankton"}]
+ },
+ ?_test(check_request(Req, Remote, Local)).
+
+should_proxy_host_header(_) ->
+ Remote = fun(Req) ->
+ "/passes_host_header" = Req:get(path),
+ "www.google.com" = Req:get_header_value("Host"),
+ {ok, {200, [], "ok"}}
+ end,
+ Local = fun
+ ({ok, "200", _, "ok"}) ->
+ true;
+ (_) ->
+ false
+ end,
+ Req = #req{
+ path="/passes_host_header",
+ headers=[{"Host", "www.google.com"}]
+ },
+ ?_test(check_request(Req, Remote, Local)).
+
+should_pass_headers_back(_) ->
+ Remote = fun(Req) ->
+ "/passes_header_back" = Req:get(path),
+ {ok, {200, [{"X-CouchDB-Plankton", "ralph"}], "ok"}}
+ end,
+ Local = fun
+ ({ok, "200", Headers, "ok"}) ->
+ lists:member({"X-CouchDB-Plankton", "ralph"}, Headers);
+ (_) ->
+ false
+ end,
+ Req = #req{path="/passes_header_back"},
+ ?_test(check_request(Req, Remote, Local)).
+
+should_use_same_protocol_version(_) ->
+ Remote = fun(Req) ->
+ "/uses_same_version" = Req:get(path),
+ {1, 0} = Req:get(version),
+ {ok, {200, [], "ok"}}
+ end,
+ Local = fun
+ ({ok, "200", _, "ok"}) ->
+ true;
+ (_) ->
+ false
+ end,
+ Req = #req{
+ path="/uses_same_version",
+ opts=[{http_vsn, {1, 0}}]
+ },
+ ?_test(check_request(Req, Remote, Local)).
+
+should_proxy_body(_) ->
+ Remote = fun(Req) ->
+ 'PUT' = Req:get(method),
+ "/passes_body" = Req:get(path),
+ <<"Hooray!">> = Req:recv_body(),
+ {ok, {201, [], "ok"}}
+ end,
+ Local = fun
+ ({ok, "201", _, "ok"}) ->
+ true;
+ (_) ->
+ false
+ end,
+ Req = #req{
+ method=put,
+ path="/passes_body",
+ body="Hooray!"
+ },
+ ?_test(check_request(Req, Remote, Local)).
+
+should_proxy_body_back(_) ->
+ BodyChunks = [<<"foo">>, <<"bar">>, <<"bazinga">>],
+ Remote = fun(Req) ->
+ 'GET' = Req:get(method),
+ "/passes_eof_body" = Req:get(path),
+ {raw, {200, [{"Connection", "close"}], BodyChunks}}
+ end,
+ Local = fun
+ ({ok, "200", _, "foobarbazinga"}) ->
+ true;
+ (_) ->
+ false
+ end,
+ Req = #req{path="/passes_eof_body"},
+ ?_test(check_request(Req, Remote, Local)).
+
+should_proxy_chunked_body(_) ->
+ BodyChunks = [<<"foo">>, <<"bar">>, <<"bazinga">>],
+ Remote = fun(Req) ->
+ 'POST' = Req:get(method),
+ "/passes_chunked_body" = Req:get(path),
+ RecvBody = fun
+ ({Length, Chunk}, [Chunk | Rest]) ->
+ Length = size(Chunk),
+ Rest;
+ ({0, []}, []) ->
+ ok
+ end,
+ ok = Req:stream_body(1024 * 1024, RecvBody, BodyChunks),
+ {ok, {201, [], "ok"}}
+ end,
+ Local = fun
+ ({ok, "201", _, "ok"}) ->
+ true;
+ (_) ->
+ false
+ end,
+ Req = #req{
+ method=post,
+ path="/passes_chunked_body",
+ headers=[{"Transfer-Encoding", "chunked"}],
+ body=chunked_body(BodyChunks)
+ },
+ ?_test(check_request(Req, Remote, Local)).
+
+should_proxy_chunked_body_back(_) ->
+ ?_test(begin
+ Remote = fun(Req) ->
+ 'GET' = Req:get(method),
+ "/passes_chunked_body_back" = Req:get(path),
+ BodyChunks = [<<"foo">>, <<"bar">>, <<"bazinga">>],
+ {chunked, {200, [{"Transfer-Encoding", "chunked"}], BodyChunks}}
+ end,
+ Req = #req{
+ path="/passes_chunked_body_back",
+ opts=[{stream_to, self()}]
+ },
+
+ Resp = check_request(Req, Remote, no_local),
+ ?assertMatch({ibrowse_req_id, _}, Resp),
+ {_, ReqId} = Resp,
+
+ % Grab headers from response
+ receive
+ {ibrowse_async_headers, ReqId, "200", Headers} ->
+ ?assertEqual("chunked",
+ proplists:get_value("Transfer-Encoding", Headers)),
+ ibrowse:stream_next(ReqId)
+ after 1000 ->
+ throw({error, timeout})
+ end,
+
+ ?assertEqual(<<"foobarbazinga">>, recv_body(ReqId, [])),
+ ?assertEqual(was_ok, test_web:check_last())
+ end).
+
+should_refuse_connection_to_backend(_) ->
+ Local = fun
+ ({ok, "500", _, _}) ->
+ true;
+ (_) ->
+ false
+ end,
+ Req = #req{opts=[{url, server_url("/_error")}]},
+ ?_test(check_request(Req, no_remote, Local)).
+
+should_rewrite_location_header(_) ->
+ {
+ "Testing location header rewrites",
+ do_rewrite_tests([
+ {"Location", proxy_url() ++ "/foo/bar",
+ server_url() ++ "/foo/bar"},
+ {"Content-Location", proxy_url() ++ "/bing?q=2",
+ server_url() ++ "/bing?q=2"},
+ {"Uri", proxy_url() ++ "/zip#frag",
+ server_url() ++ "/zip#frag"},
+ {"Destination", proxy_url(),
+ server_url() ++ "/"}
+ ])
+ }.
+
+should_not_rewrite_external_locations(_) ->
+ {
+ "Testing no rewrite of external locations",
+ do_rewrite_tests([
+ {"Location", external_url() ++ "/search",
+ external_url() ++ "/search"},
+ {"Content-Location", external_url() ++ "/s?q=2",
+ external_url() ++ "/s?q=2"},
+ {"Uri", external_url() ++ "/f#f",
+ external_url() ++ "/f#f"},
+ {"Destination", external_url() ++ "/f?q=2#f",
+ external_url() ++ "/f?q=2#f"}
+ ])
+ }.
+
+should_rewrite_relative_location(_) ->
+ {
+ "Testing relative rewrites",
+ do_rewrite_tests([
+ {"Location", "/foo",
+ server_url() ++ "/foo"},
+ {"Content-Location", "bar",
+ server_url() ++ "/bar"},
+ {"Uri", "/zing?q=3",
+ server_url() ++ "/zing?q=3"},
+ {"Destination", "bing?q=stuff#yay",
+ server_url() ++ "/bing?q=stuff#yay"}
+ ])
+ }.
+
+
+do_rewrite_tests(Tests) ->
+ lists:map(fun({Header, Location, Url}) ->
+ should_rewrite_header(Header, Location, Url)
+ end, Tests).
+
+should_rewrite_header(Header, Location, Url) ->
+ Remote = fun(Req) ->
+ "/rewrite_test" = Req:get(path),
+ {ok, {302, [{Header, Location}], "ok"}}
+ end,
+ Local = fun
+ ({ok, "302", Headers, "ok"}) ->
+ ?assertEqual(Url, couch_util:get_value(Header, Headers)),
+ true;
+ (E) ->
+ ?debugFmt("~p", [E]),
+ false
+ end,
+ Req = #req{path="/rewrite_test"},
+ {Header, ?_test(check_request(Req, Remote, Local))}.
+
+
+server_url() ->
+ server_url("/_test").
+
+server_url(Resource) ->
+ Addr = couch_config:get("httpd", "bind_address"),
+ Port = integer_to_list(mochiweb_socket_server:get(couch_httpd, port)),
+ lists:concat(["http://", Addr, ":", Port, Resource]).
+
+proxy_url() ->
+ "http://127.0.0.1:" ++ integer_to_list(test_web:get_port()).
+
+external_url() ->
+ "https://google.com".
+
+check_request(Req, Remote, Local) ->
+ case Remote of
+ no_remote ->
+ ok;
+ _ ->
+ test_web:set_assert(Remote)
+ end,
+ Url = case proplists:lookup(url, Req#req.opts) of
+ none ->
+ server_url() ++ Req#req.path;
+ {url, DestUrl} ->
+ DestUrl
+ end,
+ Opts = [{headers_as_is, true} | Req#req.opts],
+ Resp =ibrowse:send_req(
+ Url, Req#req.headers, Req#req.method, Req#req.body, Opts
+ ),
+ %?debugFmt("ibrowse response: ~p", [Resp]),
+ case Local of
+ no_local ->
+ ok;
+ _ ->
+ ?assert(Local(Resp))
+ end,
+ case {Remote, Local} of
+ {no_remote, _} ->
+ ok;
+ {_, no_local} ->
+ ok;
+ _ ->
+ ?assertEqual(was_ok, test_web:check_last())
+ end,
+ Resp.
+
+chunked_body(Chunks) ->
+ chunked_body(Chunks, []).
+
+chunked_body([], Acc) ->
+ iolist_to_binary(lists:reverse(Acc, "0\r\n\r\n"));
+chunked_body([Chunk | Rest], Acc) ->
+ Size = to_hex(size(Chunk)),
+ chunked_body(Rest, ["\r\n", Chunk, "\r\n", Size | Acc]).
+
+to_hex(Val) ->
+ to_hex(Val, []).
+
+to_hex(0, Acc) ->
+ Acc;
+to_hex(Val, Acc) ->
+ to_hex(Val div 16, [hex_char(Val rem 16) | Acc]).
+
+hex_char(V) when V < 10 -> $0 + V;
+hex_char(V) -> $A + V - 10.
+
+recv_body(ReqId, Acc) ->
+ receive
+ {ibrowse_async_response, ReqId, Data} ->
+ recv_body(ReqId, [Data | Acc]);
+ {ibrowse_async_response_end, ReqId} ->
+ iolist_to_binary(lists:reverse(Acc));
+ Else ->
+ throw({error, unexpected_mesg, Else})
+ after ?TIMEOUT ->
+ throw({error, timeout})
+ end.
+
+
+%% Copy from couch test_util @ master branch
+
+now_us() ->
+ {MegaSecs, Secs, MicroSecs} = now(),
+ (MegaSecs * 1000000 + Secs) * 1000000 + MicroSecs.
+
+stop_sync(Name) ->
+ stop_sync(Name, shutdown).
+stop_sync(Name, Reason) ->
+ stop_sync(Name, Reason, 5000).
+stop_sync(Name, Reason, Timeout) when is_atom(Name) ->
+ stop_sync(whereis(Name), Reason, Timeout);
+stop_sync(Pid, Reason, Timeout) when is_atom(Reason) and is_pid(Pid) ->
+ stop_sync(Pid, fun() -> exit(Pid, Reason) end, Timeout);
+stop_sync(Pid, Fun, Timeout) when is_function(Fun) and is_pid(Pid) ->
+ MRef = erlang:monitor(process, Pid),
+ try
+ begin
+ catch unlink(Pid),
+ Res = (catch Fun()),
+ receive
+ {'DOWN', MRef, _, _, _} ->
+ Res
+ after Timeout ->
+ timeout
+ end
+ end
+ after
+ erlang:demonitor(MRef, [flush])
+ end;
+stop_sync(_, _, _) -> error(badarg).
+
+stop_sync_throw(Name, Error) ->
+ stop_sync_throw(Name, shutdown, Error).
+stop_sync_throw(Name, Reason, Error) ->
+ stop_sync_throw(Name, Reason, Error, 5000).
+stop_sync_throw(Pid, Fun, Error, Timeout) ->
+ case stop_sync(Pid, Fun, Timeout) of
+ timeout ->
+ throw(Error);
+ Else ->
+ Else
+ end.
+
+with_process_restart(Name) ->
+ {Pid, true} = with_process_restart(
+ fun() -> exit(whereis(Name), shutdown) end, Name),
+ Pid.
+with_process_restart(Name, Fun) ->
+ with_process_restart(Name, Fun, 5000).
+with_process_restart(Name, Fun, Timeout) ->
+ ok = stop_sync(Name, Fun),
+ case wait_process(Name, Timeout) of
+ timeout ->
+ timeout;
+ Pid ->
+ Pid
+ end.
+
+wait_process(Name) ->
+ wait_process(Name, 5000).
+wait_process(Name, Timeout) ->
+ wait(fun() ->
+ case whereis(Name) of
+ undefined ->
+ wait;
+ Pid ->
+ Pid
+ end
+ end, Timeout).
+
+wait(Fun) ->
+ wait(Fun, 5000, 50).
+wait(Fun, Timeout) ->
+ wait(Fun, Timeout, 50).
+wait(Fun, Timeout, Delay) ->
+ Now = now_us(),
+ wait(Fun, Timeout * 1000, Delay, Now, Now).
+wait(_Fun, Timeout, _Delay, Started, Prev) when Prev - Started > Timeout ->
+ timeout;
+wait(Fun, Timeout, Delay, Started, _Prev) ->
+ case Fun() of
+ wait ->
+ ok = timer:sleep(Delay),
+ wait(Fun, Timeout, Delay, Started, now_us());
+ Else ->
+ Else
+ end.
diff --git a/test/etap/test_web.erl b/test/couchdb/test_web.erl
index ed78651f1..1de2cd1c3 100644
--- a/test/etap/test_web.erl
+++ b/test/couchdb/test_web.erl
@@ -13,12 +13,15 @@
-module(test_web).
-behaviour(gen_server).
--export([start_link/0, loop/1, get_port/0, set_assert/1, check_last/0]).
+-include("couch_eunit.hrl").
+
+-export([start_link/0, stop/0, loop/1, get_port/0, set_assert/1, check_last/0]).
-export([init/1, terminate/2, code_change/3]).
-export([handle_call/3, handle_cast/2, handle_info/2]).
-define(SERVER, test_web_server).
-define(HANDLER, test_web_handler).
+-define(DELAY, 500).
start_link() ->
gen_server:start({local, ?HANDLER}, ?MODULE, [], []),
@@ -29,7 +32,7 @@ start_link() ->
]).
loop(Req) ->
- %etap:diag("Handling request: ~p", [Req]),
+ %?debugFmt("Handling request: ~p", [Req]),
case gen_server:call(?HANDLER, {check_request, Req}) of
{ok, RespInfo} ->
{ok, Req:respond(RespInfo)};
@@ -40,12 +43,12 @@ loop(Req) ->
{ok, Resp};
{chunked, {Status, Headers, BodyChunks}} ->
Resp = Req:respond({Status, Headers, chunked}),
- timer:sleep(500),
+ timer:sleep(?DELAY),
lists:foreach(fun(C) -> Resp:write_chunk(C) end, BodyChunks),
Resp:write_chunk([]),
{ok, Resp};
{error, Reason} ->
- etap:diag("Error: ~p", [Reason]),
+ ?debugFmt("Error: ~p", [Reason]),
Body = lists:flatten(io_lib:format("Error: ~p", [Reason])),
{ok, Req:respond({200, [], Body})}
end.
@@ -54,7 +57,7 @@ get_port() ->
mochiweb_socket_server:get(?SERVER, port).
set_assert(Fun) ->
- ok = gen_server:call(?HANDLER, {set_assert, Fun}).
+ ?assertEqual(ok, gen_server:call(?HANDLER, {set_assert, Fun})).
check_last() ->
gen_server:call(?HANDLER, last_status).
@@ -65,12 +68,20 @@ init(_) ->
terminate(_Reason, _State) ->
ok.
+stop() ->
+ gen_server:cast(?SERVER, stop).
+
+
handle_call({check_request, Req}, _From, State) when is_function(State, 1) ->
Resp2 = case (catch State(Req)) of
- {ok, Resp} -> {reply, {ok, Resp}, was_ok};
- {raw, Resp} -> {reply, {raw, Resp}, was_ok};
- {chunked, Resp} -> {reply, {chunked, Resp}, was_ok};
- Error -> {reply, {error, Error}, not_ok}
+ {ok, Resp} ->
+ {reply, {ok, Resp}, was_ok};
+ {raw, Resp} ->
+ {reply, {raw, Resp}, was_ok};
+ {chunked, Resp} ->
+ {reply, {chunked, Resp}, was_ok};
+ Error ->
+ {reply, {error, Error}, not_ok}
end,
Req:cleanup(),
Resp2;
@@ -87,12 +98,14 @@ handle_call({set_assert, _}, _From, State) ->
handle_call(Msg, _From, State) ->
{reply, {ignored, Msg}, State}.
+handle_cast(stop, State) ->
+ {stop, normal, State};
handle_cast(Msg, State) ->
- etap:diag("Ignoring cast message: ~p", [Msg]),
+ ?debugFmt("Ignoring cast message: ~p", [Msg]),
{noreply, State}.
handle_info(Msg, State) ->
- etap:diag("Ignoring info message: ~p", [Msg]),
+ ?debugFmt("Ignoring info message: ~p", [Msg]),
{noreply, State}.
code_change(_OldVsn, State, _Extra) ->
diff --git a/test/etap/180-http-proxy.ini b/test/etap/180-http-proxy.ini
deleted file mode 100644
index 3e2ba1379..000000000
--- a/test/etap/180-http-proxy.ini
+++ /dev/null
@@ -1,20 +0,0 @@
-; Licensed to the Apache Software Foundation (ASF) under one
-; or more contributor license agreements. See the NOTICE file
-; distributed with this work for additional information
-; regarding copyright ownership. The ASF licenses this file
-; to you under the Apache License, Version 2.0 (the
-; "License"); you may not use this file except in compliance
-; with the License. You may obtain a copy of the License at
-;
-; http://www.apache.org/licenses/LICENSE-2.0
-;
-; Unless required by applicable law or agreed to in writing,
-; software distributed under the License is distributed on an
-; "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-; KIND, either express or implied. See the License for the
-; specific language governing permissions and limitations
-; under the License.
-
-; 49151 is IANA Reserved, let's assume no one is listening there
-[httpd_global_handlers]
-_error = {couch_httpd_proxy, handle_proxy_req, <<"http://127.0.0.1:49151/">>}
diff --git a/test/etap/180-http-proxy.t b/test/etap/180-http-proxy.t
deleted file mode 100755
index da6760364..000000000
--- a/test/etap/180-http-proxy.t
+++ /dev/null
@@ -1,376 +0,0 @@
-#!/usr/bin/env escript
-% Licensed under the Apache License, Version 2.0 (the "License"); you may not
-% use this file except in compliance with the License. You may obtain a copy of
-% the License at
-%
-% http://www.apache.org/licenses/LICENSE-2.0
-%
-% Unless required by applicable law or agreed to in writing, software
-% distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-% WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-% License for the specific language governing permissions and limitations under
-% the License.
-
--record(req, {method=get, path="", headers=[], body="", opts=[]}).
-
-server() ->
- lists:concat([
- "http://127.0.0.1:",
- mochiweb_socket_server:get(couch_httpd, port),
- "/_test/"
- ]).
-
-proxy() ->
- "http://127.0.0.1:" ++ integer_to_list(test_web:get_port()) ++ "/".
-
-external() -> "https://www.google.com/".
-
-main(_) ->
- test_util:init_code_path(),
-
- etap:plan(61),
- case (catch test()) of
- ok ->
- etap:end_tests();
- Other ->
- etap:diag("Test died abnormally: ~p", [Other]),
- etap:bail("Bad return value.")
- end,
- ok.
-
-check_request(Name, Req, Remote, Local) ->
- case Remote of
- no_remote -> ok;
- _ -> test_web:set_assert(Remote)
- end,
- Url = case proplists:lookup(url, Req#req.opts) of
- none -> server() ++ Req#req.path;
- {url, DestUrl} -> DestUrl
- end,
- Opts = [{headers_as_is, true} | Req#req.opts],
- Resp =ibrowse:send_req(
- Url, Req#req.headers, Req#req.method, Req#req.body, Opts
- ),
- %etap:diag("ibrowse response: ~p", [Resp]),
- case Local of
- no_local -> ok;
- _ -> etap:fun_is(Local, Resp, Name)
- end,
- case {Remote, Local} of
- {no_remote, _} ->
- ok;
- {_, no_local} ->
- ok;
- _ ->
- etap:is(test_web:check_last(), was_ok, Name ++ " - request handled")
- end,
- Resp.
-
-test() ->
- ExtraConfig = [test_util:source_file("test/etap/180-http-proxy.ini")],
- couch_server_sup:start_link(test_util:config_files() ++ ExtraConfig),
- ibrowse:start(),
- crypto:start(),
-
- % start the test_web server on a random port
- test_web:start_link(),
- Url = lists:concat([
- "{couch_httpd_proxy, handle_proxy_req, <<\"http://127.0.0.1:",
- test_web:get_port(),
- "/\">>}"
- ]),
- couch_config:set("httpd_global_handlers", "_test", Url, false),
-
- % let couch_httpd restart
- timer:sleep(100),
-
- test_basic(),
- test_alternate_status(),
- test_trailing_slash(),
- test_passes_header(),
- test_passes_host_header(),
- test_passes_header_back(),
- test_rewrites_location_headers(),
- test_doesnt_rewrite_external_locations(),
- test_rewrites_relative_location(),
- test_uses_same_version(),
- test_passes_body(),
- test_passes_eof_body_back(),
- test_passes_chunked_body(),
- test_passes_chunked_body_back(),
-
- test_connect_error(),
-
- ok.
-
-test_basic() ->
- Remote = fun(Req) ->
- 'GET' = Req:get(method),
- "/" = Req:get(path),
- 0 = Req:get(body_length),
- <<>> = Req:recv_body(),
- {ok, {200, [{"Content-Type", "text/plain"}], "ok"}}
- end,
- Local = fun({ok, "200", _, "ok"}) -> true; (_) -> false end,
- check_request("Basic proxy test", #req{}, Remote, Local).
-
-test_alternate_status() ->
- Remote = fun(Req) ->
- "/alternate_status" = Req:get(path),
- {ok, {201, [], "ok"}}
- end,
- Local = fun({ok, "201", _, "ok"}) -> true; (_) -> false end,
- Req = #req{path="alternate_status"},
- check_request("Alternate status", Req, Remote, Local).
-
-test_trailing_slash() ->
- Remote = fun(Req) ->
- "/trailing_slash/" = Req:get(path),
- {ok, {200, [], "ok"}}
- end,
- Local = fun({ok, "200", _, "ok"}) -> true; (_) -> false end,
- Req = #req{path="trailing_slash/"},
- check_request("Trailing slash", Req, Remote, Local).
-
-test_passes_header() ->
- Remote = fun(Req) ->
- "/passes_header" = Req:get(path),
- "plankton" = Req:get_header_value("X-CouchDB-Ralph"),
- {ok, {200, [], "ok"}}
- end,
- Local = fun({ok, "200", _, "ok"}) -> true; (_) -> false end,
- Req = #req{
- path="passes_header",
- headers=[{"X-CouchDB-Ralph", "plankton"}]
- },
- check_request("Passes header", Req, Remote, Local).
-
-test_passes_host_header() ->
- Remote = fun(Req) ->
- "/passes_host_header" = Req:get(path),
- "www.google.com" = Req:get_header_value("Host"),
- {ok, {200, [], "ok"}}
- end,
- Local = fun({ok, "200", _, "ok"}) -> true; (_) -> false end,
- Req = #req{
- path="passes_host_header",
- headers=[{"Host", "www.google.com"}]
- },
- check_request("Passes host header", Req, Remote, Local).
-
-test_passes_header_back() ->
- Remote = fun(Req) ->
- "/passes_header_back" = Req:get(path),
- {ok, {200, [{"X-CouchDB-Plankton", "ralph"}], "ok"}}
- end,
- Local = fun
- ({ok, "200", Headers, "ok"}) ->
- lists:member({"X-CouchDB-Plankton", "ralph"}, Headers);
- (_) ->
- false
- end,
- Req = #req{path="passes_header_back"},
- check_request("Passes header back", Req, Remote, Local).
-
-test_rewrites_location_headers() ->
- etap:diag("Testing location header rewrites."),
- do_rewrite_tests([
- {"Location", proxy() ++ "foo/bar", server() ++ "foo/bar"},
- {"Content-Location", proxy() ++ "bing?q=2", server() ++ "bing?q=2"},
- {"Uri", proxy() ++ "zip#frag", server() ++ "zip#frag"},
- {"Destination", proxy(), server()}
- ]).
-
-test_doesnt_rewrite_external_locations() ->
- etap:diag("Testing no rewrite of external locations."),
- do_rewrite_tests([
- {"Location", external() ++ "search", external() ++ "search"},
- {"Content-Location", external() ++ "s?q=2", external() ++ "s?q=2"},
- {"Uri", external() ++ "f#f", external() ++ "f#f"},
- {"Destination", external() ++ "f?q=2#f", external() ++ "f?q=2#f"}
- ]).
-
-test_rewrites_relative_location() ->
- etap:diag("Testing relative rewrites."),
- do_rewrite_tests([
- {"Location", "/foo", server() ++ "foo"},
- {"Content-Location", "bar", server() ++ "bar"},
- {"Uri", "/zing?q=3", server() ++ "zing?q=3"},
- {"Destination", "bing?q=stuff#yay", server() ++ "bing?q=stuff#yay"}
- ]).
-
-do_rewrite_tests(Tests) ->
- lists:foreach(fun({Header, Location, Url}) ->
- do_rewrite_test(Header, Location, Url)
- end, Tests).
-
-do_rewrite_test(Header, Location, Url) ->
- Remote = fun(Req) ->
- "/rewrite_test" = Req:get(path),
- {ok, {302, [{Header, Location}], "ok"}}
- end,
- Local = fun
- ({ok, "302", Headers, "ok"}) ->
- etap:is(
- couch_util:get_value(Header, Headers),
- Url,
- "Header rewritten correctly."
- ),
- true;
- (_) ->
- false
- end,
- Req = #req{path="rewrite_test"},
- Label = "Rewrite test for ",
- check_request(Label ++ Header, Req, Remote, Local).
-
-test_uses_same_version() ->
- Remote = fun(Req) ->
- "/uses_same_version" = Req:get(path),
- {1, 0} = Req:get(version),
- {ok, {200, [], "ok"}}
- end,
- Local = fun({ok, "200", _, "ok"}) -> true; (_) -> false end,
- Req = #req{
- path="uses_same_version",
- opts=[{http_vsn, {1, 0}}]
- },
- check_request("Uses same version", Req, Remote, Local).
-
-test_passes_body() ->
- Remote = fun(Req) ->
- 'PUT' = Req:get(method),
- "/passes_body" = Req:get(path),
- <<"Hooray!">> = Req:recv_body(),
- {ok, {201, [], "ok"}}
- end,
- Local = fun({ok, "201", _, "ok"}) -> true; (_) -> false end,
- Req = #req{
- method=put,
- path="passes_body",
- body="Hooray!"
- },
- check_request("Passes body", Req, Remote, Local).
-
-test_passes_eof_body_back() ->
- BodyChunks = [<<"foo">>, <<"bar">>, <<"bazinga">>],
- Remote = fun(Req) ->
- 'GET' = Req:get(method),
- "/passes_eof_body" = Req:get(path),
- {raw, {200, [{"Connection", "close"}], BodyChunks}}
- end,
- Local = fun({ok, "200", _, "foobarbazinga"}) -> true; (_) -> false end,
- Req = #req{path="passes_eof_body"},
- check_request("Passes eof body", Req, Remote, Local).
-
-test_passes_chunked_body() ->
- BodyChunks = [<<"foo">>, <<"bar">>, <<"bazinga">>],
- Remote = fun(Req) ->
- 'POST' = Req:get(method),
- "/passes_chunked_body" = Req:get(path),
- RecvBody = fun
- ({Length, Chunk}, [Chunk | Rest]) ->
- Length = size(Chunk),
- Rest;
- ({0, []}, []) ->
- ok
- end,
- ok = Req:stream_body(1024*1024, RecvBody, BodyChunks),
- {ok, {201, [], "ok"}}
- end,
- Local = fun({ok, "201", _, "ok"}) -> true; (_) -> false end,
- Req = #req{
- method=post,
- path="passes_chunked_body",
- headers=[{"Transfer-Encoding", "chunked"}],
- body=mk_chunked_body(BodyChunks)
- },
- check_request("Passes chunked body", Req, Remote, Local).
-
-test_passes_chunked_body_back() ->
- Name = "Passes chunked body back",
- Remote = fun(Req) ->
- 'GET' = Req:get(method),
- "/passes_chunked_body_back" = Req:get(path),
- BodyChunks = [<<"foo">>, <<"bar">>, <<"bazinga">>],
- {chunked, {200, [{"Transfer-Encoding", "chunked"}], BodyChunks}}
- end,
- Req = #req{
- path="passes_chunked_body_back",
- opts=[{stream_to, self()}]
- },
-
- Resp = check_request(Name, Req, Remote, no_local),
-
- etap:fun_is(
- fun({ibrowse_req_id, _}) -> true; (_) -> false end,
- Resp,
- "Received an ibrowse request id."
- ),
- {_, ReqId} = Resp,
-
- % Grab headers from response
- receive
- {ibrowse_async_headers, ReqId, "200", Headers} ->
- etap:is(
- proplists:get_value("Transfer-Encoding", Headers),
- "chunked",
- "Response included the Transfer-Encoding: chunked header"
- ),
- ibrowse:stream_next(ReqId)
- after 1000 ->
- throw({error, timeout})
- end,
-
- % Check body received
- % TODO: When we upgrade to ibrowse >= 2.0.0 this check needs to
- % check that the chunks returned are what we sent from the
- % Remote test.
- etap:diag("TODO: UPGRADE IBROWSE"),
- etap:is(recv_body(ReqId, []), <<"foobarbazinga">>, "Decoded chunked body."),
-
- % Check test_web server.
- etap:is(test_web:check_last(), was_ok, Name ++ " - request handled").
-
-test_connect_error() ->
- Local = fun({ok, "500", _Headers, _Body}) -> true; (_) -> false end,
- Url = lists:concat([
- "http://127.0.0.1:",
- mochiweb_socket_server:get(couch_httpd, port),
- "/_error"
- ]),
- Req = #req{opts=[{url, Url}]},
- check_request("Connect error", Req, no_remote, Local).
-
-
-mk_chunked_body(Chunks) ->
- mk_chunked_body(Chunks, []).
-
-mk_chunked_body([], Acc) ->
- iolist_to_binary(lists:reverse(Acc, "0\r\n\r\n"));
-mk_chunked_body([Chunk | Rest], Acc) ->
- Size = to_hex(size(Chunk)),
- mk_chunked_body(Rest, ["\r\n", Chunk, "\r\n", Size | Acc]).
-
-to_hex(Val) ->
- to_hex(Val, []).
-
-to_hex(0, Acc) ->
- Acc;
-to_hex(Val, Acc) ->
- to_hex(Val div 16, [hex_char(Val rem 16) | Acc]).
-
-hex_char(V) when V < 10 -> $0 + V;
-hex_char(V) -> $A + V - 10.
-
-recv_body(ReqId, Acc) ->
- receive
- {ibrowse_async_response, ReqId, Data} ->
- recv_body(ReqId, [Data | Acc]);
- {ibrowse_async_response_end, ReqId} ->
- iolist_to_binary(lists:reverse(Acc));
- Else ->
- throw({error, unexpected_mesg, Else})
- after 5000 ->
- throw({error, timeout})
- end.
diff --git a/test/etap/Makefile.am b/test/etap/Makefile.am
index 05a7870ca..b92faf13c 100644
--- a/test/etap/Makefile.am
+++ b/test/etap/Makefile.am
@@ -11,7 +11,7 @@
## the License.
noinst_SCRIPTS = run
-noinst_DATA = test_util.beam test_web.beam
+noinst_DATA = test_util.beam
%.beam: %.erl
$(ERLC) $<
@@ -32,8 +32,6 @@ fixture_files = \
fixtures/test.couch
tap_files = \
- 180-http-proxy.ini \
- 180-http-proxy.t \
190-json-stream-parse.t \
200-view-group-no-db-leaks.t \
201-view-group-shutdown.t \
@@ -45,6 +43,5 @@ tap_files = \
EXTRA_DIST = \
run.tpl \
- test_web.erl \
$(fixture_files) \
$(tap_files)