No title Revision 613465323335 (Thu Feb 18 2010 at 23:19) - Diff Link to this snippet: https://friendpaste.com/6B5dQU1fb27boOvxhm2nhf Embed: manni perldoc borland colorful default murphy trac fruity autumn bw emacs pastie friendly Show line numbers Wrap lines 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245diff --git a/etc/couchdb/local.ini b/etc/couchdb/local.iniindex 96fcdc7..7399f15 100644--- a/etc/couchdb/local.ini+++ b/etc/couchdb/local.ini@@ -16,6 +16,16 @@ [log] ;level = debug ++; To enable Virtual Hosts in CouchDB, add a vhost = path directive. All requests to+; the Virual Host will be redirected to the path. In the example below all requests+; to http://example.com/ are redirected to /database.+; If you run CouchDB on a specific port, include the port number in the vhost:+; example.com:5984 = /database++[vhosts]+;example.com = /database/+ [update_notification] ;unique notifier name=/full/path/to/exe -with "cmd line arg" diff --git a/src/couchdb/couch_httpd.erl b/src/couchdb/couch_httpd.erlindex 6261600..ae7d6c0 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, stop/0, handle_request/5]).+-export([start_link/0, stop/0, handle_request/6]). -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]).@@ -25,7 +25,7 @@ -export([start_json_response/2, start_json_response/3, end_json_response/1]). -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]).+-export([accepted_encodings/1,handle_request_int/5]). start_link() -> % read config and register for configuration changes@@ -35,6 +35,7 @@ start_link() -> BindAddress = couch_config:get("httpd", "bind_address", any), Port = couch_config:get("httpd", "port", "5984"),+ VirtualHosts = couch_config:get("vhosts"), DefaultSpec = "{couch_httpd_db, handle_request}", DefaultFun = make_arity_1_fun(@@ -61,7 +62,8 @@ start_link() -> DesignUrlHandlers = dict:from_list(DesignUrlHandlersList), Loop = fun(Req)-> apply(?MODULE, handle_request, [- Req, DefaultFun, UrlHandlers, DbUrlHandlers, DesignUrlHandlers+ Req, DefaultFun, UrlHandlers, DbUrlHandlers, DesignUrlHandlers,+ VirtualHosts ]) end, @@ -89,6 +91,8 @@ start_link() -> ("httpd_global_handlers", _) -> ?MODULE:stop(); ("httpd_db_handlers", _) ->+ ?MODULE:stop();+ ("vhosts", _) -> ?MODULE:stop() end, Pid), @@ -127,9 +131,46 @@ 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(path),+ Target = VhostTarget ++ Path,+ ?LOG_DEBUG("Vhost Target: '~p'~n", [Target]),+ % build a new mochiweb request+ MochiReq1 = mochiweb_request:new(MochiReq:get(socket),+ MochiReq:get(method),+ Target,+ MochiReq:get(version),+ MochiReq:get(headers)),+ % cleanup, It force mochiweb to reparse raw uri.+ MochiReq1:cleanup(),++ handle_request_int(MochiReq1, DefaultFun,+ UrlHandlers, DbUrlHandlers, DesignUrlHandlers). handle_request(MochiReq, DefaultFun,- UrlHandlers, DbUrlHandlers, DesignUrlHandlers) ->+ UrlHandlers, DbUrlHandlers, DesignUrlHandlers, VirtualHosts) ->++ % grab Host from Req+ Vhost = MochiReq:get_header_value("Host"),++ % find Vhost in config+ case proplists:get_value(Vhost, VirtualHosts) of+ undefined -> % business as usual+ handle_request_int(MochiReq, DefaultFun,+ UrlHandlers, DbUrlHandlers, DesignUrlHandlers);+ VhostTarget ->+ redirect_to_vhost(MochiReq, DefaultFun,+ UrlHandlers, DbUrlHandlers, DesignUrlHandlers, VhostTarget)+ end.+++handle_request_int(MochiReq, DefaultFun,+ UrlHandlers, DbUrlHandlers, DesignUrlHandlers) -> Begin = now(), AuthenticationSrcs = make_fun_spec_strs( couch_config:get("httpd", "authentication_handlers")),diff --git a/src/couchdb/couch_httpd_misc_handlers.erl b/src/couchdb/couch_httpd_misc_handlers.erlindex 2d67b32..a854330 100644--- a/src/couchdb/couch_httpd_misc_handlers.erl+++ b/src/couchdb/couch_httpd_misc_handlers.erl@@ -46,6 +46,7 @@ handle_favicon_req(#httpd{method='GET'}=Req, DocumentRoot) -> {"Expires", httpd_util:rfc1123_date(OneYearFromNow)} ], couch_httpd:serve_file(Req, "favicon.ico", DocumentRoot, CachingHeaders);+ handle_favicon_req(Req, _) -> send_method_not_allowed(Req, "GET,HEAD"). diff --git a/src/couchdb/couch_httpd_rewrite.erl b/src/couchdb/couch_httpd_rewrite.erlindex 72ec954..7946809 100644--- a/src/couchdb/couch_httpd_rewrite.erl+++ b/src/couchdb/couch_httpd_rewrite.erl@@ -179,7 +179,7 @@ handle_rewrite_req(#httpd{ url_handlers = UrlHandlers } = Req, - couch_httpd:handle_request(MochiReq1, DefaultFun, + couch_httpd:handle_request_int(MochiReq1, DefaultFun, UrlHandlers, DbUrlHandlers, DesignUrlHandlers) end. diff --git a/test/etap/160-vhosts.t b/test/etap/160-vhosts.tnew file mode 100755index 0000000..fa61cab--- /dev/null+++ b/test/etap/160-vhosts.t@@ -0,0 +1,96 @@+#!/usr/bin/env escript+%% -*- erlang -*-++% 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.++%% XXX: Figure out how to -include("couch_rep.hrl")+-record(http_db, {+ url,+ auth = [],+ resource = "",+ headers = [+ {"User-Agent", "CouchDB/"++couch_server:get_version()},+ {"Accept", "application/json"},+ {"Accept-Encoding", "gzip"}+ ],+ qs = [],+ method = get,+ body = nil,+ options = [+ {response_format,binary},+ {inactivity_timeout, 30000}+ ],+ retries = 10,+ pause = 1,+ conn = nil+}).++server() -> "http://127.0.0.1:5984/".+dbname() -> "etap-test-db".++config_files() ->+ lists:map(fun test_util:build_file/1, [+ "etc/couchdb/default_dev.ini",+ "etc/couchdb/local_dev.ini"+ ]).++main(_) ->+ test_util:init_code_path(),++ etap:plan(2),+ case (catch test()) of+ ok ->+ etap:end_tests();+ Other ->+ etap:diag(io_lib:format("Test died abnormally: ~p", [Other])),+ etap:bail(Other)+ end,+ ok.++test() ->+ couch_server_sup:start_link(config_files()),+ ibrowse:start(),+ crypto:start(),++ couch_server:delete(list_to_binary(dbname()), []),+ {ok, Db} = couch_db:create(list_to_binary(dbname()), []),++ %% end boilerplate, start test++ couch_config:set("vhosts", "example.com", "/etap-test-db", false),+ test_regular_request(),+ test_vhost_request(),++ %% restart boilerplate+ couch_db:close(Db),+ couch_server:delete(list_to_binary(dbname()), []),+ ok.++test_regular_request() ->+ case ibrowse:send_req(server(), [], get, []) of+ {ok, _, _, Body} ->+ {[{<<"couchdb">>, <<"Welcome">>},+ {<<"version">>,_}+ ]} = couch_util:json_decode(Body),+ etap:is(true, true, "should return server info");+ _Else -> false+ end.++test_vhost_request() ->+ case ibrowse:send_req(server(), [], get, [], [{host_header, "example.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.