Permalink
Browse files

make couch_httpd more resistant to failures.

If vhosts or the authentication cache module crash we need to make sure that
HTTPd bindings are restarted as well.
  • Loading branch information...
1 parent b93e98a commit 7360b83a4aa529c88157a939ce7e776398565263 @benoitc benoitc committed Jun 12, 2012
View
@@ -21,12 +21,6 @@
{mochiweb, ".*", {git,"git://github.com/refuge/mochiweb.git",
{branch, "master"}}},
- %% cowboy adapter to mochiweb
- {mochicow, ".*", {git,"git://github.com/benoitc/mochicow.git",
- {branch, "mochicow_request"}}},
-
-
-
%% snappy module, used for compression
{snappy, ".*", {git, "git://github.com/refuge/snappy-erlang.git",
{branch, "refuge"}}},
@@ -0,0 +1,22 @@
+%% -*- tab-width: 4;erlang-indent-level: 4;indent-tabs-mode: nil -*-
+%% ex: ft=erlang ts=4 sw=4 et
+
+{cover_enabled, true}.
+{eunit_opts, [{report,{eunit_surefire,[{dir,"."}]}}]}.
+{edoc_opts, [{preprocess, true}]}.
+{erl_opts, [warnings_as_errors, {parse_transform, lager_transform}]}.
+
+{deps, [
+ %% mochiweb module, http framework
+ {mochiweb, ".*", {git,"git://github.com/refuge/mochiweb.git",
+ {branch, "master"}}},
+
+ %% cowboy adapter to mochiweb
+ {mochicow, ".*", {git,"git://github.com/benoitc/mochicow.git",
+ {branch, "refuge"}}},
+
+ %% logging
+ {lager, ".*", {git, "git://github.com/basho/lager.git",
+ {branch, "master"}}}
+]}.
+
@@ -10,5 +10,5 @@
{mod, {couch_httpd_app, []}},
{applications, [kernel, stdlib, crypto, sasl,
- public_key, ssl, inets, oauth, ibrowse, mochiweb, cowboy, couch]}
+ public_key, ssl, inets, oauth, ibrowse, mochiweb, cowboy, lager, couch]}
]}.
@@ -32,7 +32,7 @@
-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([vendor_id/0]).
--export([display_uris/0, display_uris/1, get_listeners/0]).
+-export([display_uris/0, display_uris/1, get_bindings/0]).
ssl_options() ->
CertFile = couch_config:get("ssl", "cert_file", nil),
@@ -194,22 +194,22 @@ get_protocol_options() ->
{ok, [{dispatch, Dispatch}]}.
display_uris() ->
- display_uris([]).
+ display_uris(get_bindings()).
-display_uris(_) ->
+display_uris(Bindings) ->
Ip = couch_config:get("httpd", "bind_address"),
- lists:foreach(fun(Ref) ->
- Uri = couch_httpd_util:get_uri(Ref, Ip),
+ lists:foreach(fun(Binding) ->
+ Uri = couch_httpd_util:get_uri(Binding, Ip),
lager:info("HTTP API started on ~p~n", [Uri])
- end, get_listeners()).
+ end, Bindings).
-get_listeners() ->
- case couch_config:get("ssl", "enable", "false") of
- "true" ->
- [http, https];
- _ ->
- [http]
- end.
+get_bindings() ->
+ SchemeStr = couch_config:get("httpd", "scheme", "http"),
+ SchemeList = re:split(SchemeStr, "\\s*,\\s*",[{return, list}]),
+
+ lists:foldl(fun(S, Acc) ->
+ [list_to_atom(S) | Acc]
+ end, [], lists:reverse(SchemeList)).
set_auth_handlers() ->
AuthenticationSrcs = make_fun_spec_strs(
@@ -0,0 +1,28 @@
+% 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_binding_sup).
+-behaviour(supervisor).
+
+-export([start_link/0]).
+-export([init/1]).
+
+-define(CHILD(I), {I, {I, start_link, []}, permanent, brutal_kill, worker, [I]}).
+start_link() ->
+ supervisor:start_link({local, ?MODULE}, ?MODULE, []).
+
+
+init([]) ->
+ Bindings = lists:map(fun(Binding) ->
+ couch_httpd:child_spec(Binding)
+ end, couch_httpd:get_bindings()),
+ {ok, {{one_for_one, 10, 3600}, Bindings}}.
@@ -6,30 +6,22 @@
-export([set_protocol_options/0, get_protocol_options/1,
set_protocol_options/2, restart_httpd/0, restart_listener/1,
- stop_listener/1]).
+ stop_listener/1, ref_to_listener_pid/1]).
-export([start_link/0, config_change/2]).
-export([init/1, handle_call/3, handle_cast/2, handle_info/2,
terminate/2, code_change/3]).
set_protocol_options() ->
{ok, Options} = couch_httpd:get_protocol_options(),
- set_protocol_options(http, Options),
- case couch_config:get("ssl", "enable", "false") of
- "true" ->
- set_protocol_options(https, Options);
- "false" ->
- ok
- end.
+ lists:foreach(fun(Binding) ->
+ set_protocol_options(Binding, Options)
+ end, couch_httpd:get_bindings()).
restart_httpd() ->
- restart_listener(http),
- case couch_config:get("ssl", "enable", "false") of
- "true" ->
- restart_listener(https);
- _ ->
- ok
- end.
+ lists:foreach(fun(Binding) ->
+ restart_listener(Binding)
+ end, couch_httpd:get_bindings()).
restart_listener(Ref) ->
stop_listener(Ref),
@@ -68,7 +60,6 @@ start_link() ->
gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).
init(_) ->
- lager:info("start httpd config~n", []),
%% register to config changes
ok = couch_config:register(fun ?MODULE:config_change/2),
{ok, nil}.
@@ -110,7 +101,7 @@ config_change("ssl", _) ->
-spec ref_to_listener_pid(any()) -> pid().
ref_to_listener_pid(Ref) ->
- Children = supervisor:which_children(couch_httpd_sup),
+ Children = supervisor:which_children(couch_httpd_binding_sup),
{_, ListenerSupPid, _, _} = lists:keyfind(
{cowboy_listener_sup, Ref}, 1, Children),
ListenerSupChildren = supervisor:which_children(ListenerSupPid),
@@ -16,53 +16,15 @@
-export([start_link/0]).
-export([init/1]).
-
-%% Helper macro for declaring children of supervisor
-%% -define(CHILD(I, Type), {I, {I, start_link, []}, permanent, 5000, Type, [I]}).
--define(CHILD(I), {I, {I, start_link, []}, permanent, brutal_kill, worker, [I]}).
--define(CHILD(I, M, A), {I, {M, start_link, A}, permanent, brutal_kill, worker,
- [M]}).
+-define(CHILD(I), {I, {I, start_link, []}, permanent, brutal_kill,
+ worker, [I]}).
+-define(SUP(I), {I, {I, start_link, []}, permanent, 5000,
+ supervisor, [I]}).
start_link() ->
- {ok, Pid} = supervisor:start_link({local, ?MODULE}, ?MODULE, []),
- %% announce HTTP uri
- couch_httpd:display_uris(),
- %% write uri file
- write_uri_file(),
- {ok, Pid}.
-
-write_uri_file() ->
- Ip = couch_config:get("httpd", "bind_address"),
- Listeners = couch_httpd:get_listeners(),
- Uris = [couch_httpd_util:get_uri(Name, Ip) || Name <- Listeners],
- case couch_config:get("couchdb", "uri_file", null) of
- null -> ok;
- UriFile ->
- Lines = [begin case Uri of
- undefined -> [];
- Uri -> io_lib:format("~s~n", [Uri])
- end end || Uri <- Uris],
- case file:write_file(UriFile, Lines) of
- ok -> ok;
- {error, eacces} ->
- lager:info("Permission error when writing to URI file ~s", [UriFile]),
- throw({file_permission_error, UriFile});
- Error2 ->
- lager:info("Failed to write to URI file ~s: ~p~n", [UriFile, Error2]),
- throw(Error2)
- end
- end.
+ supervisor:start_link({local, ?MODULE}, ?MODULE, []).
init([]) ->
- HTTP = couch_httpd:child_spec(http),
- VHost = ?CHILD(couch_httpd_vhost),
- AuthCache = ?CHILD(couch_auth_cache),
- HTTPs = case couch_config:get("ssl", "enable", "false") of
- "true" ->
- [couch_httpd:child_spec(https)];
- _ ->
- []
- end,
+ VhostSup = ?SUP(couch_httpd_vhosts_sup),
Config = ?CHILD(couch_httpd_config),
- {ok, {{one_for_one, 10, 3600},
- [VHost, AuthCache, Config, HTTP] ++ HTTPs}}.
+ {ok, {{one_for_one, 10, 3600}, [VhostSup, Config]}}.
@@ -20,22 +20,11 @@ get_scheme(https) -> "https".
%%
-spec get_port(any()) -> inet:port_number().
get_port(Ref) ->
- case ref_to_listener_pid(Ref) of
+ case couch_httpd_config:ref_to_listener_pid(Ref) of
false ->
undefined;
ListenerPid ->
cowboy_listener:get_port(ListenerPid)
end.
-%% Internal.
-
--spec ref_to_listener_pid(any()) -> pid().
-ref_to_listener_pid(Ref) ->
- Children = supervisor:which_children(couch_httpd_sup),
- {_, ListenerSupPid, _, _} = lists:keyfind(
- {cowboy_listener_sup, Ref}, 1, Children),
- ListenerSupChildren = supervisor:which_children(ListenerSupPid),
- {_, ListenerPid, _, _} = lists:keyfind(
- cowboy_listener, 1, ListenerSupChildren),
- ListenerPid.
@@ -0,0 +1,59 @@
+% 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_vhosts_sup).
+-behaviour(supervisor).
+
+-export([start_link/0, write_uri_file/0]).
+-export([init/1]).
+
+-define(SUP(I), {I, {I, start_link, []}, permanent, 5000,
+ supervisor, [I]}).
+-define(CHILD(I), {I, {I, start_link, []}, permanent, brutal_kill,
+ worker, [I]}).
+
+
+start_link() ->
+ {ok, Pid} = supervisor:start_link({local, ?MODULE}, ?MODULE, []),
+ %% announce HTTP uri
+ couch_httpd:display_uris(),
+ %% write uri file
+ write_uri_file(),
+ {ok, Pid}.
+
+write_uri_file() ->
+ Ip = couch_config:get("httpd", "bind_address"),
+ Listeners = couch_httpd:get_bindings(),
+ Uris = [couch_httpd_util:get_uri(Name, Ip) || Name <- Listeners],
+ case couch_config:get("couchdb", "uri_file", null) of
+ null -> ok;
+ UriFile ->
+ Lines = [begin case Uri of
+ undefined -> [];
+ Uri -> io_lib:format("~s~n", [Uri])
+ end end || Uri <- Uris],
+ case file:write_file(UriFile, Lines) of
+ ok -> ok;
+ {error, eacces} ->
+ lager:info("Permission error when writing to URI file ~s", [UriFile]),
+ throw({file_permission_error, UriFile});
+ Error2 ->
+ lager:info("Failed to write to URI file ~s: ~p~n", [UriFile, Error2]),
+ throw(Error2)
+ end
+ end.
+
+init([]) ->
+ AuthCache = ?CHILD(couch_auth_cache),
+ Vhost = ?CHILD(couch_httpd_vhost),
+ Binding = ?SUP(couch_httpd_binding_sup),
+ {ok, {{one_for_all, 10, 10}, [AuthCache, Vhost, Binding]}}.

0 comments on commit 7360b83

Please sign in to comment.