diff options
author | Nick Vatamaniuc <vatamane@gmail.com> | 2021-04-13 23:27:10 -0400 |
---|---|---|
committer | Nick Vatamaniuc <nickva@users.noreply.github.com> | 2021-04-16 17:44:43 -0400 |
commit | e05a6bfc03e100e998de89cd99c8c9e0000d7acf (patch) | |
tree | 7b7b7e6812dd35685153b2b4169067e48e698645 | |
parent | ba6819b0086b2b80a62d8a094df889616d3744dc (diff) | |
download | couchdb-e05a6bfc03e100e998de89cd99c8c9e0000d7acf.tar.gz |
Close backend port and clean up url handlers
Backend (5986) port is closed. Requests to `/_node/_local/_config` and
a few other `_*` endpoints continues to work. Backend db access now
returns error code 410 (not_supported). Previously, it was accessing
couch_server and couch_files to create 3.x style local dbs.
Url handlers are updated to return `not_supported` for features which are not
coming back, and `not_implemented` for features which haven't been implemented
yet.
`parse_copy_destination_header/1` is the only function from `couch_httpd_db`
that's still needed, so it was moved to `chttpd_util` module.
`couch_httpd_db` handled `/_uuid` requests and that handler was moved to
`chttpd_misc` module.
"Welcome" endpoint (/) was updated to not call `clouseau_rpc:connected/0`.
Request handling in `couch_httpd` was removed, so most of the file is
now a bunch of utility functions mostly.
-rw-r--r-- | src/chttpd/src/chttpd.erl | 7 | ||||
-rw-r--r-- | src/chttpd/src/chttpd_db.erl | 146 | ||||
-rw-r--r-- | src/chttpd/src/chttpd_httpd_handlers.erl | 25 | ||||
-rw-r--r-- | src/chttpd/src/chttpd_misc.erl | 37 | ||||
-rw-r--r-- | src/chttpd/src/chttpd_node.erl | 46 | ||||
-rw-r--r-- | src/chttpd/src/chttpd_show.erl | 150 | ||||
-rw-r--r-- | src/chttpd/src/chttpd_util.erl | 41 | ||||
-rw-r--r-- | src/couch/src/couch.app.src | 34 | ||||
-rw-r--r-- | src/couch/src/couch_httpd.erl | 347 | ||||
-rw-r--r-- | src/couch/src/couch_secondary_sup.erl | 10 | ||||
-rw-r--r-- | src/couch/test/eunit/chttpd_endpoints_tests.erl | 18 | ||||
-rw-r--r-- | src/couch_replicator/src/couch_replicator_ids.erl | 2 |
12 files changed, 112 insertions, 751 deletions
diff --git a/src/chttpd/src/chttpd.erl b/src/chttpd/src/chttpd.erl index b1243755c..8567adaeb 100644 --- a/src/chttpd/src/chttpd.erl +++ b/src/chttpd/src/chttpd.erl @@ -123,6 +123,12 @@ start_link(Name, Options) -> end, ok = couch_httpd:validate_bind_address(IP), + % Ensure uuid is set so that concurrent replications + % get the same value. This used to in the backend (:5986) httpd + % start_link and was moved here for now. Ideally this should be set + % in FDB or coordinated across all the nodes + couch_server:get_uuid(), + set_auth_handlers(), Options1 = Options ++ [ @@ -153,7 +159,6 @@ stop() -> mochiweb_http:stop(?MODULE). handle_request(MochiReq0) -> - erlang:put(?REWRITE_COUNT, 0), MochiReq = couch_httpd_vhost:dispatch_host(MochiReq0), handle_request_int(MochiReq). diff --git a/src/chttpd/src/chttpd_db.erl b/src/chttpd/src/chttpd_db.erl index ac3d3b159..8b9905927 100644 --- a/src/chttpd/src/chttpd_db.erl +++ b/src/chttpd/src/chttpd_db.erl @@ -23,8 +23,7 @@ db_req/2, couch_doc_open/4,handle_changes_req/2, update_doc_result_to_json/1, update_doc_result_to_json/2, handle_design_info_req/3, handle_view_cleanup_req/2, - update_doc/4, http_code_from_status/1, - handle_partition_req/2]). + update_doc/4, http_code_from_status/1]). -import(chttpd, [send_json/2,send_json/3,send_json/4,send_method_not_allowed/2, @@ -275,80 +274,6 @@ handle_view_cleanup_req(Req, Db) -> ok = fabric2_index:cleanup(Db), send_json(Req, 202, {[{ok, true}]}). - -handle_partition_req(#httpd{path_parts=[_,_]}=_Req, _Db) -> - throw({bad_request, invalid_partition_req}); - -handle_partition_req(#httpd{method='GET', path_parts=[_,_,PartId]}=Req, Db) -> - couch_partition:validate_partition(PartId), - case couch_db:is_partitioned(Db) of - true -> - {ok, PartitionInfo} = fabric:get_partition_info(Db, PartId), - send_json(Req, {PartitionInfo}); - false -> - throw({bad_request, <<"database is not partitioned">>}) - end; - -handle_partition_req(#httpd{method='POST', - path_parts=[_, <<"_partition">>, <<"_", _/binary>>]}, _Db) -> - Msg = <<"Partition must not start with an underscore">>, - throw({illegal_partition, Msg}); - -handle_partition_req(#httpd{path_parts = [_, _, _]}=Req, _Db) -> - send_method_not_allowed(Req, "GET"); - -handle_partition_req(#httpd{path_parts=[DbName, _, PartId | Rest]}=Req, Db) -> - case couch_db:is_partitioned(Db) of - true -> - couch_partition:validate_partition(PartId), - QS = chttpd:qs(Req), - PartIdStr = ?b2l(PartId), - QSPartIdStr = couch_util:get_value("partition", QS, PartIdStr), - if QSPartIdStr == PartIdStr -> ok; true -> - Msg = <<"Conflicting value for `partition` in query string">>, - throw({bad_request, Msg}) - end, - NewQS = lists:ukeysort(1, [{"partition", PartIdStr} | QS]), - NewReq = Req#httpd{ - path_parts = [DbName | Rest], - qs = NewQS - }, - update_partition_stats(Rest), - case Rest of - [OP | _] when OP == <<"_all_docs">> orelse ?IS_MANGO(OP) -> - case chttpd_handlers:db_handler(OP, fun db_req/2) of - Handler when is_function(Handler, 2) -> - Handler(NewReq, Db); - _ -> - chttpd:send_error(Req, not_found) - end; - [<<"_design">>, _Name, <<"_", _/binary>> | _] -> - handle_design_req(NewReq, Db); - _ -> - chttpd:send_error(Req, not_found) - end; - false -> - throw({bad_request, <<"database is not partitioned">>}) - end; - -handle_partition_req(Req, _Db) -> - chttpd:send_error(Req, not_found). - -update_partition_stats(PathParts) -> - case PathParts of - [<<"_design">> | _] -> - couch_stats:increment_counter([couchdb, httpd, partition_view_requests]); - [<<"_all_docs">> | _] -> - couch_stats:increment_counter([couchdb, httpd, partition_all_docs_requests]); - [<<"_find">> | _] -> - couch_stats:increment_counter([couchdb, httpd, partition_find_requests]); - [<<"_explain">> | _] -> - couch_stats:increment_counter([couchdb, httpd, partition_explain_requests]); - _ -> - ok % ignore path that do not match - end. - - handle_design_req(#httpd{ path_parts=[_DbName, _Design, Name, <<"_",_/binary>> = Action | _Rest] }=Req, Db) -> @@ -635,41 +560,6 @@ db_req(#httpd{method='POST', path_parts=[_, <<"_bulk_get">>], db_req(#httpd{path_parts=[_, <<"_bulk_get">>]}=Req, _Db) -> send_method_not_allowed(Req, "POST"); - -db_req(#httpd{method='POST',path_parts=[_,<<"_purge">>]}=Req, Db) -> - couch_stats:increment_counter([couchdb, httpd, purge_requests]), - chttpd:validate_ctype(Req, "application/json"), - {IdsRevs} = chttpd:json_body_obj(Req), - IdsRevs2 = [{Id, couch_doc:parse_revs(Revs)} || {Id, Revs} <- IdsRevs], - MaxIds = config:get_integer("purge", "max_document_id_number", 100), - case length(IdsRevs2) =< MaxIds of - false -> throw({bad_request, "Exceeded maximum number of documents."}); - true -> ok - end, - RevsLen = lists:foldl(fun({_Id, Revs}, Acc) -> - length(Revs) + Acc - end, 0, IdsRevs2), - MaxRevs = config:get_integer("purge", "max_revisions_number", 1000), - case RevsLen =< MaxRevs of - false -> throw({bad_request, "Exceeded maximum number of revisions."}); - true -> ok - end, - couch_stats:increment_counter([couchdb, document_purges, total], length(IdsRevs2)), - Results2 = case fabric:purge_docs(Db, IdsRevs2, []) of - {ok, Results} -> - chttpd_stats:incr_writes(length(Results)), - Results; - {accepted, Results} -> - chttpd_stats:incr_writes(length(Results)), - Results - end, - {Code, Json} = purge_results_to_json(IdsRevs2, Results2), - send_json(Req, Code, {[{<<"purge_seq">>, null}, {<<"purged">>, {Json}}]}); - -db_req(#httpd{path_parts=[_,<<"_purge">>]}=Req, _Db) -> - send_method_not_allowed(Req, "POST"); - - db_req(#httpd{method='GET',path_parts=[_,OP]}=Req, Db) when ?IS_ALL_DOCS(OP) -> case chttpd:qs_json_value(Req, "keys", nil) of Keys when is_list(Keys) -> @@ -778,22 +668,6 @@ db_req(#httpd{method='GET',path_parts=[_,<<"_revs_limit">>]}=Req, Db) -> db_req(#httpd{path_parts=[_,<<"_revs_limit">>]}=Req, _Db) -> send_method_not_allowed(Req, "PUT,GET"); -db_req(#httpd{method='PUT',path_parts=[_,<<"_purged_infos_limit">>]}=Req, Db) -> - case chttpd:json_body(Req) of - Limit when is_integer(Limit), Limit > 0 -> - case fabric:set_purge_infos_limit(Db, Limit, []) of - ok -> - send_json(Req, {[{<<"ok">>, true}]}); - Error -> - throw(Error) - end; - _-> - throw({bad_request, "`purge_infos_limit` must be positive integer"}) - end; - -db_req(#httpd{method='GET',path_parts=[_,<<"_purged_infos_limit">>]}=Req, Db) -> - send_json(Req, fabric:get_purge_infos_limit(Db)); - % Special case to enable using an unencoded slash in the URL of design docs, % as slashes in document IDs must otherwise be URL encoded. db_req(#httpd{method='GET', mochi_req=MochiReq, path_parts=[_DbName, <<"_design/", _/binary>> | _]}=Req, _Db) -> @@ -1444,24 +1318,6 @@ update_doc_result_to_json(DocId, Error) -> {_Code, ErrorStr, Reason} = chttpd:error_info(Error), {[{id, DocId}, {error, ErrorStr}, {reason, Reason}]}. -purge_results_to_json([], []) -> - {201, []}; -purge_results_to_json([{DocId, _Revs} | RIn], [{ok, PRevs} | ROut]) -> - {Code, Results} = purge_results_to_json(RIn, ROut), - couch_stats:increment_counter([couchdb, document_purges, success]), - {Code, [{DocId, couch_doc:revs_to_strs(PRevs)} | Results]}; -purge_results_to_json([{DocId, _Revs} | RIn], [{accepted, PRevs} | ROut]) -> - {Code, Results} = purge_results_to_json(RIn, ROut), - couch_stats:increment_counter([couchdb, document_purges, success]), - NewResults = [{DocId, couch_doc:revs_to_strs(PRevs)} | Results], - {erlang:max(Code, 202), NewResults}; -purge_results_to_json([{DocId, _Revs} | RIn], [Error | ROut]) -> - {Code, Results} = purge_results_to_json(RIn, ROut), - {NewCode, ErrorStr, Reason} = chttpd:error_info(Error), - couch_stats:increment_counter([couchdb, document_purges, failure]), - NewResults = [{DocId, {[{error, ErrorStr}, {reason, Reason}]}} | Results], - {erlang:max(NewCode, Code), NewResults}. - send_updated_doc(Req, Db, DocId, Json) -> send_updated_doc(Req, Db, DocId, Json, []). diff --git a/src/chttpd/src/chttpd_httpd_handlers.erl b/src/chttpd/src/chttpd_httpd_handlers.erl index d50115917..e5374b1b6 100644 --- a/src/chttpd/src/chttpd_httpd_handlers.erl +++ b/src/chttpd/src/chttpd_httpd_handlers.erl @@ -15,9 +15,11 @@ -export([url_handler/1, db_handler/1, design_handler/1, handler_info/3]). -export([ - not_supported/2, not_supported/3, - not_implemented/2 + not_supported/2, + not_supported/1, + not_implemented/2, + not_implemented/1 ]). @@ -38,16 +40,22 @@ url_handler(<<"_replicate">>) -> fun chttpd_misc:handle_replicate_req/1; url_handler(<<"_uuids">>) -> fun chttpd_misc:handle_uuids_req/1; url_handler(<<"_session">>) -> fun chttpd_auth:handle_session_req/1; url_handler(<<"_up">>) -> fun chttpd_misc:handle_up_req/1; +url_handler(<<"_membership">>) -> fun ?MODULE:not_supported/1; +url_handler(<<"_reshard">>) -> fun ?MODULE:not_supported/1; +url_handler(<<"_db_updates">>) -> fun ?MODULE:not_implemented/1; +url_handler(<<"_cluster_setup">>) -> fun ?MODULE:not_implemented/1; url_handler(_) -> no_match. db_handler(<<"_view_cleanup">>) -> fun chttpd_db:handle_view_cleanup_req/2; db_handler(<<"_compact">>) -> fun chttpd_db:handle_compact_req/2; db_handler(<<"_design">>) -> fun chttpd_db:handle_design_req/2; -db_handler(<<"_partition">>) -> fun chttpd_db:handle_partition_req/2; +db_handler(<<"_partition">>) -> fun ?MODULE:not_implemented/2; db_handler(<<"_temp_view">>) -> fun ?MODULE:not_supported/2; db_handler(<<"_changes">>) -> fun chttpd_db:handle_changes_req/2; db_handler(<<"_purge">>) -> fun ?MODULE:not_implemented/2; db_handler(<<"_purged_infos_limit">>) -> fun ?MODULE:not_implemented/2; +db_handler(<<"_shards">>) -> fun ?MODULE:not_supported/2; +db_handler(<<"_sync_shards">>) -> fun ?MODULE:not_supported/2; db_handler(_) -> no_match. design_handler(<<"_view">>) -> fun chttpd_view:handle_view_req/3; @@ -186,7 +194,6 @@ handler_info(Method, [<<"_", _/binary>> = Part| Rest], Req) -> % on for known system databases. DbName = case Part of <<"_dbs">> -> '_dbs'; - <<"_global_changes">> -> '_global_changes'; <<"_metadata">> -> '_metadata'; <<"_nodes">> -> '_nodes'; <<"_replicator">> -> '_replicator'; @@ -497,7 +504,7 @@ handler_info(_, _, _) -> get_copy_destination(Req) -> try - {DocIdStr, _} = couch_httpd_db:parse_copy_destination_header(Req), + {DocIdStr, _} = chttpd_util:parse_copy_destination_header(Req), list_to_binary(mochiweb_util:unquote(DocIdStr)) catch _:_ -> unknown @@ -509,10 +516,18 @@ not_supported(#httpd{} = Req, Db, _DDoc) -> not_supported(#httpd{} = Req, _Db) -> + not_supported(Req). + + +not_supported(#httpd{} = Req) -> Msg = <<"resource is not supported in CouchDB >= 4.x">>, chttpd:send_error(Req, 410, gone, Msg). not_implemented(#httpd{} = Req, _Db) -> + not_implemented(Req). + + +not_implemented(#httpd{} = Req) -> Msg = <<"resource is not implemented">>, chttpd:send_error(Req, 501, not_implemented, Msg). diff --git a/src/chttpd/src/chttpd_misc.erl b/src/chttpd/src/chttpd_misc.erl index 5cfd0f7cb..5d9706abf 100644 --- a/src/chttpd/src/chttpd_misc.erl +++ b/src/chttpd/src/chttpd_misc.erl @@ -33,7 +33,7 @@ -include_lib("couch_mrview/include/couch_mrview.hrl"). -import(chttpd, - [send_json/2,send_json/3,send_method_not_allowed/2, + [send_json/2,send_json/3,send_json/4,send_method_not_allowed/2, send_chunk/2,start_chunked_response/3]). -define(MAX_DB_NUM_FOR_DBS_INFO, 100). @@ -61,12 +61,7 @@ handle_welcome_req(Req, _) -> send_method_not_allowed(Req, "GET,HEAD"). get_features() -> - case clouseau_rpc:connected() of - true -> - [search | config:features()]; - false -> - config:features() - end. + config:features(). handle_favicon_req(Req) -> handle_favicon_req(Req, get_docroot()). @@ -334,9 +329,33 @@ handle_reload_query_servers_req(#httpd{method='POST'}=Req) -> handle_reload_query_servers_req(Req) -> send_method_not_allowed(Req, "POST"). +handle_uuids_req(#httpd{method='GET'}=Req) -> + Max = list_to_integer(config:get("uuids","max_count","1000")), + Count = try list_to_integer(couch_httpd:qs_value(Req, "count", "1")) of + N when N > Max -> + throw({bad_request, <<"count parameter too large">>}); + N when N < 0 -> + throw({bad_request, <<"count must be a positive integer">>}); + N -> N + catch + error:badarg -> + throw({bad_request, <<"count must be a positive integer">>}) + end, + UUIDs = [couch_uuids:new() || _ <- lists:seq(1, Count)], + Etag = couch_httpd:make_etag(UUIDs), + couch_httpd:etag_respond(Req, Etag, fun() -> + CacheBustingHeaders = [ + {"Date", couch_util:rfc1123_date()}, + {"Cache-Control", "no-cache"}, + % Past date, ON PURPOSE! + {"Expires", "Mon, 01 Jan 1990 00:00:00 GMT"}, + {"Pragma", "no-cache"}, + {"ETag", Etag} + ], + send_json(Req, 200, CacheBustingHeaders, {[{<<"uuids">>, UUIDs}]}) + end); handle_uuids_req(Req) -> - couch_httpd_misc_handlers:handle_uuids_req(Req). - + send_method_not_allowed(Req, "GET"). handle_up_req(#httpd{method='GET'} = Req) -> case config:get("couchdb", "maintenance_mode") of diff --git a/src/chttpd/src/chttpd_node.erl b/src/chttpd/src/chttpd_node.erl index b6c4fac6c..e36380aeb 100644 --- a/src/chttpd/src/chttpd_node.erl +++ b/src/chttpd/src/chttpd_node.erl @@ -138,54 +138,14 @@ handle_node_req(#httpd{method='POST', path_parts=[_, Node, <<"_restart">>]}=Req) send_json(Req, 200, {[{ok, true}]}); handle_node_req(#httpd{path_parts=[_, _Node, <<"_restart">>]}=Req) -> send_method_not_allowed(Req, "POST"); -handle_node_req(#httpd{path_parts=[_, Node | PathParts], - mochi_req=MochiReq0}) -> - % strip /_node/{node} from Req0 before descending further - RawUri = MochiReq0:get(raw_path), - {_, Query, Fragment} = mochiweb_util:urlsplit_path(RawUri), - NewPath0 = "/" ++ lists:join("/", [couch_util:url_encode(P) || P <- PathParts]), - NewRawPath = mochiweb_util:urlunsplit_path({NewPath0, Query, Fragment}), - MaxSize = config:get_integer("httpd", "max_http_request_size", 4294967296), - NewOpts = [{body, MochiReq0:recv_body(MaxSize)} | MochiReq0:get(opts)], - Ref = erlang:make_ref(), - MochiReq = mochiweb_request:new({remote, self(), Ref}, - NewOpts, - MochiReq0:get(method), - NewRawPath, - MochiReq0:get(version), - MochiReq0:get(headers)), - call_node(Node, couch_httpd, handle_request, [MochiReq]), - recv_loop(Ref, MochiReq0); +handle_node_req(#httpd{path_parts=[_, _Node | _PathParts]}=Req) -> + % Local (backend) dbs are not support any more + chttpd_httpd_handlers:not_supported(Req); handle_node_req(#httpd{path_parts=[_]}=Req) -> chttpd:send_error(Req, {bad_request, <<"Incomplete path to _node request">>}); handle_node_req(Req) -> chttpd:send_error(Req, not_found). -recv_loop(Ref, ReqResp) -> - receive - {Ref, Code, Headers, _Args, start_response} -> - recv_loop(Ref, ReqResp:start({Code, Headers})); - {Ref, Code, Headers, Len, start_response_length} -> - recv_loop(Ref, ReqResp:start_response_length({Code, Headers, Len})); - {Ref, Code, Headers, chunked, respond} -> - Resp = ReqResp:respond({Code, Headers, chunked}), - recv_loop(Ref, Resp); - {Ref, Code, Headers, Args, respond} -> - Resp = ReqResp:respond({Code, Headers, Args}), - {ok, Resp}; - {Ref, send, Data} -> - ReqResp:send(Data), - {ok, ReqResp}; - {Ref, chunk, <<>>} -> - ReqResp:write_chunk(<<>>), - {ok, ReqResp}; - {Ref, chunk, Data} -> - ReqResp:write_chunk(Data), - recv_loop(Ref, ReqResp); - _Else -> - recv_loop(Ref, ReqResp) - end. - call_node(Node0, Mod, Fun, Args) when is_binary(Node0) -> Node1 = try list_to_existing_atom(?b2l(Node0)) diff --git a/src/chttpd/src/chttpd_show.erl b/src/chttpd/src/chttpd_show.erl index 8a15bdcbe..295d753a0 100644 --- a/src/chttpd/src/chttpd_show.erl +++ b/src/chttpd/src/chttpd_show.erl @@ -12,15 +12,11 @@ -module(chttpd_show). --export([handle_doc_show_req/3, handle_doc_update_req/3, handle_view_list_req/3]). +-export([handle_doc_update_req/3]). -include_lib("couch/include/couch_db.hrl"). -include_lib("couch_mrview/include/couch_mrview.hrl"). -% /db/_design/foo/_show/bar/docid -% show converts a json doc to a response of any content-type. -% it looks up the doc an then passes it to the query server. -% then it sends the response from the query server to the http client. maybe_open_doc(Db, DocId, Options) -> case fabric:open_doc(Db, DocId, Options) of @@ -31,70 +27,6 @@ maybe_open_doc(Db, DocId, Options) -> nil end. -handle_doc_show_req(#httpd{ - path_parts=[_, _, _, _, ShowName, DocId] - }=Req, Db, DDoc) -> - - % open the doc - Options = [conflicts, {user_ctx, Req#httpd.user_ctx}], - Doc = maybe_open_doc(Db, DocId, Options), - - % we don't handle revs here b/c they are an internal api - % returns 404 if there is no doc with DocId - handle_doc_show(Req, Db, DDoc, ShowName, Doc, DocId); - -handle_doc_show_req(#httpd{ - path_parts=[_, _, _, _, ShowName, DocId|Rest] - }=Req, Db, DDoc) -> - - DocParts = [DocId|Rest], - DocId1 = ?l2b(string:join([?b2l(P)|| P <- DocParts], "/")), - - % open the doc - Options = [conflicts, {user_ctx, Req#httpd.user_ctx}], - Doc = maybe_open_doc(Db, DocId1, Options), - - % we don't handle revs here b/c they are an internal api - % pass 404 docs to the show function - handle_doc_show(Req, Db, DDoc, ShowName, Doc, DocId1); - -handle_doc_show_req(#httpd{ - path_parts=[_, _, _, _, ShowName] - }=Req, Db, DDoc) -> - % with no docid the doc is nil - handle_doc_show(Req, Db, DDoc, ShowName, nil); - -handle_doc_show_req(Req, _Db, _DDoc) -> - chttpd:send_error(Req, 404, <<"show_error">>, <<"Invalid path.">>). - -handle_doc_show(Req, Db, DDoc, ShowName, Doc) -> - handle_doc_show(Req, Db, DDoc, ShowName, Doc, null). - -handle_doc_show(Req, Db, DDoc, ShowName, Doc, DocId) -> - %% Will throw an exception if the _show handler is missing - couch_util:get_nested_json_value(DDoc#doc.body, [<<"shows">>, ShowName]), - % get responder for ddoc/showname - CurrentEtag = show_etag(Req, Doc, DDoc, []), - chttpd:etag_respond(Req, CurrentEtag, fun() -> - JsonReq = chttpd_external:json_req_obj(Req, Db, DocId), - JsonDoc = couch_query_servers:json_doc(Doc), - [<<"resp">>, ExternalResp] = - couch_query_servers:ddoc_prompt(DDoc, [<<"shows">>, ShowName], - [JsonDoc, JsonReq]), - JsonResp = apply_etag(ExternalResp, CurrentEtag), - chttpd_external:send_external_response(Req, JsonResp) - end). - - -show_etag(#httpd{user_ctx=UserCtx}=Req, Doc, DDoc, More) -> - Accept = chttpd:header_value(Req, "Accept"), - DocPart = case Doc of - nil -> nil; - Doc -> chttpd:doc_etag(Doc) - end, - couch_httpd:make_etag({couch_httpd:doc_etag(DDoc), DocPart, Accept, - UserCtx#user_ctx.roles, More}). - % /db/_design/foo/update/bar/docid % updates a doc based on a request % handle_doc_update_req(#httpd{method = 'GET'}=Req, _Db, _DDoc) -> @@ -154,86 +86,6 @@ send_doc_update_response(Req, Db, DDoc, UpdateName, Doc, DocId) -> % todo set location field chttpd_external:send_external_response(Req, JsonResp). - -% view-list request with view and list from same design doc. -handle_view_list_req(#httpd{method=Method, - path_parts=[_, _, DesignName, _, ListName, ViewName]}=Req, Db, DDoc) - when Method =:= 'GET' orelse Method =:= 'OPTIONS' -> - Keys = chttpd:qs_json_value(Req, "keys", undefined), - handle_view_list(Req, Db, DDoc, ListName, {DesignName, ViewName}, Keys); - -% view-list request with view and list from different design docs. -handle_view_list_req(#httpd{method=Method, - path_parts=[_, _, _, _, ListName, DesignName, ViewName]}=Req, Db, DDoc) - when Method =:= 'GET' orelse Method =:= 'OPTIONS' -> - Keys = chttpd:qs_json_value(Req, "keys", undefined), - handle_view_list(Req, Db, DDoc, ListName, {DesignName, ViewName}, Keys); - -handle_view_list_req(#httpd{method=Method}=Req, _Db, _DDoc) - when Method =:= 'GET' orelse Method =:= 'OPTIONS' -> - chttpd:send_error(Req, 404, <<"list_error">>, <<"Invalid path.">>); - -handle_view_list_req(#httpd{method='POST', - path_parts=[_, _, DesignName, _, ListName, ViewName]}=Req, Db, DDoc) -> - chttpd:validate_ctype(Req, "application/json"), - ReqBody = chttpd:body(Req), - {Props2} = ?JSON_DECODE(ReqBody), - Keys = proplists:get_value(<<"keys">>, Props2, undefined), - handle_view_list(Req#httpd{req_body=ReqBody}, Db, DDoc, ListName, - {DesignName, ViewName}, Keys); - -handle_view_list_req(#httpd{method='POST', - path_parts=[_, _, _, _, ListName, DesignName, ViewName]}=Req, Db, DDoc) -> - chttpd:validate_ctype(Req, "application/json"), - ReqBody = chttpd:body(Req), - {Props2} = ?JSON_DECODE(ReqBody), - Keys = proplists:get_value(<<"keys">>, Props2, undefined), - handle_view_list(Req#httpd{req_body=ReqBody}, Db, DDoc, ListName, - {DesignName, ViewName}, Keys); - -handle_view_list_req(#httpd{method='POST'}=Req, _Db, _DDoc) -> - chttpd:send_error(Req, 404, <<"list_error">>, <<"Invalid path.">>); - -handle_view_list_req(Req, _Db, _DDoc) -> - chttpd:send_method_not_allowed(Req, "GET,POST,HEAD"). - -handle_view_list(Req, Db, DDoc, LName, {ViewDesignName, ViewName}, Keys) -> - %% Will throw an exception if the _list handler is missing - couch_util:get_nested_json_value(DDoc#doc.body, [<<"lists">>, LName]), - DbName = couch_db:name(Db), - {ok, VDoc} = ddoc_cache:open(DbName, <<"_design/", ViewDesignName/binary>>), - CB = fun list_cb/2, - QueryArgs = couch_mrview_http:parse_body_and_query(Req, Keys), - Options = [{user_ctx, Req#httpd.user_ctx}], - couch_query_servers:with_ddoc_proc(DDoc, fun(QServer) -> - Acc = #lacc{ - lname = LName, - req = Req, - qserver = QServer, - db = Db - }, - case ViewName of - <<"_all_docs">> -> - fabric:all_docs(Db, Options, CB, Acc, QueryArgs); - _ -> - fabric:query_view(Db, Options, VDoc, ViewName, - CB, Acc, QueryArgs) - end - end). - - -list_cb({row, Row} = Msg, Acc) -> - case lists:keymember(doc, 1, Row) of - true -> chttpd_stats:incr_reads(); - false -> ok - end, - chttpd_stats:incr_rows(), - couch_mrview_show:list_cb(Msg, Acc); - -list_cb(Msg, Acc) -> - couch_mrview_show:list_cb(Msg, Acc). - - % Maybe this is in the proplists API % todo move to couch_util json_apply_field(H, {L}) -> diff --git a/src/chttpd/src/chttpd_util.erl b/src/chttpd/src/chttpd_util.erl new file mode 100644 index 000000000..fcaa09de0 --- /dev/null +++ b/src/chttpd/src/chttpd_util.erl @@ -0,0 +1,41 @@ +% 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(chttpd_util). + + +-export([ + parse_copy_destination_header/1 +]). + + +parse_copy_destination_header(Req) -> + case couch_httpd:header_value(Req, "Destination") of + undefined -> + throw({bad_request, "Destination header is mandatory for COPY."}); + Destination -> + case re:run(Destination, "^https?://", [{capture, none}]) of + match -> + throw({bad_request, "Destination URL must be relative."}); + nomatch -> + % see if ?rev=revid got appended to the Destination header + case re:run(Destination, "\\?", [{capture, none}]) of + nomatch -> + {list_to_binary(Destination), {0, []}}; + match -> + [DocId, RevQs] = re:split(Destination, "\\?", [{return, list}]), + [_RevQueryKey, Rev] = re:split(RevQs, "=", [{return, list}]), + {Pos, RevId} = couch_doc:parse_rev(Rev), + {list_to_binary(DocId), {Pos, [RevId]}} + end + end + end. diff --git a/src/couch/src/couch.app.src b/src/couch/src/couch.app.src index e411b5eab..af277c161 100644 --- a/src/couch/src/couch.app.src +++ b/src/couch/src/couch.app.src @@ -42,39 +42,5 @@ couch_stats, hyper, couch_prometheus - ]}, - {env, [ - { httpd_global_handlers, [ - {"/", "{couch_httpd_misc_handlers, handle_welcome_req, <<\"Welcome\">>}"}, - {"favicon.ico", "{couch_httpd_misc_handlers, handle_favicon_req, \"{{prefix}}/share/www\"}"}, - {"_utils", "{couch_httpd_misc_handlers, handle_utils_dir_req, \"{{prefix}}/share/www\"}"}, - {"_all_dbs", "{couch_httpd_misc_handlers, handle_all_dbs_req}"}, - {"_active_tasks", "{couch_httpd_misc_handlers, handle_task_status_req}"}, - {"_config", "{couch_httpd_misc_handlers, handle_config_req}"}, - {"_replicate", "{couch_replicator_httpd, handle_req}"}, - {"_uuids", "{couch_httpd_misc_handlers, handle_uuids_req}"}, - {"_stats", "{couch_stats_httpd, handle_stats_req}"}, - {"_session", "{couch_httpd_auth, handle_session_req}"}, - {"_plugins", "{couch_plugins_httpd, handle_req}"} - ]}, - { httpd_db_handlers, [ - {"_all_docs", "{couch_mrview_http, handle_all_docs_req}"}, - {"_local_docs", "{couch_mrview_http, handle_local_docs_req}"}, - {"_design_docs", "{couch_mrview_http, handle_design_docs_req}"}, - {"_changes", "{couch_httpd_db, handle_db_changes_req}"}, - {"_compact", "{couch_httpd_db, handle_compact_req}"}, - {"_design", "{couch_httpd_db, handle_design_req}"}, - {"_temp_view", "{couch_mrview_http, handle_temp_view_req}"}, - {"_view_cleanup", "{couch_mrview_http, handle_cleanup_req}"} - ]}, - { httpd_design_handlers, [ - {"_compact", "{couch_mrview_http, handle_compact_req}"}, - {"_info", "{couch_mrview_http, handle_info_req}"}, - {"_list", "{couch_mrview_show, handle_view_list_req}"}, - {"_rewrite", "{couch_httpd_rewrite, handle_rewrite_req}"}, - {"_show", "{couch_mrview_show, handle_doc_show_req}"}, - {"_update", "{couch_mrview_show, handle_doc_update_req}"}, - {"_view", "{couch_mrview_http, handle_view_req}"} - ]} ]} ]}. diff --git a/src/couch/src/couch_httpd.erl b/src/couch/src/couch_httpd.erl index d89c74957..fd83c258a 100644 --- a/src/couch/src/couch_httpd.erl +++ b/src/couch/src/couch_httpd.erl @@ -16,8 +16,6 @@ -include_lib("couch/include/couch_db.hrl"). --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,qs_json_value/3]). -export([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]). @@ -32,164 +30,17 @@ -export([send_response/4,send_response_no_cors/4,send_method_not_allowed/2, send_error/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([accepted_encodings/1,validate_referer/1,validate_ctype/2]). -export([http_1_0_keep_alive/2]). -export([validate_host/1]). -export([validate_bind_address/1]). -export([check_max_request_length/1]). --export([handle_request/1]). --export([set_auth_handlers/0]). -export([maybe_decompress/2]). -define(HANDLER_NAME_IN_MODULE_POS, 6). -define(MAX_DRAIN_BYTES, 1048576). -define(MAX_DRAIN_TIME_MSEC, 1000). -start_link() -> - start_link(http). -start_link(http) -> - Port = config:get("httpd", "port", "5984"), - start_link(?MODULE, [{port, Port}]); -start_link(https) -> - Port = config:get("ssl", "port", "6984"), - {ok, Ciphers} = couch_util:parse_term(config:get("ssl", "ciphers", undefined)), - {ok, Versions} = couch_util:parse_term(config:get("ssl", "tls_versions", undefined)), - {ok, SecureRenegotiate} = couch_util:parse_term(config:get("ssl", "secure_renegotiate", undefined)), - ServerOpts0 = - [{cacertfile, config:get("ssl", "cacert_file", undefined)}, - {keyfile, config:get("ssl", "key_file", undefined)}, - {certfile, config:get("ssl", "cert_file", undefined)}, - {password, config:get("ssl", "password", undefined)}, - {secure_renegotiate, SecureRenegotiate}, - {versions, Versions}, - {ciphers, Ciphers}], - - case (couch_util:get_value(keyfile, ServerOpts0) == undefined orelse - couch_util:get_value(certfile, ServerOpts0) == undefined) of - true -> - couch_log:error("SSL enabled but PEM certificates are missing", []), - throw({error, missing_certs}); - false -> - ok - end, - - ServerOpts = [Opt || {_, V}=Opt <- ServerOpts0, V /= undefined], - - ClientOpts = case config:get("ssl", "verify_ssl_certificates", "false") of - "false" -> - []; - "true" -> - FailIfNoPeerCert = case config:get("ssl", "fail_if_no_peer_cert", "false") of - "false" -> false; - "true" -> true - end, - [{depth, list_to_integer(config:get("ssl", - "ssl_certificate_max_depth", "1"))}, - {fail_if_no_peer_cert, FailIfNoPeerCert}, - {verify, verify_peer}] ++ - case config:get("ssl", "verify_fun", undefined) of - undefined -> []; - SpecStr -> - [{verify_fun, make_arity_3_fun(SpecStr)}] - end - end, - SslOpts = ServerOpts ++ ClientOpts, - - Options = - [{port, Port}, - {ssl, true}, - {ssl_opts, SslOpts}], - start_link(https, Options). -start_link(Name, Options) -> - BindAddress = case config:get("httpd", "bind_address", "any") of - "any" -> any; - Else -> Else - end, - ok = validate_bind_address(BindAddress), - - {ok, ServerOptions} = couch_util:parse_term( - config:get("httpd", "server_options", "[]")), - {ok, SocketOptions} = couch_util:parse_term( - config:get("httpd", "socket_options", "[]")), - - set_auth_handlers(), - Handlers = get_httpd_handlers(), - - % ensure uuid is set so that concurrent replications - % get the same value. - couch_server:get_uuid(), - - Loop = fun(Req)-> - case SocketOptions of - [] -> - ok; - _ -> - ok = mochiweb_socket:setopts(Req:get(socket), SocketOptions) - end, - apply(?MODULE, handle_request, [Req | Handlers]) - end, - - % set mochiweb options - FinalOptions = lists:append([Options, ServerOptions, [ - {loop, Loop}, - {name, Name}, - {ip, BindAddress}]]), - - % launch mochiweb - case mochiweb_http:start(FinalOptions) of - {ok, MochiPid} -> - {ok, MochiPid}; - {error, Reason} -> - couch_log:error("Failure to start Mochiweb: ~s~n", [Reason]), - throw({error, Reason}) - end. - - -stop() -> - mochiweb_http:stop(couch_httpd), - catch mochiweb_http:stop(https). - - -set_auth_handlers() -> - AuthenticationSrcs = make_fun_spec_strs( - config:get("httpd", "authentication_handlers", "")), - AuthHandlers = lists:map( - fun(A) -> {auth_handler_name(A), make_arity_1_fun(A)} end, AuthenticationSrcs), - AuthenticationFuns = AuthHandlers ++ [ - fun couch_httpd_auth:party_mode_handler/1 %% must be last - ], - ok = application:set_env(couch, auth_handlers, AuthenticationFuns). - -auth_handler_name(SpecStr) -> - lists:nth(?HANDLER_NAME_IN_MODULE_POS, re:split(SpecStr, "[\\W_]", [])). - -get_httpd_handlers() -> - {ok, HttpdGlobalHandlers} = application:get_env(couch, httpd_global_handlers), - - UrlHandlersList = lists:map( - fun({UrlKey, SpecStr}) -> - {?l2b(UrlKey), make_arity_1_fun(SpecStr)} - end, HttpdGlobalHandlers), - - {ok, HttpdDbHandlers} = application:get_env(couch, httpd_db_handlers), - - DbUrlHandlersList = lists:map( - fun({UrlKey, SpecStr}) -> - {?l2b(UrlKey), make_arity_2_fun(SpecStr)} - end, HttpdDbHandlers), - - {ok, HttpdDesignHandlers} = application:get_env(couch, httpd_design_handlers), - - DesignUrlHandlersList = lists:map( - fun({UrlKey, SpecStr}) -> - {?l2b(UrlKey), make_arity_3_fun(SpecStr)} - end, HttpdDesignHandlers), - - UrlHandlers = dict:from_list(UrlHandlersList), - DbUrlHandlers = dict:from_list(DbUrlHandlersList), - DesignUrlHandlers = dict:from_list(DesignUrlHandlersList), - DefaultFun = make_arity_1_fun("{couch_httpd_db, handle_request}"), - [DefaultFun, UrlHandlers, DbUrlHandlers, DesignUrlHandlers]. % SpecStr is a string like "{my_module, my_fun}" % or "{my_module, my_fun, <<"my_arg">>}" @@ -221,175 +72,6 @@ make_arity_3_fun(SpecStr) -> make_fun_spec_strs(SpecStr) -> re:split(SpecStr, "(?<=})\\s*,\\s*(?={)", [{return, list}]). -handle_request(MochiReq) -> - Body = proplists:get_value(body, MochiReq:get(opts)), - erlang:put(mochiweb_request_body, Body), - apply(?MODULE, handle_request, [MochiReq | get_httpd_handlers()]). - -handle_request(MochiReq, DefaultFun, UrlHandlers, DbUrlHandlers, - DesignUrlHandlers) -> - %% reset rewrite count for new request - erlang:put(?REWRITE_COUNT, 0), - - MochiReq1 = couch_httpd_vhost:dispatch_host(MochiReq), - - handle_request_int(MochiReq1, DefaultFun, - UrlHandlers, DbUrlHandlers, DesignUrlHandlers). - -handle_request_int(MochiReq, DefaultFun, - UrlHandlers, DbUrlHandlers, DesignUrlHandlers) -> - Begin = os:timestamp(), - % for the path, use the raw path with the query string and fragment - % removed, but URL quoting left intact - RawUri = MochiReq:get(raw_path), - {"/" ++ Path, _, _} = mochiweb_util:urlsplit_path(RawUri), - - % get requested path - RequestedPath = case MochiReq:get_header_value("x-couchdb-vhost-path") of - undefined -> - case MochiReq:get_header_value("x-couchdb-requested-path") of - undefined -> RawUri; - R -> R - end; - P -> P - end, - - HandlerKey = - case mochiweb_util:partition(Path, "/") of - {"", "", ""} -> - <<"/">>; % Special case the root url handler - {FirstPart, _, _} -> - list_to_binary(FirstPart) - end, - couch_log:debug("~p ~s ~p from ~p~nHeaders: ~p", [ - MochiReq:get(method), - RawUri, - MochiReq:get(version), - peer(MochiReq), - mochiweb_headers:to_list(MochiReq:get(headers)) - ]), - - Method1 = - case MochiReq:get(method) of - % already an atom - Meth when is_atom(Meth) -> Meth; - - % Non standard HTTP verbs aren't atoms (COPY, MOVE etc) so convert when - % possible (if any module references the atom, then it's existing). - Meth -> couch_util:to_existing_atom(Meth) - end, - increment_method_stats(Method1), - - % allow broken HTTP clients to fake a full method vocabulary with an X-HTTP-METHOD-OVERRIDE header - MethodOverride = MochiReq:get_primary_header_value("X-HTTP-Method-Override"), - Method2 = case lists:member(MethodOverride, ["GET", "HEAD", "POST", - "PUT", "DELETE", - "TRACE", "CONNECT", - "COPY"]) of - true -> - couch_log:info("MethodOverride: ~s (real method was ~s)", - [MethodOverride, Method1]), - case Method1 of - 'POST' -> couch_util:to_existing_atom(MethodOverride); - _ -> - % Ignore X-HTTP-Method-Override when the original verb isn't POST. - % I'd like to send a 406 error to the client, but that'd require a nasty refactor. - % throw({not_acceptable, <<"X-HTTP-Method-Override may only be used with POST requests.">>}) - Method1 - end; - _ -> Method1 - end, - - % alias HEAD to GET as mochiweb takes care of stripping the body - Method = case Method2 of - 'HEAD' -> 'GET'; - Other -> Other - end, - - HttpReq = #httpd{ - mochi_req = MochiReq, - peer = peer(MochiReq), - method = Method, - requested_path_parts = - [?l2b(unquote(Part)) || Part <- string:tokens(RequestedPath, "/")], - path_parts = [?l2b(unquote(Part)) || Part <- string:tokens(Path, "/")], - db_url_handlers = DbUrlHandlers, - design_url_handlers = DesignUrlHandlers, - default_fun = DefaultFun, - url_handlers = UrlHandlers, - user_ctx = erlang:erase(pre_rewrite_user_ctx), - auth = erlang:erase(pre_rewrite_auth) - }, - - HandlerFun = couch_util:dict_find(HandlerKey, UrlHandlers, DefaultFun), - - {ok, Resp} = - try - validate_host(HttpReq), - check_request_uri_length(RawUri), - case chttpd_cors:maybe_handle_preflight_request(HttpReq) of - not_preflight -> - case authenticate_request(HttpReq) of - #httpd{} = Req -> - HandlerFun(Req); - Response -> - Response - end; - Response -> - Response - end - catch - throw:{http_head_abort, Resp0} -> - {ok, Resp0}; - throw:{invalid_json, S} -> - couch_log:error("attempted upload of invalid JSON" - " (set log_level to debug to log it)", []), - couch_log:debug("Invalid JSON: ~p",[S]), - send_error(HttpReq, {bad_request, invalid_json}); - throw:unacceptable_encoding -> - couch_log:error("unsupported encoding method for the response", []), - send_error(HttpReq, {not_acceptable, "unsupported encoding"}); - throw:bad_accept_encoding_value -> - couch_log:error("received invalid Accept-Encoding header", []), - send_error(HttpReq, bad_request); - exit:normal -> - exit(normal); - exit:snappy_nif_not_loaded -> - ErrorReason = "To access the database or view index, Apache CouchDB" - " must be built with Erlang OTP R13B04 or higher.", - couch_log:error("~s", [ErrorReason]), - send_error(HttpReq, {bad_otp_release, ErrorReason}); - exit:{body_too_large, _} -> - send_error(HttpReq, request_entity_too_large); - exit:{uri_too_long, _} -> - send_error(HttpReq, request_uri_too_long); - throw:Error -> - Stack = erlang:get_stacktrace(), - couch_log:debug("Minor error in HTTP request: ~p",[Error]), - couch_log:debug("Stacktrace: ~p",[Stack]), - send_error(HttpReq, Error); - error:badarg -> - Stack = erlang:get_stacktrace(), - couch_log:error("Badarg error in HTTP request",[]), - couch_log:info("Stacktrace: ~p",[Stack]), - send_error(HttpReq, badarg); - error:function_clause -> - Stack = erlang:get_stacktrace(), - couch_log:error("function_clause error in HTTP request",[]), - couch_log:info("Stacktrace: ~p",[Stack]), - send_error(HttpReq, function_clause); - Tag:Error -> - Stack = erlang:get_stacktrace(), - couch_log:error("Uncaught error in HTTP request: ~p", - [{Tag, Error}]), - couch_log:info("Stacktrace: ~p",[Stack]), - send_error(HttpReq, Error) - end, - RequestTime = round(timer:now_diff(os:timestamp(), Begin)/1000), - couch_stats:update_histogram([couchdb, request_time], RequestTime), - couch_stats:increment_counter([couchdb, httpd, requests]), - {ok, Resp}. - validate_host(#httpd{} = Req) -> case config:get_boolean("httpd", "validate_host", false) of true -> @@ -418,26 +100,6 @@ valid_hosts() -> List = config:get("httpd", "valid_hosts", ""), re:split(List, ",", [{return, list}]). -check_request_uri_length(Uri) -> - check_request_uri_length(Uri, config:get("httpd", "max_uri_length")). - -check_request_uri_length(_Uri, undefined) -> - ok; -check_request_uri_length(Uri, MaxUriLen) when is_list(MaxUriLen) -> - case length(Uri) > list_to_integer(MaxUriLen) of - true -> - throw(request_uri_too_long); - false -> - ok - end. - -authenticate_request(Req) -> - {ok, AuthenticationFuns} = application:get_env(couch, auth_handlers), - chttpd:authenticate_request(Req, couch_auth_cache, AuthenticationFuns). - -increment_method_stats(Method) -> - couch_stats:increment_counter([couchdb, httpd_request_methods, Method]). - validate_referer(Req) -> Host = host_for_request(Req), Referer = header_value(Req, "Referer", fail), @@ -1225,13 +887,6 @@ http_respond_(#httpd{mochi_req = MochiReq}, 413, Headers, Args, Type) -> http_respond_(#httpd{mochi_req = MochiReq}, Code, Headers, Args, Type) -> MochiReq:Type({Code, Headers, Args}). -peer(MochiReq) -> - case MochiReq:get(socket) of - {remote, Pid, _} -> - node(Pid); - _ -> - MochiReq:get(peer) - end. %%%%%%%% module tests below %%%%%%%% diff --git a/src/couch/src/couch_secondary_sup.erl b/src/couch/src/couch_secondary_sup.erl index bb7821555..4ccd0c9dd 100644 --- a/src/couch/src/couch_secondary_sup.erl +++ b/src/couch/src/couch_secondary_sup.erl @@ -33,11 +33,6 @@ init([]) -> {uuids, {couch_uuids, start, []}} ], - MaybeHttp = case http_enabled() of - true -> [{httpd, {couch_httpd, start_link, []}}]; - false -> couch_httpd:set_auth_handlers(), [] - end, - MaybeHttps = case https_enabled() of true -> [{httpsd, {chttpd, start_link, [https]}}]; false -> [] @@ -55,13 +50,10 @@ init([]) -> [Module]} end || {Name, Spec} - <- Daemons ++ MaybeHttp ++ MaybeHttps, Spec /= ""], + <- Daemons ++ MaybeHttps, Spec /= ""], {ok, {{one_for_one, 50, 3600}, couch_epi:register_service(couch_db_epi, Children)}}. -http_enabled() -> - config:get_boolean("httpd", "enable", false). - https_enabled() -> % 1. [ssl] enable = true | false % 2. if [daemons] httpsd == {chttpd, start_link, [https]} -> pretend true as well diff --git a/src/couch/test/eunit/chttpd_endpoints_tests.erl b/src/couch/test/eunit/chttpd_endpoints_tests.erl index 3c8586a14..f164ae684 100644 --- a/src/couch/test/eunit/chttpd_endpoints_tests.erl +++ b/src/couch/test/eunit/chttpd_endpoints_tests.erl @@ -47,10 +47,10 @@ url_handlers() -> {<<"_replicate">>, chttpd_misc, handle_replicate_req}, {<<"_uuids">>, chttpd_misc, handle_uuids_req}, {<<"_session">>, chttpd_auth, handle_session_req}, - {<<"_up">>, chttpd_misc, handle_up_req}, - {<<"_membership">>, mem3_httpd, handle_membership_req}, - {<<"_db_updates">>, global_changes_httpd, handle_global_changes_req}, - {<<"_cluster_setup">>, setup_httpd, handle_setup_req} + {<<"_membership">>, chttpd_httpd_handlers, not_supported}, + {<<"_db_updates">>, chttpd_httpd_handlers, not_implemented}, + {<<"_cluster_setup">>, chttpd_httpd_handlers, not_implemented}, + {<<"_up">>, chttpd_misc, handle_up_req} ], lists:foreach(fun({Path, Mod, Fun}) -> @@ -67,9 +67,9 @@ db_handlers() -> {<<"_view_cleanup">>, chttpd_db, handle_view_cleanup_req}, {<<"_compact">>, chttpd_db, handle_compact_req}, {<<"_design">>, chttpd_db, handle_design_req}, - {<<"_temp_view">>, chttpd_view, handle_temp_view_req}, + {<<"_temp_view">>, chttpd_httpd_handlers, not_supported}, {<<"_changes">>, chttpd_db, handle_changes_req}, - {<<"_shards">>, mem3_httpd, handle_shards_req}, + {<<"_shards">>, chttpd_httpd_handlers, not_supported}, {<<"_index">>, mango_httpd, handle_req}, {<<"_explain">>, mango_httpd, handle_req}, {<<"_find">>, mango_httpd, handle_req} @@ -87,11 +87,11 @@ db_handlers() -> design_handlers() -> Handlers = [ {<<"_view">>, chttpd_view, handle_view_req}, - {<<"_show">>, chttpd_show, handle_doc_show_req}, - {<<"_list">>, chttpd_show, handle_view_list_req}, + {<<"_show">>, chttpd_httpd_handlers, not_supported}, + {<<"_list">>, chttpd_httpd_handlers, not_supported}, {<<"_update">>, chttpd_show, handle_doc_update_req}, {<<"_info">>, chttpd_db, handle_design_info_req}, - {<<"_rewrite">>, chttpd_rewrite, handle_rewrite_req} + {<<"_rewrite">>, chttpd_httpd_handlers, not_supported} ], lists:foreach(fun({Path, Mod, Fun}) -> diff --git a/src/couch_replicator/src/couch_replicator_ids.erl b/src/couch_replicator/src/couch_replicator_ids.erl index d1cbe571c..44b9e47e6 100644 --- a/src/couch_replicator/src/couch_replicator_ids.erl +++ b/src/couch_replicator/src/couch_replicator_ids.erl @@ -58,7 +58,7 @@ base_id(#{?SOURCE := Src0, ?TARGET := Tgt0} = Rep, 3) -> base_id(#{?SOURCE := Src0, ?TARGET := Tgt0} = Rep, 2) -> {ok, HostName} = inet:gethostname(), - Port = case (catch mochiweb_socket_server:get(couch_httpd, port)) of + Port = case (catch mochiweb_socket_server:get(chttpd, port)) of P when is_number(P) -> P; _ -> |