diff options
author | Benoit Chesneau <benoitc@apache.org> | 2010-08-17 04:11:02 +0000 |
---|---|---|
committer | Benoit Chesneau <benoitc@apache.org> | 2010-08-17 04:11:02 +0000 |
commit | 84ad2322c28a4d40463dbe031ee0778d50d6b18a (patch) | |
tree | 60700763c0b4c34ab2d99e8a4989edac722a596e | |
parent | 64da83d7ae72bb23030c3b5c0e810225e09cb706 (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.in | 1 | ||||
-rw-r--r-- | src/couchdb/Makefile.am | 2 | ||||
-rw-r--r-- | src/couchdb/couch_httpd.erl | 76 | ||||
-rwxr-xr-x | test/etap/160-vhosts.t | 65 |
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. |