From 69453db0152bb85f856d359c9ed0433d27da2ff2 Mon Sep 17 00:00:00 2001 From: Jan Lehnardt Date: Thu, 13 Sep 2018 14:52:54 +0200 Subject: remove config: [httpd] default_handler --- rel/overlay/etc/default.ini | 1 - src/couch/src/couch_httpd.erl | 5 +---- 2 files changed, 1 insertion(+), 5 deletions(-) diff --git a/rel/overlay/etc/default.ini b/rel/overlay/etc/default.ini index ba2a498eb..3fe59c92d 100644 --- a/rel/overlay/etc/default.ini +++ b/rel/overlay/etc/default.ini @@ -132,7 +132,6 @@ database_prefix = userdb- port = {{backend_port}} bind_address = 127.0.0.1 authentication_handlers = {couch_httpd_auth, cookie_authentication_handler}, {couch_httpd_auth, default_authentication_handler} -default_handler = {couch_httpd_db, handle_request} secure_rewrites = true vhost_global_handlers = _utils, _uuids, _session, _users allow_jsonp = false diff --git a/src/couch/src/couch_httpd.erl b/src/couch/src/couch_httpd.erl index e66a78e70..0663020ff 100644 --- a/src/couch/src/couch_httpd.erl +++ b/src/couch/src/couch_httpd.erl @@ -104,10 +104,7 @@ start_link(Name, Options) -> Else -> Else end, ok = validate_bind_address(BindAddress), - DefaultSpec = "{couch_httpd_db, handle_request}", - DefaultFun = make_arity_1_fun( - config:get("httpd", "default_handler", DefaultSpec) - ), + DefaultFun = make_arity_1_fun("{couch_httpd_db, handle_request}"), UrlHandlersList = lists:map( fun({UrlKey, SpecStr}) -> -- cgit v1.2.1 From b03b646b035a58fb1263ad31f0166de9b0f5905b Mon Sep 17 00:00:00 2001 From: Jan Lehnardt Date: Thu, 13 Sep 2018 15:02:43 +0200 Subject: remove config: [httpd_{global,db,design}_handlers --- rel/overlay/etc/default.ini | 35 ---------------------------------- src/couch/src/couch_httpd.erl | 44 ++++++++++++++++++++++++++++++++++++++++--- 2 files changed, 41 insertions(+), 38 deletions(-) diff --git a/rel/overlay/etc/default.ini b/rel/overlay/etc/default.ini index 3fe59c92d..b4f148123 100644 --- a/rel/overlay/etc/default.ini +++ b/rel/overlay/etc/default.ini @@ -302,41 +302,6 @@ compaction_daemon={couch_compaction_daemon, start_link, []} [indexers] couch_mrview = true -[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} -_system = {chttpd_misc, handle_system_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} -_view_changes = {couch_mrview_http, handle_view_changes_req} - [uuids] ; Known algorithms: ; random - 128 bits of random awesome diff --git a/src/couch/src/couch_httpd.erl b/src/couch/src/couch_httpd.erl index 0663020ff..3177e0673 100644 --- a/src/couch/src/couch_httpd.erl +++ b/src/couch/src/couch_httpd.erl @@ -106,20 +106,58 @@ start_link(Name, Options) -> ok = validate_bind_address(BindAddress), DefaultFun = make_arity_1_fun("{couch_httpd_db, handle_request}"), + + HttpdGlobalHandlers = [ + {"/", "{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}"}, + {"_system", "{chttpd_misc, handle_system_req}"} + ], + UrlHandlersList = lists:map( fun({UrlKey, SpecStr}) -> {?l2b(UrlKey), make_arity_1_fun(SpecStr)} - end, config:get("httpd_global_handlers")), + end, HttpdGlobalHandlers), + + HttpdDbHandlers = [ + {"_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}"} + ], DbUrlHandlersList = lists:map( fun({UrlKey, SpecStr}) -> {?l2b(UrlKey), make_arity_2_fun(SpecStr)} - end, config:get("httpd_db_handlers")), + end, HttpdDbHandlers), + + HttpdDesignHandlers = [ + {"_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}"}, + {"_view_changes", "{couch_mrview_http, handle_view_changes_req}"} + ], DesignUrlHandlersList = lists:map( fun({UrlKey, SpecStr}) -> {?l2b(UrlKey), make_arity_3_fun(SpecStr)} - end, config:get("httpd_design_handlers")), + end, HttpdDesignHandlers), UrlHandlers = dict:from_list(UrlHandlersList), DbUrlHandlers = dict:from_list(DbUrlHandlersList), -- cgit v1.2.1 From bed99b99ce54946c519eb6315506b74e0b6b0f93 Mon Sep 17 00:00:00 2001 From: Jan Lehnardt Date: Thu, 13 Sep 2018 15:08:10 +0200 Subject: remove config: [httpd] vhost_global_handlers, redirect_vhost_handler --- rel/overlay/etc/default.ini | 1 - src/couch/src/couch_httpd_vhost.erl | 16 +++++----------- 2 files changed, 5 insertions(+), 12 deletions(-) diff --git a/rel/overlay/etc/default.ini b/rel/overlay/etc/default.ini index b4f148123..73dd947bd 100644 --- a/rel/overlay/etc/default.ini +++ b/rel/overlay/etc/default.ini @@ -133,7 +133,6 @@ port = {{backend_port}} bind_address = 127.0.0.1 authentication_handlers = {couch_httpd_auth, cookie_authentication_handler}, {couch_httpd_auth, default_authentication_handler} secure_rewrites = true -vhost_global_handlers = _utils, _uuids, _session, _users allow_jsonp = false ; Options for the MochiWeb HTTP server. ;server_options = [{backlog, 128}, {acceptor_pool_size, 16}] diff --git a/src/couch/src/couch_httpd_vhost.erl b/src/couch/src/couch_httpd_vhost.erl index d8f952190..574dba9c8 100644 --- a/src/couch/src/couch_httpd_vhost.erl +++ b/src/couch/src/couch_httpd_vhost.erl @@ -380,10 +380,6 @@ code_change(_OldVsn, State, _Extra) -> {ok, State}. -handle_config_change("httpd", "vhost_global_handlers", _, _, _) -> - {ok, ?MODULE:reload()}; -handle_config_change("httpd", "redirect_vhost_handler", _, _, _) -> - {ok, ?MODULE:reload()}; handle_config_change("vhosts", _, _, _, _) -> {ok, ?MODULE:reload()}; handle_config_change(_, _, _, _, _) -> @@ -396,25 +392,23 @@ handle_config_terminate(_Server, _Reason, _State) -> load_conf() -> %% get vhost globals - VHostGlobals = re:split(config:get("httpd", - "vhost_global_handlers",""), "\\s*,\\s*",[{return, list}]), + VHostGlobals = re:split("_utils, _uuids, _session, _users", "\\s*,\\s*", + [{return, list}]), %% build vhosts matching rules VHosts = make_vhosts(), %% build vhosts handler fun DefaultVHostFun = "{couch_httpd_vhost, redirect_to_vhost}", - Fun = couch_httpd:make_arity_2_fun(config:get("httpd", - "redirect_vhost_handler", DefaultVHostFun)), + Fun = couch_httpd:make_arity_2_fun(DefaultVHostFun), {VHostGlobals, VHosts, Fun}. %% cheaply determine if there are any virtual hosts %% configured at all. vhost_enabled() -> - case {config:get("httpd", "vhost_global_handlers"), - config:get("vhosts")} of - {undefined, []} -> + case config:get("vhosts") of + [] -> false; _ -> true -- cgit v1.2.1 From 21368b7449bd9276b5450838f13758de342e9df6 Mon Sep 17 00:00:00 2001 From: Jan Lehnardt Date: Thu, 13 Sep 2018 15:15:08 +0200 Subject: remove config: [daemons] --- rel/overlay/etc/default.ini | 10 ---------- src/couch/src/couch_secondary_sup.erl | 13 ++++++++++++- 2 files changed, 12 insertions(+), 11 deletions(-) diff --git a/rel/overlay/etc/default.ini b/rel/overlay/etc/default.ini index 73dd947bd..215d7eabe 100644 --- a/rel/overlay/etc/default.ini +++ b/rel/overlay/etc/default.ini @@ -281,16 +281,6 @@ os_process_limit = 100 ; "infinity" is also a valid configuration value. ;group_info_timeout = 5000 -[daemons] -index_server={couch_index_server, start_link, []} -query_servers={couch_proc_manager, start_link, []} -vhosts={couch_httpd_vhost, start_link, []} -httpd={couch_httpd, start_link, []} -uuids={couch_uuids, start, []} -auth_cache={couch_auth_cache, start_link, []} -os_daemons={couch_os_daemons, start_link, []} -compaction_daemon={couch_compaction_daemon, start_link, []} - [mango] ; Set to true to disable the "index all fields" text index, which can lead ; to out of memory issues when users have documents with nested array fields. diff --git a/src/couch/src/couch_secondary_sup.erl b/src/couch/src/couch_secondary_sup.erl index 0c3b7aa5a..cfbce6a9a 100644 --- a/src/couch/src/couch_secondary_sup.erl +++ b/src/couch/src/couch_secondary_sup.erl @@ -26,6 +26,17 @@ init([]) -> worker, dynamic} ], + Daemons = [ + {"index_server", "{couch_index_server, start_link, []}"}, + {"query_servers", "{couch_proc_manager, start_link, []}"}, + {"vhosts", "{couch_httpd_vhost, start_link, []}"}, + {"httpd", "{couch_httpd, start_link, []}"}, + {"uuids", "{couch_uuids, start, []}"}, + {"auth_cache", "{couch_auth_cache, start_link, []}"}, + {"os_daemons", "{couch_os_daemons, start_link, []}"}, + {"compaction_daemon", "{couch_compaction_daemon, start_link, []}"} + ], + Children = SecondarySupervisors ++ [ begin {ok, {Module, Fun, Args}} = couch_util:parse_term(SpecStr), @@ -38,6 +49,6 @@ init([]) -> [Module]} end || {Name, SpecStr} - <- config:get("daemons"), SpecStr /= ""], + <- Daemons, SpecStr /= ""], {ok, {{one_for_one, 50, 3600}, couch_epi:register_service(couch_db_epi, Children)}}. -- cgit v1.2.1 From 36e2323044dee3cadf5f766462000d7be89fc4dc Mon Sep 17 00:00:00 2001 From: Jan Lehnardt Date: Fri, 14 Sep 2018 10:42:02 +0200 Subject: feat: move [native_]query_server & os_daemons setup out of runtime config MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit CouchDB ships with two default query_servers (javascript and coffeescript) as well as one default native_query_server (query aka mango). These used to be configured in default.ini in these sections: ``` [query_servers] javascript = {{prefix}}/bin/couchjs {{prefix}}/share/server/main.js coffeescript = {{prefix}}/bin/couchjs {{prefix}}/share/server/main-coffee.js ; enable mango query engine [native_query_servers] query = {mango_native_proc, start_link, []} ; erlang query server ; erlang = {couch_native_process, start_link, []} ``` This allowed end-users post-install and even runtime-changes to which query servers are enabled and where their binaries live. This patch changes things, so only a post-install, but not at-runtime changes are possible from now on. This still allows people to configure their CouchDB to run a third- party query server like the somewhat popular Python query server, but it changes the way the setup is done. Query Servers The javascript and coffeescript query servers continue to be enabled by default. Setup differences have been moved from default.ini to the couchdb and couchdb.cmd start scripts respectively. Additional query servers can now be configured using environment variables: ``` export COUCHDB_QUERY_SERVER_PYTHON="/path/to/python/query/server.py with args" couchdb ``` Where the last segment in the environment variable matches the usual lowercase(!) query language in the design doc `language` field. Multiple query servers can be configured by using more environment variables. Native Query Servers The mango query server continues to be enabled by default. The erlang query server continues to be disabled by default. This patch adds a `[native_query_servers] enable_erlang_query_server = BOOL` setting (defaults to `"false"`) to enable the erlang query server. If the legacy configuration for enabling the query server is detected, that is counted as a `true` setting as well, so existing configurations continue to work just fine. Windows Since the setting of the `./configure` time `PREFIX` happens during `make release`, I had to adapt the `couchdb` and `couchdb.cmd` scripts to have the correct env vars set and the `PREFIX` replaced there. I did this to the best of my abilities and research, but this needs review from the Windows team (Hi Joan! :). OS Daemons Although deprecated in 2.2.0, we’re keeping support for this until 3.x, but the configuration changes analogous to query servers. Previously, configuration looked like this: ``` [os_daemons] name = /path/to/daemon with args ``` With this patch, setup looks like this: ``` COUCHDB_OS_DAEMON_NAME="/path/to/daemon with args" couchdb ``` Multiple OS Daemons can be started with multiple env vars. The final segment in the env var becomes the daemon identifier inside CouchDB as lowercase(!). --- dev/run | 24 ++++++++++------- rel/files/couchdb.cmd.in | 35 ++++++++++++++++++++++++ rel/files/couchdb.in | 37 +++++++++++++++++++++++++ rel/overlay/bin/couchdb | 35 ------------------------ rel/overlay/bin/couchdb.cmd | 32 ---------------------- rel/overlay/etc/default.ini | 8 ++---- rel/reltool.config | 4 ++- src/couch/src/couch_os_daemons.erl | 21 +++++++++++---- src/couch/src/couch_proc_manager.erl | 52 ++++++++++++++++++++++++++++++++++-- 9 files changed, 157 insertions(+), 91 deletions(-) create mode 100644 rel/files/couchdb.cmd.in create mode 100755 rel/files/couchdb.in delete mode 100755 rel/overlay/bin/couchdb delete mode 100644 rel/overlay/bin/couchdb.cmd diff --git a/dev/run b/dev/run index 5ab895eb1..5bf5fc0f5 100755 --- a/dev/run +++ b/dev/run @@ -273,16 +273,6 @@ def boot_haproxy(ctx): def hack_default_ini(ctx, node, contents): - # Replace couchjs command - couchjs = os.path.join(ctx['rootdir'], "src", "couch", "priv", "couchjs") - mainjs = os.path.join(ctx['rootdir'], "share", "server", "main.js") - coffeejs = os.path.join(ctx['rootdir'], "share", "server", "main-coffee.js") - - repl = toposixpath("javascript = %s %s" % (couchjs, mainjs)) - contents = re.sub("(?m)^javascript.*$", repl, contents) - - repl = toposixpath("coffeescript = %s %s" % (couchjs, coffeejs)) - contents = re.sub("(?m)^coffeescript.*$", repl, contents) if ctx['enable_erlang_views']: contents = re.sub( @@ -413,15 +403,29 @@ def check_node_alive(url): if error is not None: raise error +def set_boot_env(ctx): + + # fudge default query server paths + couchjs = os.path.join(ctx['rootdir'], "src", "couch", "priv", "couchjs") + mainjs = os.path.join(ctx['rootdir'], "share", "server", "main.js") + coffeejs = os.path.join(ctx['rootdir'], "share", "server", "main-coffee.js") + + qs_javascript = toposixpath("%s %s" % (couchjs, mainjs)) + qs_coffescript = toposixpath("%s %s" % (couchjs, coffeejs)) + + os.environ['COUCHDB_QUERY_SERVER_JAVASCRIPT'] = qs_javascript + os.environ['COUCHDB_QUERY_SERVER_COFFEESCRIPT'] = qs_coffescript @log('Start node {node}') def boot_node(ctx, node): erl_libs = os.path.join(ctx['rootdir'], "src") + set_boot_env(ctx) env = os.environ.copy() env["ERL_LIBS"] = os.pathsep.join([erl_libs]) node_etcdir = os.path.join(ctx['devdir'], "lib", node, "etc") reldir = os.path.join(ctx['rootdir'], "rel") + cmd = [ "erl", "-args_file", os.path.join(node_etcdir, "vm.args"), diff --git a/rel/files/couchdb.cmd.in b/rel/files/couchdb.cmd.in new file mode 100644 index 000000000..9438872c6 --- /dev/null +++ b/rel/files/couchdb.cmd.in @@ -0,0 +1,35 @@ +@ECHO OFF + +:: 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. + +SET COUCHDB_BIN_DIR=%~dp0 +SET ROOTDIR=%COUCHDB_BIN_DIR%\..\ +CD "%ROOTDIR%" + +SET /P START_ERL= < releases\start_erl.data +FOR /F "tokens=1" %%G IN ("%START_ERL%") DO SET ERTS_VSN=%%G +FOR /F "tokens=2" %%G IN ("%START_ERL%") DO SET APP_VSN=%%G + +set BINDIR=%ROOTDIR%/erts-%ERTS_VSN%/bin +set EMU=beam +set PROGNAME=%~n0 +set PATH=%PATH%;%COUCHDB_BIN_DIR% + +set COUCHDB_QUERY_SERVER_JAVASCRIPT="{{prefix}}/bin/couchjs {{prefix}}/share/server/main.js" +set COUCHDB_QUERY_SERVER_COFFEESCRIPT="{{prefix}}/bin/couchjs {{prefix}}/share/server/main-coffee.js" + +"%BINDIR%\erl" -boot "%ROOTDIR%\releases\%APP_VSN%\couchdb" ^ +-args_file "%ROOTDIR%\etc\vm.args" ^ +-config "%ROOTDIR%\releases\%APP_VSN%\sys.config" %* + +:: EXIT /B diff --git a/rel/files/couchdb.in b/rel/files/couchdb.in new file mode 100755 index 000000000..aae179aa7 --- /dev/null +++ b/rel/files/couchdb.in @@ -0,0 +1,37 @@ +#!/bin/sh + +# 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. + +COUCHDB_BIN_DIR=$(cd "${0%/*}" && pwd) +ERTS_BIN_DIR=$COUCHDB_BIN_DIR/../ +cd "$COUCHDB_BIN_DIR/../" + +export ROOTDIR=${ERTS_BIN_DIR%/*} + +START_ERL=`cat "$ROOTDIR/releases/start_erl.data"` +ERTS_VSN=${START_ERL% *} +APP_VSN=${START_ERL#* } + +export BINDIR="$ROOTDIR/erts-$ERTS_VSN/bin" +export EMU=beam +export PROGNAME=`echo $0 | sed 's/.*\///'` + +export COUCHDB_QUERY_SERVER_JAVASCRIPT="{{prefix}}/bin/couchjs {{prefix}}/share/server/main.js" +export COUCHDB_QUERY_SERVER_COFFEESCRIPT="{{prefix}}/bin/couchjs {{prefix}}/share/server/main-coffee.js" + +ARGS_FILE="${COUCHDB_ARGS_FILE:-$ROOTDIR/etc/vm.args}" +SYSCONFIG_FILE="${COUCHDB_SYSCONFIG_FILE:-$ROOTDIR/releases/$APP_VSN/sys.config}" + +exec "$BINDIR/erlexec" -boot "$ROOTDIR/releases/$APP_VSN/couchdb" \ + -args_file "${ARGS_FILE}" \ + -config "${SYSCONFIG_FILE}" "$@" diff --git a/rel/overlay/bin/couchdb b/rel/overlay/bin/couchdb deleted file mode 100755 index a9e6e9bea..000000000 --- a/rel/overlay/bin/couchdb +++ /dev/null @@ -1,35 +0,0 @@ -#!/bin/sh - -# 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. - -COUCHDB_BIN_DIR=$(cd "${0%/*}" && pwd) -ERTS_BIN_DIR=$COUCHDB_BIN_DIR/../ -cd "$COUCHDB_BIN_DIR/../" - -export ROOTDIR=${ERTS_BIN_DIR%/*} - -START_ERL=`cat "$ROOTDIR/releases/start_erl.data"` -ERTS_VSN=${START_ERL% *} -APP_VSN=${START_ERL#* } - -export BINDIR="$ROOTDIR/erts-$ERTS_VSN/bin" -export EMU=beam -export PROGNAME=`echo $0 | sed 's/.*\///'` - -ARGS_FILE="${COUCHDB_ARGS_FILE:-$ROOTDIR/etc/vm.args}" -SYSCONFIG_FILE="${COUCHDB_SYSCONFIG_FILE:-$ROOTDIR/releases/$APP_VSN/sys.config}" - -exec "$BINDIR/erlexec" -boot "$ROOTDIR/releases/$APP_VSN/couchdb" \ - -args_file "${ARGS_FILE}" \ - -config "${SYSCONFIG_FILE}" "$@" - diff --git a/rel/overlay/bin/couchdb.cmd b/rel/overlay/bin/couchdb.cmd deleted file mode 100644 index 5e5f2cfe6..000000000 --- a/rel/overlay/bin/couchdb.cmd +++ /dev/null @@ -1,32 +0,0 @@ -@ECHO OFF - -:: 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. - -SET COUCHDB_BIN_DIR=%~dp0 -SET ROOTDIR=%COUCHDB_BIN_DIR%\..\ -CD "%ROOTDIR%" - -SET /P START_ERL= < releases\start_erl.data -FOR /F "tokens=1" %%G IN ("%START_ERL%") DO SET ERTS_VSN=%%G -FOR /F "tokens=2" %%G IN ("%START_ERL%") DO SET APP_VSN=%%G - -set BINDIR=%ROOTDIR%/erts-%ERTS_VSN%/bin -set EMU=beam -set PROGNAME=%~n0 -set PATH=%PATH%;%COUCHDB_BIN_DIR% - -"%BINDIR%\erl" -boot "%ROOTDIR%\releases\%APP_VSN%\couchdb" ^ --args_file "%ROOTDIR%\etc\vm.args" ^ --config "%ROOTDIR%\releases\%APP_VSN%\sys.config" %* - -:: EXIT /B diff --git a/rel/overlay/etc/default.ini b/rel/overlay/etc/default.ini index 215d7eabe..f384de3aa 100644 --- a/rel/overlay/etc/default.ini +++ b/rel/overlay/etc/default.ini @@ -260,13 +260,9 @@ credentials = false ; List of hosts separated by a comma. * means accept all ; hosts = -[query_servers] -javascript = {{prefix}}/bin/couchjs {{prefix}}/share/server/main.js -coffeescript = {{prefix}}/bin/couchjs {{prefix}}/share/server/main-coffee.js - -; enable mango query engine [native_query_servers] -query = {mango_native_proc, start_link, []} +; erlang query server +; enable_erlang_query_server = false ; Changing reduce_limit to false will disable reduce_limit. ; If you think you're hitting reduce_limit with a "good" reduce function, diff --git a/rel/reltool.config b/rel/reltool.config index 2c55d0900..bf4ae448b 100644 --- a/rel/reltool.config +++ b/rel/reltool.config @@ -125,5 +125,7 @@ {copy, "files/sys.config", "releases/\{\{rel_vsn\}\}/sys.config"}, {copy, "files/vm.args", "releases/\{\{rel_vsn\}\}/vm.args"}, {template, "overlay/etc/default.ini", "etc/default.ini"}, - {template, "overlay/etc/vm.args", "etc/vm.args"} + {template, "overlay/etc/vm.args", "etc/vm.args"}, + {template, "files/couchdb.in", "bin/couchdb"}, + {template, "files/couchdb.cmd.in", "bin/couchdb.cmd"} ]}. diff --git a/src/couch/src/couch_os_daemons.erl b/src/couch/src/couch_os_daemons.erl index cd019dbb5..2427d67ab 100644 --- a/src/couch/src/couch_os_daemons.erl +++ b/src/couch/src/couch_os_daemons.erl @@ -72,10 +72,6 @@ handle_call(Msg, From, Table) -> handle_cast({config_change, Sect, Key}, Table) -> restart_daemons(Table, Sect, Key), - case Sect of - "os_daemons" -> reload_daemons(Table); - _ -> ok - end, {noreply, Table}; handle_cast(stop, Table) -> {stop, normal, Table}; @@ -288,9 +284,24 @@ handle_log_message(Name, Msg, Level) -> % Daemon management helpers % +get_daemons() -> + % map all COUCHDB_OS_DAEMON_* vars out of os:getenv + lists:filtermap(fun(Var) -> + case string:split(Var, "=") of + [VarName, Cmd] -> + case string:find(VarName, "COUCHDB_OS_DAEMON_") of + nomatch -> + false; + "COUCHDB_OS_DAEMON_" ++ Name -> + {true, {string:lowercase(Name), Cmd}} + end; + _Else -> false + end + end, os:getenv()). + reload_daemons(Table) -> % List of daemons we want to have running. - Configured = lists:sort(config:get("os_daemons")), + Configured = lists:sort(get_daemons()), % Remove records for daemons that were halted. MSpecHalted = #daemon{name='$1', cmd='$2', status=halted, _='_'}, diff --git a/src/couch/src/couch_proc_manager.erl b/src/couch/src/couch_proc_manager.erl index 04101f240..b6639288f 100644 --- a/src/couch/src/couch_proc_manager.erl +++ b/src/couch/src/couch_proc_manager.erl @@ -372,12 +372,60 @@ new_proc(Client) -> end, exit(Resp). +get_env_for_spec(Spec, Target) -> + % loop over os:getenv(), match SPEC_TARGET + lists:filtermap(fun(VarName) -> + SpecStr = Spec ++ Target, + case string:split(VarName, "=") of + [SpecStr, Cmd] -> {true, Cmd}; + _Else -> false + end + end, os:getenv()). + +get_query_server(LangStr) -> + % look for COUCH_QUERY_SERVER_LANGSTR in env + % if exists, return value, else undefined + UpperLangString = string:uppercase(LangStr), + case get_env_for_spec("COUCHDB_QUERY_SERVER_", UpperLangString) of + [] -> undefined; + [Command] -> Command + end. + +native_query_server_enabled() -> + % 1. [native_query_server] enable_erlang_query_server = true | false + % 2. if [native_query_server] erlang == {couch_native_process, start_link, []} -> pretend true as well + NativeEnabled = config:get_boolean("native_query_server", "enable_erlang_query_server", false), + NativeLegacyConfig = config:get("native_query_server", "erlang", ""), + NativeLegacyEnabled = NativeLegacyConfig =:= "{couch_native_process, start_link, []}", + + % there surely is a more elegant way to do this that eludes me at present + case {NativeEnabled, NativeLegacyEnabled} of + {true, true} -> true; + {true, _} -> true; + {_, true} -> true; + _ -> false + end. + +get_native_query_server("query") -> % mango query server + "{mango_native_proc, start_link, []}"; +get_native_query_server("erlang") -> % erlang query server + case native_query_server_enabled() of + true -> "{couch_native_process, start_link, []}"; + _Else -> undefined + end; +get_native_query_server(LangStr) -> + % same as above, but COUCH_NATIVE_QUERY_SERVER_LANGSTR + UpperLangString = string:uppercase(LangStr), + case get_env_for_spec("COUCHDB_NATIVE_QUERY_SERVER_", UpperLangString) of + [] -> undefined; + [Command] -> Command + end. new_proc_int(From, Lang) when is_binary(Lang) -> LangStr = binary_to_list(Lang), - case config:get("query_servers", LangStr) of + case get_query_server(LangStr) of undefined -> - case config:get("native_query_servers", LangStr) of + case get_native_query_server(LangStr) of undefined -> gen_server:reply(From, {unknown_query_language, Lang}); SpecStr -> -- cgit v1.2.1 From 67f4da6e0bfbf31f1f72d9d9431f03a5d870d561 Mon Sep 17 00:00:00 2001 From: Jan Lehnardt Date: Sat, 15 Sep 2018 09:50:37 +0200 Subject: feat: remove deprecated os_daemons --- src/couch/src/couch_os_daemons.erl | 405 ---------------------------- src/couch/src/couch_secondary_sup.erl | 1 - src/couch/test/couchdb_os_daemons_tests.erl | 259 ------------------ 3 files changed, 665 deletions(-) delete mode 100644 src/couch/src/couch_os_daemons.erl delete mode 100644 src/couch/test/couchdb_os_daemons_tests.erl diff --git a/src/couch/src/couch_os_daemons.erl b/src/couch/src/couch_os_daemons.erl deleted file mode 100644 index 2427d67ab..000000000 --- a/src/couch/src/couch_os_daemons.erl +++ /dev/null @@ -1,405 +0,0 @@ -% 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(couch_os_daemons). --behaviour(gen_server). --vsn(1). --behaviour(config_listener). - --export([start_link/0, info/0, info/1]). - --export([init/1, terminate/2, code_change/3]). --export([handle_call/3, handle_cast/2, handle_info/2]). - -% config_listener api --export([handle_config_change/5, handle_config_terminate/3]). - --include_lib("couch/include/couch_db.hrl"). - --record(daemon, { - port, - name, - cmd, - kill, - status=running, - cfg_patterns=[], - errors=[], - buf=[] -}). - --define(PORT_OPTIONS, [stream, {line, 1024}, binary, exit_status, hide]). --define(RELISTEN_DELAY, 5000). - -start_link() -> - gen_server:start_link({local, ?MODULE}, ?MODULE, [], []). - -info() -> - info([]). - -info(Options) -> - gen_server:call(?MODULE, {daemon_info, Options}). - -init(_) -> - process_flag(trap_exit, true), - ok = config:listen_for_changes(?MODULE, nil), - Table = ets:new(?MODULE, [protected, set, {keypos, #daemon.port}]), - reload_daemons(Table), - {ok, Table}. - -terminate(_Reason, Table) -> - [stop_port(D) || D <- ets:tab2list(Table)], - ok. - -handle_call({daemon_info, Options}, _From, Table) when is_list(Options) -> - case lists:member(table, Options) of - true -> - {reply, {ok, ets:tab2list(Table)}, Table}; - _ -> - {reply, {ok, Table}, Table} - end; -handle_call(Msg, From, Table) -> - couch_log:error("Unknown call message to ~p from ~p: ~p", - [?MODULE, From, Msg]), - {stop, error, Table}. - -handle_cast({config_change, Sect, Key}, Table) -> - restart_daemons(Table, Sect, Key), - {noreply, Table}; -handle_cast(stop, Table) -> - {stop, normal, Table}; -handle_cast(Msg, Table) -> - couch_log:error("Unknown cast message to ~p: ~p", [?MODULE, Msg]), - {stop, error, Table}. - -handle_info({'EXIT', Port, Reason}, Table) -> - case ets:lookup(Table, Port) of - [] -> - couch_log:info("Port ~p exited after stopping: ~p~n", - [Port, Reason]); - [#daemon{status=stopping}] -> - true = ets:delete(Table, Port); - [#daemon{name=Name, status=restarting}=D] -> - couch_log:info("Daemon ~p restarting after config change.", [Name]), - true = ets:delete(Table, Port), - {ok, Port2} = start_port(D#daemon.cmd), - true = ets:insert(Table, D#daemon{ - port=Port2, status=running, kill=undefined, buf=[] - }); - [#daemon{name=Name, status=halted}] -> - couch_log:error("Halted daemon process: ~p", [Name]); - [D] -> - couch_log:error("Invalid port state at exit: ~p", [D]) - end, - {noreply, Table}; -handle_info({Port, closed}, Table) -> - handle_info({Port, {exit_status, closed}}, Table); -handle_info({Port, {exit_status, Status}}, Table) -> - case ets:lookup(Table, Port) of - [] -> - couch_log:error("Unknown port ~p exiting ~p", [Port, Status]), - {stop, {error, unknown_port_died, Status}, Table}; - [#daemon{name=Name, status=restarting}=D] -> - couch_log:info("Daemon ~p restarting after config change.", [Name]), - true = ets:delete(Table, Port), - {ok, Port2} = start_port(D#daemon.cmd), - true = ets:insert(Table, D#daemon{ - port=Port2, status=running, kill=undefined, buf=[] - }), - {noreply, Table}; - [#daemon{status=stopping}=D] -> - % The configuration changed and this daemon is no - % longer needed. - couch_log:debug("Port ~p shut down.", [D#daemon.name]), - true = ets:delete(Table, Port), - {noreply, Table}; - [D] -> - % Port died for unknown reason. Check to see if it's - % died too many times or if we should boot it back up. - case should_halt([os:timestamp() | D#daemon.errors]) of - {true, _} -> - % Halting the process. We won't try and reboot - % until the configuration changes. - Fmt = "Daemon ~p halted with exit_status ~p", - couch_log:error(Fmt, [D#daemon.name, Status]), - D2 = D#daemon{status=halted, errors=nil, buf=nil}, - true = ets:insert(Table, D2), - {noreply, Table}; - {false, Errors} -> - % We're guessing it was a random error, this daemon - % has behaved so we'll give it another chance. - Fmt = "Daemon ~p is being rebooted after exit_status ~p", - couch_log:info(Fmt, [D#daemon.name, Status]), - true = ets:delete(Table, Port), - {ok, Port2} = start_port(D#daemon.cmd), - true = ets:insert(Table, D#daemon{ - port=Port2, status=running, kill=undefined, - errors=Errors, buf=[] - }), - {noreply, Table} - end; - _Else -> - throw(error) - end; -handle_info({Port, {data, {noeol, Data}}}, Table) -> - [#daemon{buf=Buf}=D] = ets:lookup(Table, Port), - true = ets:insert(Table, D#daemon{buf=[Data | Buf]}), - {noreply, Table}; -handle_info({Port, {data, {eol, Data}}}, Table) -> - [#daemon{buf=Buf}=D] = ets:lookup(Table, Port), - Line = lists:reverse(Buf, Data), - % The first line echoed back is the kill command - % for when we go to get rid of the port. Lines after - % that are considered part of the stdio API. - case D#daemon.kill of - undefined -> - true = ets:insert(Table, D#daemon{kill=?b2l(Line), buf=[]}); - _Else -> - D2 = case (catch ?JSON_DECODE(Line)) of - {invalid_json, Rejected} -> - couch_log:error("Ignoring OS daemon request: ~p", - [Rejected]), - D; - JSON -> - {ok, D3} = handle_port_message(D, JSON), - D3 - end, - true = ets:insert(Table, D2#daemon{buf=[]}) - end, - {noreply, Table}; -handle_info({Port, Error}, Table) -> - couch_log:error("Unexpectd message from port ~p: ~p", [Port, Error]), - stop_port(Port), - [D] = ets:lookup(Table, Port), - true = ets:insert(Table, D#daemon{status=restarting, buf=nil}), - {noreply, Table}; -handle_info(restart_config_listener, State) -> - ok = config:listen_for_changes(?MODULE, nil), - {noreply, State}; -handle_info(Msg, Table) -> - couch_log:error("Unexpected info message to ~p: ~p", [?MODULE, Msg]), - {stop, error, Table}. - -code_change(_OldVsn, State, _Extra) -> - {ok, State}. - - -handle_config_change(Section, Key, _, _, _) -> - gen_server:cast(?MODULE, {config_change, Section, Key}), - {ok, nil}. - -handle_config_terminate(_, stop, _) -> - ok; -handle_config_terminate(_Server, _Reason, _State) -> - erlang:send_after(?RELISTEN_DELAY, whereis(?MODULE), restart_config_listener). - - -% Internal API - -% -% Port management helpers -% - -start_port(Command) -> - start_port(Command, []). - -start_port(Command, EnvPairs) -> - PrivDir = couch_util:priv_dir(), - Spawnkiller = "\"" ++ filename:join(PrivDir, "couchspawnkillable") ++ "\"", - Opts = case lists:keytake(env, 1, ?PORT_OPTIONS) of - false -> - ?PORT_OPTIONS ++ [ {env,EnvPairs} ]; - {value, {env,OldPairs}, SubOpts} -> - AllPairs = lists:keymerge(1, EnvPairs, OldPairs), - SubOpts ++ [ {env,AllPairs} ] - end, - Port = open_port({spawn, Spawnkiller ++ " " ++ Command}, Opts), - {ok, Port}. - - -stop_port(#daemon{port=Port, kill=undefined}=D) -> - couch_log:error("Stopping daemon without a kill command: ~p", - [D#daemon.name]), - catch port_close(Port); -stop_port(#daemon{port=Port}=D) -> - couch_log:debug("Stopping daemon: ~p", [D#daemon.name]), - os:cmd(D#daemon.kill), - catch port_close(Port). - - -handle_port_message(#daemon{port=Port}=Daemon, [<<"get">>, Section]) -> - KVs = config:get(Section), - Data = lists:map(fun({K, V}) -> {?l2b(K), ?l2b(V)} end, KVs), - Json = iolist_to_binary(?JSON_ENCODE({Data})), - port_command(Port, <>), - {ok, Daemon}; -handle_port_message(#daemon{port=Port}=Daemon, [<<"get">>, Section, Key]) -> - Value = case config:get(Section, Key, undefined) of - undefined -> null; - String -> ?l2b(String) - end, - Json = iolist_to_binary(?JSON_ENCODE(Value)), - port_command(Port, <>), - {ok, Daemon}; -handle_port_message(Daemon, [<<"register">>, Sec]) when is_binary(Sec) -> - Patterns = lists:usort(Daemon#daemon.cfg_patterns ++ [{?b2l(Sec)}]), - {ok, Daemon#daemon{cfg_patterns=Patterns}}; -handle_port_message(Daemon, [<<"register">>, Sec, Key]) - when is_binary(Sec) andalso is_binary(Key) -> - Pattern = {?b2l(Sec), ?b2l(Key)}, - Patterns = lists:usort(Daemon#daemon.cfg_patterns ++ [Pattern]), - {ok, Daemon#daemon{cfg_patterns=Patterns}}; -handle_port_message(#daemon{name=Name}=Daemon, [<<"log">>, Msg]) -> - handle_log_message(Name, Msg, <<"info">>), - {ok, Daemon}; -handle_port_message(#daemon{name=Name}=Daemon, [<<"log">>, Msg, {Opts}]) -> - Level = couch_util:get_value(<<"level">>, Opts, <<"info">>), - handle_log_message(Name, Msg, Level), - {ok, Daemon}; -handle_port_message(#daemon{name=Name}=Daemon, Else) -> - couch_log:error("Daemon ~p made invalid request: ~p", [Name, Else]), - {ok, Daemon}. - - -handle_log_message(Name, Msg, _Level) when not is_binary(Msg) -> - couch_log:error("Invalid log message from daemon ~p: ~p", [Name, Msg]); -handle_log_message(Name, Msg, <<"debug">>) -> - couch_log:debug("Daemon ~p :: ~s", [Name, ?b2l(Msg)]); -handle_log_message(Name, Msg, <<"info">>) -> - couch_log:info("Daemon ~p :: ~s", [Name, ?b2l(Msg)]); -handle_log_message(Name, Msg, <<"error">>) -> - couch_log:error("Daemon: ~p :: ~s", [Name, ?b2l(Msg)]); -handle_log_message(Name, Msg, Level) -> - couch_log:error("Invalid log level from daemon: ~p", [Level]), - couch_log:info("Daemon: ~p :: ~s", [Name, ?b2l(Msg)]). - -% -% Daemon management helpers -% - -get_daemons() -> - % map all COUCHDB_OS_DAEMON_* vars out of os:getenv - lists:filtermap(fun(Var) -> - case string:split(Var, "=") of - [VarName, Cmd] -> - case string:find(VarName, "COUCHDB_OS_DAEMON_") of - nomatch -> - false; - "COUCHDB_OS_DAEMON_" ++ Name -> - {true, {string:lowercase(Name), Cmd}} - end; - _Else -> false - end - end, os:getenv()). - -reload_daemons(Table) -> - % List of daemons we want to have running. - Configured = lists:sort(get_daemons()), - - % Remove records for daemons that were halted. - MSpecHalted = #daemon{name='$1', cmd='$2', status=halted, _='_'}, - Halted = lists:sort([{N, C} || [N, C] <- ets:match(Table, MSpecHalted)]), - ok = stop_os_daemons(Table, find_to_stop(Configured, Halted, [])), - - % Stop daemons that are running - % Start newly configured daemons - MSpecRunning = #daemon{name='$1', cmd='$2', status=running, _='_'}, - Running = lists:sort([{N, C} || [N, C] <- ets:match(Table, MSpecRunning)]), - ok = stop_os_daemons(Table, find_to_stop(Configured, Running, [])), - ok = boot_os_daemons(Table, find_to_boot(Configured, Running, [])), - ok. - - -restart_daemons(Table, Sect, Key) -> - restart_daemons(Table, Sect, Key, ets:first(Table)). - -restart_daemons(_, _, _, '$end_of_table') -> - ok; -restart_daemons(Table, Sect, Key, Port) -> - [D] = ets:lookup(Table, Port), - HasSect = lists:member({Sect}, D#daemon.cfg_patterns), - HasKey = lists:member({Sect, Key}, D#daemon.cfg_patterns), - case HasSect or HasKey of - true -> - stop_port(D), - D2 = D#daemon{status=restarting, buf=nil}, - true = ets:insert(Table, D2); - _ -> - ok - end, - restart_daemons(Table, Sect, Key, ets:next(Table, Port)). - - -stop_os_daemons(_Table, []) -> - ok; -stop_os_daemons(Table, [{Name, Cmd} | Rest]) -> - [[Port]] = ets:match(Table, #daemon{port='$1', name=Name, cmd=Cmd, _='_'}), - [D] = ets:lookup(Table, Port), - case D#daemon.status of - halted -> - ets:delete(Table, Port); - _ -> - stop_port(D), - D2 = D#daemon{status=stopping, errors=nil, buf=nil}, - true = ets:insert(Table, D2) - end, - stop_os_daemons(Table, Rest). - -boot_os_daemons(_Table, []) -> - ok; -boot_os_daemons(Table, [{Name, Cmd} | Rest]) -> - {ok, Port} = start_port(Cmd), - true = ets:insert(Table, #daemon{port=Port, name=Name, cmd=Cmd}), - boot_os_daemons(Table, Rest). - -% Elements unique to the configured set need to be booted. -find_to_boot([], _Rest, Acc) -> - % Nothing else configured. - Acc; -find_to_boot([D | R1], [D | R2], Acc) -> - % Elements are equal, daemon already running. - find_to_boot(R1, R2, Acc); -find_to_boot([D1 | R1], [D2 | _]=A2, Acc) when D1 < D2 -> - find_to_boot(R1, A2, [D1 | Acc]); -find_to_boot(A1, [_ | R2], Acc) -> - find_to_boot(A1, R2, Acc); -find_to_boot(Rest, [], Acc) -> - % No more candidates for already running. Boot all. - Rest ++ Acc. - -% Elements unique to the running set need to be killed. -find_to_stop([], Rest, Acc) -> - % The rest haven't been found, so they must all - % be ready to die. - Rest ++ Acc; -find_to_stop([D | R1], [D | R2], Acc) -> - % Elements are equal, daemon already running. - find_to_stop(R1, R2, Acc); -find_to_stop([D1 | R1], [D2 | _]=A2, Acc) when D1 < D2 -> - find_to_stop(R1, A2, Acc); -find_to_stop(A1, [D2 | R2], Acc) -> - find_to_stop(A1, R2, [D2 | Acc]); -find_to_stop(_, [], Acc) -> - % No more running daemons to worry about. - Acc. - -should_halt(Errors) -> - RetryTimeCfg = config:get("os_daemon_settings", "retry_time", "5"), - RetryTime = list_to_integer(RetryTimeCfg), - - Now = os:timestamp(), - RecentErrors = lists:filter(fun(Time) -> - timer:now_diff(Now, Time) =< RetryTime * 1000000 - end, Errors), - - RetryCfg = config:get("os_daemon_settings", "max_retries", "3"), - Retries = list_to_integer(RetryCfg), - - {length(RecentErrors) >= Retries, RecentErrors}. diff --git a/src/couch/src/couch_secondary_sup.erl b/src/couch/src/couch_secondary_sup.erl index cfbce6a9a..d78b5b15b 100644 --- a/src/couch/src/couch_secondary_sup.erl +++ b/src/couch/src/couch_secondary_sup.erl @@ -33,7 +33,6 @@ init([]) -> {"httpd", "{couch_httpd, start_link, []}"}, {"uuids", "{couch_uuids, start, []}"}, {"auth_cache", "{couch_auth_cache, start_link, []}"}, - {"os_daemons", "{couch_os_daemons, start_link, []}"}, {"compaction_daemon", "{couch_compaction_daemon, start_link, []}"} ], diff --git a/src/couch/test/couchdb_os_daemons_tests.erl b/src/couch/test/couchdb_os_daemons_tests.erl deleted file mode 100644 index 1728314bb..000000000 --- a/src/couch/test/couchdb_os_daemons_tests.erl +++ /dev/null @@ -1,259 +0,0 @@ -% 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_os_daemons_tests). - -%% tests are UNIX-specific, will not function under Windows --ifdef(WINDOWS). --undef(TEST). --define(NOTEST, 1). --endif. - --include_lib("couch/include/couch_eunit.hrl"). - -%% keep in sync with couchdb/couch_os_daemons.erl --record(daemon, { - port, - name, - cmd, - kill, - status=running, - cfg_patterns=[], - errors=[], - buf=[] -}). - --define(DAEMON_CONFIGER, "os_daemon_configer.escript"). --define(DAEMON_LOOPER, "os_daemon_looper.escript"). --define(DAEMON_BAD_PERM, "os_daemon_bad_perm.sh"). --define(DAEMON_CAN_REBOOT, "os_daemon_can_reboot.sh"). --define(DAEMON_DIE_ON_BOOT, "os_daemon_die_on_boot.sh"). --define(DAEMON_DIE_QUICKLY, "os_daemon_die_quickly.sh"). --define(TRIES, 40). --define(TRY_DELAY_MS, 100). --define(TIMEOUT, 10000). --define(CONFIG_TIMEOUT, 1000). - - -setup(DName) -> - Ctx = test_util:start(?MODULE, [couch_log], [{dont_mock, [config]}]), - {ok, OsDPid} = couch_os_daemons:start_link(), - config:set("os_daemons", DName, - filename:join([?FIXTURESDIR, DName]), false), - % Set configuration option to be used by configuration_reader_test_ - % This will be used in os_daemon_configer.escript:test_get_cfg2 - config:set("uuids", "algorithm","sequential", false), - config:set("os_daemon_settings", "max_retries", "2", false), - ensure_n_daemons_are_alive(1), - {Ctx, OsDPid}. - -teardown(_, {Ctx, OsDPid}) -> - try - test_util:stop_sync_throw(OsDPid, fun() -> - exit(OsDPid, shutdown) - end, {timeout, os_daemon_stop}, ?TIMEOUT) - catch - {timeout, os_daemon_stop} -> - Msg = "~nWARNING: OS daemons test stop ~p msec timeout exceeded~n", - io:format(standard_error, Msg, [?TIMEOUT]), - exit(OsDPid, kill) - end, - test_util:stop(Ctx). - - -os_daemons_test_() -> - { - "OS Daemons tests", - { - foreachx, - fun setup/1, fun teardown/2, - [{?DAEMON_LOOPER, Fun} || Fun <- [ - fun should_check_daemon/2, - fun should_check_daemon_table_form/2, - fun should_clean_tables_on_daemon_remove/2, - fun should_spawn_multiple_daemons/2, - fun should_keep_alive_one_daemon_on_killing_other/2 - ]] - } - }. - -configuration_reader_test_() -> - { - "OS Daemon requests CouchDB configuration", - { - foreachx, - fun setup/1, fun teardown/2, - [{?DAEMON_CONFIGER, - fun should_read_write_config_settings_by_daemon/2}] - - } - }. - -error_test_() -> - { - "OS Daemon process error tests", - { - foreachx, - fun setup/1, fun teardown/2, - [{?DAEMON_BAD_PERM, fun should_fail_due_to_lack_of_permissions/2}, - {?DAEMON_DIE_ON_BOOT, fun should_die_on_boot/2}, - {?DAEMON_DIE_QUICKLY, fun should_die_quickly/2}, - {?DAEMON_CAN_REBOOT, fun should_not_being_halted/2}] - } - }. - - -should_check_daemon(DName, _) -> - ?_test(begin - {ok, [D]} = couch_os_daemons:info([table]), - check_daemon(D, DName) - end). - -should_check_daemon_table_form(DName, _) -> - ?_test(begin - {ok, Tab} = couch_os_daemons:info(), - [D] = ets:tab2list(Tab), - check_daemon(D, DName) - end). - -should_clean_tables_on_daemon_remove(DName, _) -> - ?_test(begin - config:delete("os_daemons", DName, false), - {ok, Tab2} = couch_os_daemons:info(), - ?_assertEqual([], ets:tab2list(Tab2)) - end). - -should_spawn_multiple_daemons(DName, _) -> - ?_test(begin - config:set("os_daemons", "bar", - filename:join([?FIXTURESDIR, DName]), false), - config:set("os_daemons", "baz", - filename:join([?FIXTURESDIR, DName]), false), - ensure_n_daemons_are_alive(3), % DName, "bar" and "baz" - {ok, Daemons} = couch_os_daemons:info([table]), - lists:foreach(fun(D) -> - check_daemon(D) - end, Daemons), - {ok, Tab} = couch_os_daemons:info(), - lists:foreach(fun(D) -> - check_daemon(D) - end, ets:tab2list(Tab)) - end). - -should_keep_alive_one_daemon_on_killing_other(DName, _) -> - ?_test(begin - config:set("os_daemons", "bar", - filename:join([?FIXTURESDIR, DName]), false), - ensure_n_daemons_are_alive(2), % DName and "bar" - {ok, Daemons} = couch_os_daemons:info([table]), - lists:foreach(fun(D) -> - check_daemon(D) - end, Daemons), - - config:delete("os_daemons", "bar", false), - ensure_n_daemons_are_alive(1), % Dname only, "bar" should be dead - {ok, [D2]} = couch_os_daemons:info([table]), - check_daemon(D2, DName), - - {ok, Tab} = couch_os_daemons:info(), - [T] = ets:tab2list(Tab), - check_daemon(T, DName) - end). - -should_read_write_config_settings_by_daemon(DName, _) -> - ?_test(begin - % have to wait till daemon run all his tests - % see daemon's script for more info - timer:sleep(?CONFIG_TIMEOUT), - {ok, [D]} = couch_os_daemons:info([table]), - check_daemon(D, DName) - end). - -should_fail_due_to_lack_of_permissions(DName, _) -> - ?_test(should_halts(DName, 1000)). - -should_die_on_boot(DName, _) -> - ?_test(should_halts(DName, 2000)). - -should_die_quickly(DName, _) -> - ?_test(should_halts(DName, 4000)). - -should_not_being_halted(DName, _) -> - ?_test(begin - timer:sleep(1000), - {ok, [D1]} = couch_os_daemons:info([table]), - check_daemon(D1, DName, 0), - - % Should reboot every two seconds. We're at 1s, so wait - % until 3s to be in the middle of the next invocation's - % life span. - - timer:sleep(2000), - {ok, [D2]} = couch_os_daemons:info([table]), - check_daemon(D2, DName, 1), - - % If the kill command changed, that means we rebooted the process. - ?assertNotEqual(D1#daemon.kill, D2#daemon.kill) - end). - -should_halts(DName, Time) -> - timer:sleep(Time), - {ok, [D]} = couch_os_daemons:info([table]), - check_dead(D, DName), - config:delete("os_daemons", DName, false). - -check_daemon(D) -> - check_daemon(D, D#daemon.name). - -check_daemon(D, Name) -> - check_daemon(D, Name, 0). - -check_daemon(D, Name, Errs) -> - ?assert(is_port(D#daemon.port)), - ?assertEqual(Name, D#daemon.name), - ?assertNotEqual(undefined, D#daemon.kill), - ?assertEqual(running, D#daemon.status), - ?assertEqual(Errs, length(D#daemon.errors)), - ?assertEqual([], D#daemon.buf). - -check_dead(D, Name) -> - ?assert(is_port(D#daemon.port)), - ?assertEqual(Name, D#daemon.name), - ?assertNotEqual(undefined, D#daemon.kill), - ?assertEqual(halted, D#daemon.status), - ?assertEqual(nil, D#daemon.errors), - ?assertEqual(nil, D#daemon.buf). - -daemons() -> - {ok, Daemons} = couch_os_daemons:info([table]), - Daemons. - -ensure_n_daemons_are_alive(NumDaemons) -> - retry(fun() -> length(daemons()) == NumDaemons end, "spawning"), - retry(fun() -> - lists:all(fun(D) -> D#daemon.kill =/= undefined end, daemons()) - end, "waiting for kill flag"). - -retry(Pred, FailReason) -> - retry(Pred, ?TRIES, FailReason). - -retry(_Pred, 0, FailReason) -> - erlang:error({assertion_failed,[{module, ?MODULE}, {line, ?LINE}, - {reason, "Timed out: " ++ FailReason}]}); -retry(Pred, N, FailReason) -> - case Pred() of - true -> - ok; - false -> - timer:sleep(?TRY_DELAY_MS), - retry(Pred, N - 1, FailReason) - end. -- cgit v1.2.1 From c3af624b3aa8c6898a1448379dd517173cb6aa6f Mon Sep 17 00:00:00 2001 From: Jan Lehnardt Date: Sat, 15 Sep 2018 09:50:59 +0200 Subject: feat: point to branch on couhc_config that is needed here --- rebar.config.script | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/rebar.config.script b/rebar.config.script index 4d1649347..fd20e623e 100644 --- a/rebar.config.script +++ b/rebar.config.script @@ -48,7 +48,7 @@ SubDirs = [ DepDescs = [ %% Independent Apps -{config, "config", {tag, "1.0.4"}}, +{config, "config", {tag, "fix/conf"}}, {b64url, "b64url", {tag, "1.0.1"}}, {ets_lru, "ets-lru", {tag, "1.0.0"}}, {khash, "khash", {tag, "1.0.1"}}, -- cgit v1.2.1 From c357ff395339bc81eedfd41817054db1de0b03de Mon Sep 17 00:00:00 2001 From: Jan Lehnardt Date: Sat, 15 Sep 2018 09:51:30 +0200 Subject: test: fix os proc tests --- src/chttpd/test/chttpd_view_test.erl | 1 + src/couch/src/couch_proc_manager.erl | 6 +++--- src/couch/test/couchdb_os_proc_pool.erl | 7 +++---- 3 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/chttpd/test/chttpd_view_test.erl b/src/chttpd/test/chttpd_view_test.erl index 3457c6f30..6743f31a6 100644 --- a/src/chttpd/test/chttpd_view_test.erl +++ b/src/chttpd/test/chttpd_view_test.erl @@ -31,6 +31,7 @@ setup() -> TmpDb = ?tempdb(), Addr = config:get("chttpd", "bind_address", "127.0.0.1"), Port = mochiweb_socket_server:get(chttpd, port), + os:putenv("COUCHDB_QUERY_SERVER_JAVASCRIPT", "../../..//bin/couchjs ../../..//share/server/main.js"), Url = lists:concat(["http://", Addr, ":", Port, "/", ?b2l(TmpDb)]), create_db(Url), Url. diff --git a/src/couch/src/couch_proc_manager.erl b/src/couch/src/couch_proc_manager.erl index b6639288f..4c11618eb 100644 --- a/src/couch/src/couch_proc_manager.erl +++ b/src/couch/src/couch_proc_manager.erl @@ -394,13 +394,13 @@ get_query_server(LangStr) -> native_query_server_enabled() -> % 1. [native_query_server] enable_erlang_query_server = true | false % 2. if [native_query_server] erlang == {couch_native_process, start_link, []} -> pretend true as well - NativeEnabled = config:get_boolean("native_query_server", "enable_erlang_query_server", false), - NativeLegacyConfig = config:get("native_query_server", "erlang", ""), + NativeEnabled = config:get_boolean("native_query_servers", "enable_erlang_query_server", false), + NativeLegacyConfig = config:get("native_query_servers", "erlang", ""), NativeLegacyEnabled = NativeLegacyConfig =:= "{couch_native_process, start_link, []}", + couch_log:error("~nNativeEnabled: ~p~n", [NativeEnabled]), % there surely is a more elegant way to do this that eludes me at present case {NativeEnabled, NativeLegacyEnabled} of - {true, true} -> true; {true, _} -> true; {_, true} -> true; _ -> false diff --git a/src/couch/test/couchdb_os_proc_pool.erl b/src/couch/test/couchdb_os_proc_pool.erl index 65ae5c54c..69f8051ad 100644 --- a/src/couch/test/couchdb_os_proc_pool.erl +++ b/src/couch/test/couchdb_os_proc_pool.erl @@ -206,8 +206,7 @@ should_reduce_pool_on_idle_os_procs() -> setup_config() -> - MFA = "{couch_native_process, start_link, []}", - config:set("native_query_servers", "test_lang", MFA, false), + config:set("native_query_servers", "enable_erlang_query_server", "true", false), config:set("query_server_config", "os_process_limit", "3", false), config:set("query_server_config", "os_process_soft_limit", "2", false), ok = confirm_config("os_process_soft_limit", "2"). @@ -235,7 +234,7 @@ spawn_client() -> Parent = self(), Ref = make_ref(), Pid = spawn(fun() -> - Proc = couch_query_servers:get_os_process(<<"test_lang">>), + Proc = couch_query_servers:get_os_process(<<"erlang">>), loop(Parent, Ref, Proc) end), {Pid, Ref}. @@ -245,7 +244,7 @@ spawn_client(DDocId) -> Ref = make_ref(), Pid = spawn(fun() -> DDocKey = {DDocId, <<"1-abcdefgh">>}, - DDoc = #doc{body={[{<<"language">>, <<"test_lang">>}]}}, + DDoc = #doc{body={[{<<"language">>, <<"erlang">>}]}}, Proc = couch_query_servers:get_ddoc_process(DDoc, DDocKey), loop(Parent, Ref, Proc) end), -- cgit v1.2.1 From aba14001c740df3c15d4d4cebfd8a759c3f0b284 Mon Sep 17 00:00:00 2001 From: Jan Lehnardt Date: Sat, 15 Sep 2018 13:17:17 +0200 Subject: remove deprecated http proxy code and tests from couch_httpd --- src/couch/src/couch_httpd_proxy.erl | 428 -------------------------- src/couch/test/couchdb_http_proxy_tests.erl | 456 ---------------------------- 2 files changed, 884 deletions(-) delete mode 100644 src/couch/src/couch_httpd_proxy.erl delete mode 100644 src/couch/test/couchdb_http_proxy_tests.erl diff --git a/src/couch/src/couch_httpd_proxy.erl b/src/couch/src/couch_httpd_proxy.erl deleted file mode 100644 index d2c7acc3a..000000000 --- a/src/couch/src/couch_httpd_proxy.erl +++ /dev/null @@ -1,428 +0,0 @@ -% 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(couch_httpd_proxy). - --compile(tuple_calls). - --export([handle_proxy_req/2]). - --include_lib("couch/include/couch_db.hrl"). --include_lib("ibrowse/include/ibrowse.hrl"). - --define(TIMEOUT, infinity). --define(PKT_SIZE, 4096). - - -handle_proxy_req(Req, ProxyDest) -> - Method = get_method(Req), - Url = get_url(Req, ProxyDest), - Version = get_version(Req), - Headers = get_headers(Req), - Body = get_body(Req), - Options = [ - {http_vsn, Version}, - {headers_as_is, true}, - {response_format, binary}, - {stream_to, {self(), once}} - ], - case ibrowse:send_req(Url, Headers, Method, Body, Options, ?TIMEOUT) of - {ibrowse_req_id, ReqId} -> - stream_response(Req, ProxyDest, ReqId); - {error, Reason} -> - throw({error, Reason}) - end. - - -get_method(#httpd{mochi_req=MochiReq}) -> - case MochiReq:get(method) of - Method when is_atom(Method) -> - list_to_atom(string:to_lower(atom_to_list(Method))); - Method when is_list(Method) -> - list_to_atom(string:to_lower(Method)); - Method when is_binary(Method) -> - list_to_atom(string:to_lower(?b2l(Method))) - end. - - -get_url(Req, ProxyDest) when is_binary(ProxyDest) -> - get_url(Req, ?b2l(ProxyDest)); -get_url(#httpd{mochi_req=MochiReq}=Req, ProxyDest) -> - BaseUrl = case mochiweb_util:partition(ProxyDest, "/") of - {[], "/", _} -> couch_httpd:absolute_uri(Req, ProxyDest); - _ -> ProxyDest - end, - ProxyPrefix = "/" ++ ?b2l(hd(Req#httpd.path_parts)), - RequestedPath = MochiReq:get(raw_path), - case mochiweb_util:partition(RequestedPath, ProxyPrefix) of - {[], ProxyPrefix, []} -> - BaseUrl; - {[], ProxyPrefix, [$/ | DestPath]} -> - remove_trailing_slash(BaseUrl) ++ "/" ++ DestPath; - {[], ProxyPrefix, DestPath} -> - remove_trailing_slash(BaseUrl) ++ "/" ++ DestPath; - _Else -> - throw({invalid_url_path, {ProxyPrefix, RequestedPath}}) - end. - -get_version(#httpd{mochi_req=MochiReq}) -> - MochiReq:get(version). - - -get_headers(#httpd{mochi_req=MochiReq}) -> - to_ibrowse_headers(mochiweb_headers:to_list(MochiReq:get(headers)), []). - -to_ibrowse_headers([], Acc) -> - lists:reverse(Acc); -to_ibrowse_headers([{K, V} | Rest], Acc) when is_atom(K) -> - to_ibrowse_headers([{atom_to_list(K), V} | Rest], Acc); -to_ibrowse_headers([{K, V} | Rest], Acc) when is_list(K) -> - case string:to_lower(K) of - "content-length" -> - to_ibrowse_headers(Rest, [{content_length, V} | Acc]); - % This appears to make ibrowse too smart. - %"transfer-encoding" -> - % to_ibrowse_headers(Rest, [{transfer_encoding, V} | Acc]); - _ -> - to_ibrowse_headers(Rest, [{K, V} | Acc]) - end. - -get_body(#httpd{method='GET'}) -> - fun() -> eof end; -get_body(#httpd{method='HEAD'}) -> - fun() -> eof end; -get_body(#httpd{method='DELETE'}) -> - fun() -> eof end; -get_body(#httpd{mochi_req=MochiReq}) -> - case MochiReq:get(body_length) of - undefined -> - <<>>; - {unknown_transfer_encoding, Unknown} -> - exit({unknown_transfer_encoding, Unknown}); - chunked -> - {fun stream_chunked_body/1, {init, MochiReq, 0}}; - 0 -> - <<>>; - Length when is_integer(Length) andalso Length > 0 -> - {fun stream_length_body/1, {init, MochiReq, Length}}; - Length -> - exit({invalid_body_length, Length}) - end. - - -remove_trailing_slash(Url) -> - rem_slash(lists:reverse(Url)). - -rem_slash([]) -> - []; -rem_slash([$\s | RevUrl]) -> - rem_slash(RevUrl); -rem_slash([$\t | RevUrl]) -> - rem_slash(RevUrl); -rem_slash([$\r | RevUrl]) -> - rem_slash(RevUrl); -rem_slash([$\n | RevUrl]) -> - rem_slash(RevUrl); -rem_slash([$/ | RevUrl]) -> - rem_slash(RevUrl); -rem_slash(RevUrl) -> - lists:reverse(RevUrl). - - -stream_chunked_body({init, MReq, 0}) -> - % First chunk, do expect-continue dance. - init_body_stream(MReq), - stream_chunked_body({stream, MReq, 0, [], ?PKT_SIZE}); -stream_chunked_body({stream, MReq, 0, Buf, BRem}) -> - % Finished a chunk, get next length. If next length - % is 0, its time to try and read trailers. - {CRem, Data} = read_chunk_length(MReq), - case CRem of - 0 -> - BodyData = lists:reverse(Buf, Data), - {ok, BodyData, {trailers, MReq, [], ?PKT_SIZE}}; - _ -> - stream_chunked_body( - {stream, MReq, CRem, [Data | Buf], BRem-size(Data)} - ) - end; -stream_chunked_body({stream, MReq, CRem, Buf, BRem}) when BRem =< 0 -> - % Time to empty our buffers to the upstream socket. - BodyData = lists:reverse(Buf), - {ok, BodyData, {stream, MReq, CRem, [], ?PKT_SIZE}}; -stream_chunked_body({stream, MReq, CRem, Buf, BRem}) -> - % Buffer some more data from the client. - Length = lists:min([CRem, BRem]), - Socket = MReq:get(socket), - NewState = case mochiweb_socket:recv(Socket, Length, ?TIMEOUT) of - {ok, Data} when size(Data) == CRem -> - case mochiweb_socket:recv(Socket, 2, ?TIMEOUT) of - {ok, <<"\r\n">>} -> - {stream, MReq, 0, [<<"\r\n">>, Data | Buf], BRem-Length-2}; - _ -> - exit(normal) - end; - {ok, Data} -> - {stream, MReq, CRem-Length, [Data | Buf], BRem-Length}; - _ -> - exit(normal) - end, - stream_chunked_body(NewState); -stream_chunked_body({trailers, MReq, Buf, BRem}) when BRem =< 0 -> - % Empty our buffers and send data upstream. - BodyData = lists:reverse(Buf), - {ok, BodyData, {trailers, MReq, [], ?PKT_SIZE}}; -stream_chunked_body({trailers, MReq, Buf, BRem}) -> - % Read another trailer into the buffer or stop on an - % empty line. - Socket = MReq:get(socket), - mochiweb_socket:setopts(Socket, [{packet, line}]), - case mochiweb_socket:recv(Socket, 0, ?TIMEOUT) of - {ok, <<"\r\n">>} -> - mochiweb_socket:setopts(Socket, [{packet, raw}]), - BodyData = lists:reverse(Buf, <<"\r\n">>), - {ok, BodyData, eof}; - {ok, Footer} -> - mochiweb_socket:setopts(Socket, [{packet, raw}]), - NewState = {trailers, MReq, [Footer | Buf], BRem-size(Footer)}, - stream_chunked_body(NewState); - _ -> - exit(normal) - end; -stream_chunked_body(eof) -> - % Tell ibrowse we're done sending data. - eof. - - -stream_length_body({init, MochiReq, Length}) -> - % Do the expect-continue dance - init_body_stream(MochiReq), - stream_length_body({stream, MochiReq, Length}); -stream_length_body({stream, _MochiReq, 0}) -> - % Finished streaming. - eof; -stream_length_body({stream, MochiReq, Length}) -> - BufLen = lists:min([Length, ?PKT_SIZE]), - case MochiReq:recv(BufLen) of - <<>> -> eof; - Bin -> {ok, Bin, {stream, MochiReq, Length-BufLen}} - end. - - -init_body_stream(MochiReq) -> - Expect = case MochiReq:get_header_value("expect") of - undefined -> - undefined; - Value when is_list(Value) -> - string:to_lower(Value) - end, - case Expect of - "100-continue" -> - MochiReq:start_raw_response({100, gb_trees:empty()}); - _Else -> - ok - end. - - -read_chunk_length(MochiReq) -> - Socket = MochiReq:get(socket), - mochiweb_socket:setopts(Socket, [{packet, line}]), - case mochiweb_socket:recv(Socket, 0, ?TIMEOUT) of - {ok, Header} -> - mochiweb_socket:setopts(Socket, [{packet, raw}]), - Splitter = fun(C) -> - C =/= $\r andalso C =/= $\n andalso C =/= $\s - end, - {Hex, _Rest} = lists:splitwith(Splitter, ?b2l(Header)), - {mochihex:to_int(Hex), Header}; - _ -> - exit(normal) - end. - - -stream_response(Req, ProxyDest, ReqId) -> - receive - {ibrowse_async_headers, ReqId, "100", _} -> - % ibrowse doesn't handle 100 Continue responses which - % means we have to discard them so the proxy client - % doesn't get confused. - ibrowse:stream_next(ReqId), - stream_response(Req, ProxyDest, ReqId); - {ibrowse_async_headers, ReqId, Status, Headers} -> - {Source, Dest} = get_urls(Req, ProxyDest), - FixedHeaders = fix_headers(Source, Dest, Headers, []), - case body_length(FixedHeaders) of - chunked -> - {ok, Resp} = couch_httpd:start_chunked_response( - Req, list_to_integer(Status), FixedHeaders - ), - ibrowse:stream_next(ReqId), - stream_chunked_response(Req, ReqId, Resp), - {ok, Resp}; - Length when is_integer(Length) -> - {ok, Resp} = couch_httpd:start_response_length( - Req, list_to_integer(Status), FixedHeaders, Length - ), - ibrowse:stream_next(ReqId), - stream_length_response(Req, ReqId, Resp), - {ok, Resp}; - _ -> - {ok, Resp} = couch_httpd:start_response( - Req, list_to_integer(Status), FixedHeaders - ), - ibrowse:stream_next(ReqId), - stream_length_response(Req, ReqId, Resp), - % XXX: MochiWeb apparently doesn't look at the - % response to see if it must force close the - % connection. So we help it out here. - erlang:put(mochiweb_request_force_close, true), - {ok, Resp} - end - end. - - -stream_chunked_response(Req, ReqId, Resp) -> - receive - {ibrowse_async_response, ReqId, {error, Reason}} -> - throw({error, Reason}); - {ibrowse_async_response, ReqId, Chunk} -> - couch_httpd:send_chunk(Resp, Chunk), - ibrowse:stream_next(ReqId), - stream_chunked_response(Req, ReqId, Resp); - {ibrowse_async_response_end, ReqId} -> - couch_httpd:last_chunk(Resp) - end. - - -stream_length_response(Req, ReqId, Resp) -> - receive - {ibrowse_async_response, ReqId, {error, Reason}} -> - throw({error, Reason}); - {ibrowse_async_response, ReqId, Chunk} -> - couch_httpd:send(Resp, Chunk), - ibrowse:stream_next(ReqId), - stream_length_response(Req, ReqId, Resp); - {ibrowse_async_response_end, ReqId} -> - ok - end. - - -get_urls(Req, ProxyDest) -> - SourceUrl = couch_httpd:absolute_uri(Req, "/" ++ hd(Req#httpd.path_parts)), - Source = parse_url(?b2l(iolist_to_binary(SourceUrl))), - case (catch parse_url(ProxyDest)) of - Dest when is_record(Dest, url) -> - {Source, Dest}; - _ -> - DestUrl = couch_httpd:absolute_uri(Req, ProxyDest), - {Source, parse_url(DestUrl)} - end. - - -fix_headers(_, _, [], Acc) -> - lists:reverse(Acc); -fix_headers(Source, Dest, [{K, V} | Rest], Acc) -> - Fixed = case string:to_lower(K) of - "location" -> rewrite_location(Source, Dest, V); - "content-location" -> rewrite_location(Source, Dest, V); - "uri" -> rewrite_location(Source, Dest, V); - "destination" -> rewrite_location(Source, Dest, V); - "set-cookie" -> rewrite_cookie(Source, Dest, V); - _ -> V - end, - fix_headers(Source, Dest, Rest, [{K, Fixed} | Acc]). - - -rewrite_location(Source, #url{host=Host, port=Port, protocol=Proto}, Url) -> - case (catch parse_url(Url)) of - #url{host=Host, port=Port, protocol=Proto} = Location -> - DestLoc = #url{ - protocol=Source#url.protocol, - host=Source#url.host, - port=Source#url.port, - path=join_url_path(Source#url.path, Location#url.path) - }, - url_to_url(DestLoc); - #url{} -> - Url; - _ -> - url_to_url(Source#url{path=join_url_path(Source#url.path, Url)}) - end. - - -rewrite_cookie(_Source, _Dest, Cookie) -> - Cookie. - - -parse_url(Url) when is_binary(Url) -> - ibrowse_lib:parse_url(?b2l(Url)); -parse_url(Url) when is_list(Url) -> - ibrowse_lib:parse_url(?b2l(iolist_to_binary(Url))). - - -join_url_path(Src, Dst) -> - Src2 = case lists:reverse(Src) of - "/" ++ RestSrc -> lists:reverse(RestSrc); - _ -> Src - end, - Dst2 = case Dst of - "/" ++ RestDst -> RestDst; - _ -> Dst - end, - Src2 ++ "/" ++ Dst2. - - -url_to_url(#url{host=Host, port=Port, path=Path, protocol=Proto} = Url) -> - LPort = case {Proto, Port} of - {http, 80} -> ""; - {https, 443} -> ""; - _ -> ":" ++ integer_to_list(Port) - end, - LPath = case Path of - "/" ++ _RestPath -> Path; - _ -> "/" ++ Path - end, - HostPart = case Url#url.host_type of - ipv6_address -> - "[" ++ Host ++ "]"; - _ -> - Host - end, - atom_to_list(Proto) ++ "://" ++ HostPart ++ LPort ++ LPath. - - -body_length(Headers) -> - case is_chunked(Headers) of - true -> chunked; - _ -> content_length(Headers) - end. - - -is_chunked([]) -> - false; -is_chunked([{K, V} | Rest]) -> - case string:to_lower(K) of - "transfer-encoding" -> - string:to_lower(V) == "chunked"; - _ -> - is_chunked(Rest) - end. - -content_length([]) -> - undefined; -content_length([{K, V} | Rest]) -> - case string:to_lower(K) of - "content-length" -> - list_to_integer(V); - _ -> - content_length(Rest) - end. - diff --git a/src/couch/test/couchdb_http_proxy_tests.erl b/src/couch/test/couchdb_http_proxy_tests.erl deleted file mode 100644 index f60ba3b08..000000000 --- a/src/couch/test/couchdb_http_proxy_tests.erl +++ /dev/null @@ -1,456 +0,0 @@ -% 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). - --compile(tuple_calls). - --include_lib("couch/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) - Ctx = test_util:start_couch(?CONFIG_CHAIN ++ [?CONFIG_FIXTURE_TEMP], []), - % 49151 is IANA Reserved, let's assume no one is listening there - test_util:with_process_restart(couch_httpd, fun() -> - config:set("httpd_global_handlers", "_error", - "{couch_httpd_proxy, handle_proxy_req, <<\"http://127.0.0.1:49151/\">>}" - ) - end), - Ctx. - -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())])), - test_util:with_process_restart(couch_httpd, fun() -> - config:set("httpd_global_handlers", "_test", Value) - end), - Pid. - -teardown(Pid) -> - test_util:stop_sync_throw(Pid, fun() -> - test_web:stop() - end, {timeout, test_web_stop}, ?TIMEOUT). - -http_proxy_test_() -> - { - "HTTP Proxy handler tests", - { - setup, - fun start/0, fun test_util:stop_couch/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 = 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. -- cgit v1.2.1 From 83f4b9faf5241caa8cfffc7788c7b5bcdbde1f64 Mon Sep 17 00:00:00 2001 From: Jan Lehnardt Date: Sat, 15 Sep 2018 13:42:34 +0200 Subject: remove daemons from config js tests --- test/javascript/tests/config.js | 3 --- 1 file changed, 3 deletions(-) diff --git a/test/javascript/tests/config.js b/test/javascript/tests/config.js index 8c7ce9917..a9dce637f 100644 --- a/test/javascript/tests/config.js +++ b/test/javascript/tests/config.js @@ -50,10 +50,7 @@ couchTests.config = function(debug) { } T(config.couchdb.database_dir); - T(config.daemons.httpd); - T(config.httpd_global_handlers._config); T(config.log.level); - T(config.query_servers.javascript); // test that settings can be altered, and that an undefined whitelist allows any change TEquals(undefined, config.httpd.config_whitelist, "Default whitelist is empty"); -- cgit v1.2.1 From cbbd2fad30a951971a66899ad1d184220263b976 Mon Sep 17 00:00:00 2001 From: Jan Lehnardt Date: Sun, 16 Sep 2018 16:44:21 +0200 Subject: compatibility for erlang < 20 --- src/chttpd/test/chttpd_view_test.erl | 2 +- src/couch/src/couch_proc_manager.erl | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/src/chttpd/test/chttpd_view_test.erl b/src/chttpd/test/chttpd_view_test.erl index 6743f31a6..114eb089b 100644 --- a/src/chttpd/test/chttpd_view_test.erl +++ b/src/chttpd/test/chttpd_view_test.erl @@ -31,7 +31,7 @@ setup() -> TmpDb = ?tempdb(), Addr = config:get("chttpd", "bind_address", "127.0.0.1"), Port = mochiweb_socket_server:get(chttpd, port), - os:putenv("COUCHDB_QUERY_SERVER_JAVASCRIPT", "../../..//bin/couchjs ../../..//share/server/main.js"), + os:putenv("COUCHDB_QUERY_SERVER_JAVASCRIPT", "../../../bin/couchjs ../../../share/server/main.js"), Url = lists:concat(["http://", Addr, ":", Port, "/", ?b2l(TmpDb)]), create_db(Url), Url. diff --git a/src/couch/src/couch_proc_manager.erl b/src/couch/src/couch_proc_manager.erl index 4c11618eb..26fe5ade5 100644 --- a/src/couch/src/couch_proc_manager.erl +++ b/src/couch/src/couch_proc_manager.erl @@ -376,7 +376,7 @@ get_env_for_spec(Spec, Target) -> % loop over os:getenv(), match SPEC_TARGET lists:filtermap(fun(VarName) -> SpecStr = Spec ++ Target, - case string:split(VarName, "=") of + case string:tokens(VarName, "=") of [SpecStr, Cmd] -> {true, Cmd}; _Else -> false end @@ -385,7 +385,7 @@ get_env_for_spec(Spec, Target) -> get_query_server(LangStr) -> % look for COUCH_QUERY_SERVER_LANGSTR in env % if exists, return value, else undefined - UpperLangString = string:uppercase(LangStr), + UpperLangString = string:to_upper(LangStr), case get_env_for_spec("COUCHDB_QUERY_SERVER_", UpperLangString) of [] -> undefined; [Command] -> Command @@ -398,7 +398,6 @@ native_query_server_enabled() -> NativeLegacyConfig = config:get("native_query_servers", "erlang", ""), NativeLegacyEnabled = NativeLegacyConfig =:= "{couch_native_process, start_link, []}", - couch_log:error("~nNativeEnabled: ~p~n", [NativeEnabled]), % there surely is a more elegant way to do this that eludes me at present case {NativeEnabled, NativeLegacyEnabled} of {true, _} -> true; -- cgit v1.2.1 From 0b88ebce3ba740314feb76231e0374539eb83383 Mon Sep 17 00:00:00 2001 From: Jan Lehnardt Date: Mon, 17 Sep 2018 18:14:33 +0200 Subject: simplify, h/t @vatamane --- src/couch/src/couch_proc_manager.erl | 7 +------ 1 file changed, 1 insertion(+), 6 deletions(-) diff --git a/src/couch/src/couch_proc_manager.erl b/src/couch/src/couch_proc_manager.erl index 26fe5ade5..d2a198e82 100644 --- a/src/couch/src/couch_proc_manager.erl +++ b/src/couch/src/couch_proc_manager.erl @@ -398,12 +398,7 @@ native_query_server_enabled() -> NativeLegacyConfig = config:get("native_query_servers", "erlang", ""), NativeLegacyEnabled = NativeLegacyConfig =:= "{couch_native_process, start_link, []}", - % there surely is a more elegant way to do this that eludes me at present - case {NativeEnabled, NativeLegacyEnabled} of - {true, _} -> true; - {_, true} -> true; - _ -> false - end. + NativeEnabled orelse NativeLegacyEnabled. get_native_query_server("query") -> % mango query server "{mango_native_proc, start_link, []}"; -- cgit v1.2.1 From 73b32713c33be7ddc2e64ab1fd1be0f472f6d3e2 Mon Sep 17 00:00:00 2001 From: Jan Lehnardt Date: Thu, 20 Sep 2018 11:53:47 +0200 Subject: feat: move handler definition to .app file --- src/couch/src/couch.app.src | 36 ++++++++++++++++++++++++++++++++++++ src/couch/src/couch_httpd.erl | 38 +++----------------------------------- 2 files changed, 39 insertions(+), 35 deletions(-) diff --git a/src/couch/src/couch.app.src b/src/couch/src/couch.app.src index 6af213624..208938745 100644 --- a/src/couch/src/couch.app.src +++ b/src/couch/src/couch.app.src @@ -48,5 +48,41 @@ ioq, couch_stats, hyper + ]}, + {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}"}, + {"_system", "{chttpd_misc, handle_system_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}"}, + {"_view_changes", "{couch_mrview_http, handle_view_changes_req}"} + ]} ]} ]}. diff --git a/src/couch/src/couch_httpd.erl b/src/couch/src/couch_httpd.erl index 3177e0673..861fd58c4 100644 --- a/src/couch/src/couch_httpd.erl +++ b/src/couch/src/couch_httpd.erl @@ -106,53 +106,21 @@ start_link(Name, Options) -> ok = validate_bind_address(BindAddress), DefaultFun = make_arity_1_fun("{couch_httpd_db, handle_request}"), - - HttpdGlobalHandlers = [ - {"/", "{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}"}, - {"_system", "{chttpd_misc, handle_system_req}"} - ], + {ok, HttpdGlobalHandlers} = application:get_env(httpd_global_handlers), UrlHandlersList = lists:map( fun({UrlKey, SpecStr}) -> {?l2b(UrlKey), make_arity_1_fun(SpecStr)} end, HttpdGlobalHandlers), - HttpdDbHandlers = [ - {"_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}"} - ], + {ok, HttpdDbHandlers} = application:get_env(httpd_db_handlers), DbUrlHandlersList = lists:map( fun({UrlKey, SpecStr}) -> {?l2b(UrlKey), make_arity_2_fun(SpecStr)} end, HttpdDbHandlers), - HttpdDesignHandlers = [ - {"_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}"}, - {"_view_changes", "{couch_mrview_http, handle_view_changes_req}"} - ], + {ok, HttpdDesignHandlers} = application:get_env(httpd_design_handlers), DesignUrlHandlersList = lists:map( fun({UrlKey, SpecStr}) -> -- cgit v1.2.1 From 8a1e54c907427307c74b05a1c92fdcd11537bbf3 Mon Sep 17 00:00:00 2001 From: Jan Lehnardt Date: Fri, 9 Nov 2018 10:42:36 +0100 Subject: feat: remove redundant string parsing --- src/couch/src/couch_secondary_sup.erl | 22 +++++++++++----------- 1 file changed, 11 insertions(+), 11 deletions(-) diff --git a/src/couch/src/couch_secondary_sup.erl b/src/couch/src/couch_secondary_sup.erl index d78b5b15b..255c84980 100644 --- a/src/couch/src/couch_secondary_sup.erl +++ b/src/couch/src/couch_secondary_sup.erl @@ -27,27 +27,27 @@ init([]) -> dynamic} ], Daemons = [ - {"index_server", "{couch_index_server, start_link, []}"}, - {"query_servers", "{couch_proc_manager, start_link, []}"}, - {"vhosts", "{couch_httpd_vhost, start_link, []}"}, - {"httpd", "{couch_httpd, start_link, []}"}, - {"uuids", "{couch_uuids, start, []}"}, - {"auth_cache", "{couch_auth_cache, start_link, []}"}, - {"compaction_daemon", "{couch_compaction_daemon, start_link, []}"} + {index_server, {couch_index_server, start_link, []}}, + {query_servers, {couch_proc_manager, start_link, []}}, + {vhosts, {couch_httpd_vhost, start_link, []}}, + {httpd, {couch_httpd, start_link, []}}, + {uuids, {couch_uuids, start, []}}, + {auth_cache, {couch_auth_cache, start_link, []}}, + {compaction_daemon, {couch_compaction_daemon, start_link, []}} ], Children = SecondarySupervisors ++ [ begin - {ok, {Module, Fun, Args}} = couch_util:parse_term(SpecStr), + {Module, Fun, Args} = Spec, - {list_to_atom(Name), + {Name, {Module, Fun, Args}, permanent, brutal_kill, worker, [Module]} end - || {Name, SpecStr} - <- Daemons, SpecStr /= ""], + || {Name, Spec} + <- Daemons, Spec /= ""], {ok, {{one_for_one, 50, 3600}, couch_epi:register_service(couch_db_epi, Children)}}. -- cgit v1.2.1 From c850ffa889dc1f8bf562107aa07da429357bc8ef Mon Sep 17 00:00:00 2001 From: Jan Lehnardt Date: Fri, 9 Nov 2018 11:23:04 +0100 Subject: feat: change enaabling of chttpsd to `[ssl] enable = true` The previous configuration `[daemons] httpsd = {chttpd, start_link, [https]}` is also sill supported for backwards cmpatibility reasons. --- rel/overlay/etc/local.ini | 6 +----- src/couch/src/couch_secondary_sup.erl | 16 +++++++++++++++- 2 files changed, 16 insertions(+), 6 deletions(-) diff --git a/rel/overlay/etc/local.ini b/rel/overlay/etc/local.ini index ea5467c9a..ecc97f466 100644 --- a/rel/overlay/etc/local.ini +++ b/rel/overlay/etc/local.ini @@ -52,12 +52,8 @@ ; Basic realm="server" in order to prevent you getting logged out. ; require_valid_user = false -[daemons] -; enable SSL support by uncommenting the following line and supply the PEM's below. -; the default ssl port CouchDB listens on is 6984 -; httpsd = {chttpd, start_link, [https]} - [ssl] +;enable = true ;cert_file = /full/path/to/server_cert.pem ;key_file = /full/path/to/server_key.pem ;password = somepassword diff --git a/src/couch/src/couch_secondary_sup.erl b/src/couch/src/couch_secondary_sup.erl index 255c84980..9b424dc6a 100644 --- a/src/couch/src/couch_secondary_sup.erl +++ b/src/couch/src/couch_secondary_sup.erl @@ -36,6 +36,11 @@ init([]) -> {compaction_daemon, {couch_compaction_daemon, start_link, []}} ], + MaybeHttps = case https_enabled() of + true -> [{httpsd, {chttpd, start_link, [https]}}]; + _False -> [] + end, + Children = SecondarySupervisors ++ [ begin {Module, Fun, Args} = Spec, @@ -48,6 +53,15 @@ init([]) -> [Module]} end || {Name, Spec} - <- Daemons, Spec /= ""], + <- Daemons ++ MaybeHttps, Spec /= ""], {ok, {{one_for_one, 50, 3600}, couch_epi:register_service(couch_db_epi, Children)}}. + +https_enabled() -> + % 1. [ssl] enable = true | false + % 2. if [daemons] httpsd == {chttpd, start_link, [https]} -> pretend true as well + SSLEnabled = config:get_boolean("ssl", "enable", false), + LegacySSL = config:get("daemons", "httpsd"), + LegacySSLEnabled = LegacySSL =:= "{chttpd, start_link, [https]}", + + SSLEnabled orelse LegacySSLEnabled. -- cgit v1.2.1