summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBenoit Chesneau <benoitc@apache.org>2010-08-17 04:11:02 +0000
committerBenoit Chesneau <benoitc@apache.org>2010-08-17 04:11:02 +0000
commit84ad2322c28a4d40463dbe031ee0778d50d6b18a (patch)
tree60700763c0b4c34ab2d99e8a4989edac722a596e
parent64da83d7ae72bb23030c3b5c0e810225e09cb706 (diff)
New vhost manager. allows dynamic add of vhosts without restart,
wildcard in vhost and dynamic routing via pattern matching. Close #COUCHDB-855. git-svn-id: https://svn.apache.org/repos/asf/couchdb/trunk@986182 13f79535-47bb-0310-9956-ffa450edef68
-rw-r--r--etc/couchdb/default.ini.tpl.in1
-rw-r--r--src/couchdb/Makefile.am2
-rw-r--r--src/couchdb/couch_httpd.erl76
-rwxr-xr-xtest/etap/160-vhosts.t65
4 files changed, 70 insertions, 74 deletions
diff --git a/etc/couchdb/default.ini.tpl.in b/etc/couchdb/default.ini.tpl.in
index 872b1444..456f16ee 100644
--- a/etc/couchdb/default.ini.tpl.in
+++ b/etc/couchdb/default.ini.tpl.in
@@ -64,6 +64,7 @@ stats_collector={couch_stats_collector, start, []}
uuids={couch_uuids, start, []}
auth_cache={couch_auth_cache, start_link, []}
rep_db_changes_listener={couch_rep_db_listener, start_link, []}
+vhosts={couch_httpd_vhost, start_link, []}
[httpd_global_handlers]
/ = {couch_httpd_misc_handlers, handle_welcome_req, <<"Welcome">>}
diff --git a/src/couchdb/Makefile.am b/src/couchdb/Makefile.am
index 219f7d82..badfb104 100644
--- a/src/couchdb/Makefile.am
+++ b/src/couchdb/Makefile.am
@@ -52,6 +52,7 @@ source_files = \
couch_httpd_misc_handlers.erl \
couch_httpd_rewrite.erl \
couch_httpd_stats_handlers.erl \
+ couch_httpd_vhost.erl \
couch_key_tree.erl \
couch_log.erl \
couch_native_process.erl \
@@ -111,6 +112,7 @@ compiled_files = \
couch_httpd_misc_handlers.beam \
couch_httpd_rewrite.beam \
couch_httpd_stats_handlers.beam \
+ couch_httpd_vhost.beam \
couch_key_tree.beam \
couch_log.beam \
couch_native_process.beam \
diff --git a/src/couchdb/couch_httpd.erl b/src/couchdb/couch_httpd.erl
index b5fe6cce..ecb4421d 100644
--- a/src/couchdb/couch_httpd.erl
+++ b/src/couchdb/couch_httpd.erl
@@ -13,7 +13,7 @@
-module(couch_httpd).
-include("couch_db.hrl").
--export([start_link/0, start_link/1, stop/0, handle_request/7]).
+-export([start_link/0, start_link/1, stop/0, handle_request/5]).
-export([header_value/2,header_value/3,qs_value/2,qs_value/3,qs/1,path/1,absolute_uri/2,body_length/1]).
-export([verify_is_server_admin/1,unquote/1,quote/1,recv/2,recv_chunked/4,error_info/1]).
@@ -26,6 +26,7 @@
-export([send_response/4,send_method_not_allowed/2,send_error/4, send_redirect/2,send_chunked_error/2]).
-export([send_json/2,send_json/3,send_json/4,last_chunk/1,parse_multipart_request/3]).
-export([accepted_encodings/1,handle_request_int/5,validate_referer/1,validate_ctype/2]).
+-export([make_arity_1_fun/1, make_arity_2_fun/1, make_arity_3_fun/1]).
start_link() ->
start_link(http).
@@ -56,12 +57,7 @@ start_link(Name, Options) ->
BindAddress = couch_config:get("httpd", "bind_address", any),
%% MaxConnections = couch_config:get("httpd", "max_connections", "2048"),
- VirtualHosts = couch_config:get("vhosts"),
- VhostGlobals = re:split(
- couch_config:get("httpd", "vhost_global_handlers", ""),
- ", ?",
- [{return, list}]
- ),
+
DefaultSpec = "{couch_httpd_db, handle_request}",
DefaultFun = make_arity_1_fun(
couch_config:get("httpd", "default_handler", DefaultSpec)
@@ -87,8 +83,7 @@ start_link(Name, Options) ->
DesignUrlHandlers = dict:from_list(DesignUrlHandlersList),
Loop = fun(Req)->
apply(?MODULE, handle_request, [
- Req, DefaultFun, UrlHandlers, DbUrlHandlers, DesignUrlHandlers,
- VirtualHosts, VhostGlobals
+ Req, DefaultFun, UrlHandlers, DbUrlHandlers, DesignUrlHandlers
])
end,
@@ -159,54 +154,13 @@ make_fun_spec_strs(SpecStr) ->
stop() ->
mochiweb_http:stop(?MODULE).
-%%
-% if there's a vhost definition that matches the request, redirect internally
-redirect_to_vhost(MochiReq, DefaultFun,
- UrlHandlers, DbUrlHandlers, DesignUrlHandlers, VhostTarget) ->
-
- Path = MochiReq:get(raw_path),
- Target = VhostTarget ++ Path,
- ?LOG_DEBUG("Vhost Target: '~p'~n", [Target]),
-
- Headers = mochiweb_headers:enter("x-couchdb-vhost-path", Path,
- MochiReq:get(headers)),
-
- % build a new mochiweb request
- MochiReq1 = mochiweb_request:new(MochiReq:get(socket),
- MochiReq:get(method),
- Target,
- MochiReq:get(version),
- Headers),
- % cleanup, It force mochiweb to reparse raw uri.
- MochiReq1:cleanup(),
+handle_request(MochiReq, DefaultFun, UrlHandlers, DbUrlHandlers,
+ DesignUrlHandlers) ->
+ MochiReq1 = couch_httpd_vhost:match_vhost(MochiReq),
handle_request_int(MochiReq1, DefaultFun,
- UrlHandlers, DbUrlHandlers, DesignUrlHandlers).
-
-handle_request(MochiReq, DefaultFun,
- UrlHandlers, DbUrlHandlers, DesignUrlHandlers, VirtualHosts, VhostGlobals) ->
-
- % grab Host from Req
- Vhost = MochiReq:get_header_value("Host"),
-
- % find Vhost in config
- case couch_util:get_value(Vhost, VirtualHosts) of
- undefined -> % business as usual
- handle_request_int(MochiReq, DefaultFun,
- UrlHandlers, DbUrlHandlers, DesignUrlHandlers);
- VhostTarget ->
- case vhost_global(VhostGlobals, MochiReq) of
- true ->% global handler for vhosts
- handle_request_int(MochiReq, DefaultFun,
- UrlHandlers, DbUrlHandlers, DesignUrlHandlers);
- _Else ->
- % do rewrite
- redirect_to_vhost(MochiReq, DefaultFun,
- UrlHandlers, DbUrlHandlers, DesignUrlHandlers, VhostTarget)
- end
- end.
-
+ UrlHandlers, DbUrlHandlers, DesignUrlHandlers).
handle_request_int(MochiReq, DefaultFun,
UrlHandlers, DbUrlHandlers, DesignUrlHandlers) ->
@@ -273,8 +227,6 @@ handle_request_int(MochiReq, DefaultFun,
Other -> Other
end,
-
-
HttpReq = #httpd{
mochi_req = MochiReq,
peer = MochiReq:get(peer),
@@ -362,18 +314,6 @@ authenticate_request(Response, _AuthSrcs) ->
increment_method_stats(Method) ->
couch_stats_collector:increment({httpd_request_methods, Method}).
-% if so, then it will not be rewritten, but will run as a normal couchdb request.
-% normally you'd use this for _uuids _utils and a few of the others you want to keep available on vhosts. You can also use it to make databases 'global'.
-vhost_global(VhostGlobals, MochiReq) ->
- "/" ++ Path = MochiReq:get(path),
- Front = case partition(Path) of
- {"", "", ""} ->
- "/"; % Special case the root url handler
- {FirstPart, _, _} ->
- FirstPart
- end,
- [true] == [true||V <- VhostGlobals, V == Front].
-
validate_referer(Req) ->
Host = host_for_request(Req),
Referer = header_value(Req, "Referer", fail),
diff --git a/test/etap/160-vhosts.t b/test/etap/160-vhosts.t
index 45b3f893..b1050d7f 100755
--- a/test/etap/160-vhosts.t
+++ b/test/etap/160-vhosts.t
@@ -54,7 +54,7 @@ config_files() ->
main(_) ->
test_util:init_code_path(),
- etap:plan(6),
+ etap:plan(10),
case (catch test()) of
ok ->
etap:end_tests();
@@ -69,6 +69,7 @@ test() ->
ibrowse:start(),
crypto:start(),
+ timer:sleep(1000),
couch_server:delete(list_to_binary(dbname()), [admin_user_ctx()]),
{ok, Db} = couch_db:create(list_to_binary(dbname()), [admin_user_ctx()]),
@@ -101,9 +102,16 @@ test() ->
%% end boilerplate, start test
- couch_config:set("vhosts", "example.com", "/etap-test-db", false),
- couch_config:set("vhosts", "example1.com",
-"/etap-test-db/_design/doc1/_rewrite/", false),
+ ok = couch_config:set("vhosts", "example.com", "/etap-test-db", false),
+ ok = couch_config:set("vhosts", "*.example.com",
+ "/etap-test-db/_design/doc1/_rewrite", false),
+ ok = couch_config:set("vhosts", "example1.com",
+ "/etap-test-db/_design/doc1/_rewrite/", false),
+ ok = couch_config:set("vhosts","$appname.$dbname.example1.com",
+ "/$dbname/_design/$appname/_rewrite/", false),
+ ok = couch_config:set("vhosts", "$dbname.example1.com", "/$dbname", false),
+ ok = couch_config:set("vhosts", "*.example2.com", "/*", false),
+
test_regular_request(),
test_vhost_request(),
@@ -111,10 +119,16 @@ test() ->
test_vhost_request_with_global(),
test_vhost_requested_path(),
test_vhost_requested_path_path(),
+ test_vhost_request_wildcard(),
+ test_vhost_request_replace_var(),
+ test_vhost_request_replace_var1(),
+ test_vhost_request_replace_wildcard(),
%% restart boilerplate
couch_db:close(Db),
- couch_server:delete(list_to_binary(dbname()), []),
+ timer:sleep(3000),
+ couch_server_sup:stop(),
+
ok.
test_regular_request() ->
@@ -166,7 +180,6 @@ test_vhost_requested_path() ->
_Else -> false
end.
-
test_vhost_requested_path_path() ->
case ibrowse:send_req(server(), [], get, [], [{host_header, "example1.com"}]) of
{ok, _, _, Body} ->
@@ -178,3 +191,43 @@ test_vhost_requested_path_path() ->
_Else -> false
end.
+test_vhost_request_wildcard()->
+ case ibrowse:send_req(server(), [], get, [], [{host_header, "test.example.com"}]) of
+ {ok, _, _, Body} ->
+ {Json} = couch_util:json_decode(Body),
+ etap:is(case proplists:get_value(<<"path">>, Json) of
+ <<"/etap-test-db/_design/doc1/_show/test">> -> true;
+ _ -> false
+ end, true, <<"wildcard ok">>);
+ _Else -> false
+ end.
+
+
+test_vhost_request_replace_var() ->
+ case ibrowse:send_req(server(), [], get, [], [{host_header,"etap-test-db.example1.com"}]) of
+ {ok, _, _, Body} ->
+ {[{<<"db_name">>, <<"etap-test-db">>},_,_,_,_,_,_,_,_,_]}
+ = couch_util:json_decode(Body),
+ etap:is(true, true, "should return database info");
+ _Else -> false
+ end.
+
+test_vhost_request_replace_var1() ->
+ case ibrowse:send_req(server(), [], get, [], [{host_header, "doc1.etap-test-db.example1.com"}]) of
+ {ok, _, _, Body} ->
+ {Json} = couch_util:json_decode(Body),
+ etap:is(case proplists:get_value(<<"path">>, Json) of
+ <<"/etap-test-db/_design/doc1/_show/test">> -> true;
+ _ -> false
+ end, true, <<"wildcard ok">>);
+ _Else -> false
+ end.
+
+test_vhost_request_replace_wildcard() ->
+ case ibrowse:send_req(server(), [], get, [], [{host_header,"etap-test-db.example2.com"}]) of
+ {ok, _, _, Body} ->
+ {[{<<"db_name">>, <<"etap-test-db">>},_,_,_,_,_,_,_,_,_]}
+ = couch_util:json_decode(Body),
+ etap:is(true, true, "should return database info");
+ _Else -> false
+ end.