Skip to content

HTTPS clone URL

Subversion checkout URL

You can clone with HTTPS or Subversion.

Download ZIP
Browse files

Upgrade the cowboy reauest to a mochiweb request

Instead of only using the cowboy requets pool we now use a cowboy
handler that upgrades the connections to a mochiweb request. It will
will quietly allow us to replace mochiweb by cowboy. While I was here I
also changed the way config changes are handled: the HTTP(s) listener is
not any more restarted when only the dispatching rules change. Instead
we update the cowboy dispatching rules.
  • Loading branch information...
commit b93e98a14ad2c129f66364bdcaea3779659f4799 1 parent 2847210
@benoitc benoitc authored
View
3  apps/couch/rebar.config
@@ -23,7 +23,8 @@
%% cowboy adapter to mochiweb
{mochicow, ".*", {git,"git://github.com/benoitc/mochicow.git",
- {branch, "refuge"}}},
+ {branch, "mochicow_request"}}},
+
%% snappy module, used for compression
View
1  apps/couch_httpd/src/couch_httpd.app.src
@@ -3,6 +3,7 @@
{vsn, "1.3.0"},
{registered, [
couch_httpd,
+ couch_httpd_config,
couch_httpd_sup
]},
View
69 apps/couch_httpd/src/couch_httpd.erl
@@ -14,7 +14,8 @@
-include_lib("couch/include/couch_db.hrl").
-include_lib("couch_httpd/include/couch_httpd.hrl").
--export([handle_request/5, child_spec/1]).
+-export([handle_request/2, child_spec/1, init/3, get_protocol_options/0,
+ set_auth_handlers/0]).
-export([header_value/2,header_value/3,qs_value/2,qs_value/3,qs/1,qs_json_value/3]).
-export([path/1,absolute_uri/2,body_length/1]).
@@ -133,7 +134,6 @@ server_options() ->
[{ip, ParsedIp} | ServerOptions].
-
child_spec(http) ->
Port = list_to_integer(couch_config:get("httpd", "port", "5984")),
child_spec(http, cowboy_tcp_transport, Port, server_options());
@@ -143,6 +143,17 @@ child_spec(https) ->
child_spec(https, cowboy_ssl_transport, Port, Options).
child_spec(Name, Transport, Port, TransOpts0) ->
+ {ok, ProtoOpts} = get_protocol_options(),
+ set_auth_handlers(),
+ NbAcceptors = list_to_integer(
+ couch_config:get("httpd", "nb_acceptors", "100")
+ ),
+
+ TransOpts = [{port, Port}|TransOpts0],
+ cowboy:child_spec(Name, NbAcceptors, Transport, TransOpts,
+ cowboy_http_protocol, ProtoOpts).
+
+get_protocol_options() ->
DefaultSpec = "{couch_httpd_db, handle_request}",
DefaultFun = make_arity_1_fun(
couch_config:get("httpd", "default_handler", DefaultSpec)
@@ -166,29 +177,21 @@ child_spec(Name, Transport, Port, TransOpts0) ->
UrlHandlers = dict:from_list(UrlHandlersList),
DbUrlHandlers = dict:from_list(DbUrlHandlersList),
DesignUrlHandlers = dict:from_list(DesignUrlHandlersList),
+
{ok, SocketOptions} = couch_util:parse_term(
- couch_config:get("httpd", "socket_options", "[]")),
+ couch_config:get("httpd", "socket_options", "[]")
+ ),
- set_auth_handlers(),
+ Args = [{DefaultFun, UrlHandlers, DbUrlHandlers, DesignUrlHandlers,
+ SocketOptions}],
+ HttpdOptions = [{loop, {couch_httpd, handle_request, Args}}],
- Loop = fun(Req)->
- case SocketOptions of
- [] ->
- ok;
- _ ->
- ok = mochiweb_socket:setopts(Req:get(socket), SocketOptions)
- end,
- apply(?MODULE, handle_request, [
- Req, DefaultFun, UrlHandlers, DbUrlHandlers, DesignUrlHandlers
- ])
- end,
- NbAcceptors = list_to_integer(
- couch_config:get("httpd", "nb_acceptors", "100")
- ),
+ Dispatch = [
+ %% {Host, list({Path, Handler, Opts})}
+ {'_', [{'_', couch_httpd, HttpdOptions}]}
+ ],
- TransOpts = [{port, Port}|TransOpts0],
- cowboy:child_spec(Name, NbAcceptors, Transport, TransOpts,
- mochicow_protocol, [{loop, Loop}]).
+ {ok, [{dispatch, Dispatch}]}.
display_uris() ->
display_uris([]).
@@ -245,13 +248,25 @@ make_arity_3_fun(SpecStr) ->
make_fun_spec_strs(SpecStr) ->
re:split(SpecStr, "(?<=})\\s*,\\s*(?={)", [{return, list}]).
-handle_request(MochiReq, DefaultFun, UrlHandlers, DbUrlHandlers,
- DesignUrlHandlers) ->
+init(_Prot, _Req, _Opts) ->
+ {upgrade, protocol, mochicow_upgrade}.
+
+handle_request(Req, {DefaultFun, UrlHandlers, DbUrlHandlers,
+ DesignUrlHandlers, SocketOptions}) ->
+
+ case SocketOptions of
+ [] ->
+ ok;
+ _ ->
+ ok = mochiweb_socket:setopts(Req:get(socket), SocketOptions)
+ end,
- MochiReq1 = couch_httpd_vhost:dispatch_host(MochiReq),
+ %% vhost rewriting
+ Req1 = couch_httpd_vhost:dispatch_host(Req),
- handle_request_int(MochiReq1, DefaultFun,
- UrlHandlers, DbUrlHandlers, DesignUrlHandlers).
+ %% finally handle the request
+ handle_request_int(Req1, DefaultFun, UrlHandlers, DbUrlHandlers,
+ DesignUrlHandlers).
handle_request_int(MochiReq, DefaultFun,
UrlHandlers, DbUrlHandlers, DesignUrlHandlers) ->
@@ -943,7 +958,7 @@ send_chunked_error(Resp, Error) ->
last_chunk(Resp).
send_redirect(Req, Path) ->
- send_response(Req, 301, [{"Location", absolute_uri(Req, Path)}], <<>>).
+ send_response(Req, 301, [{"Location", absolute_uri(Req, Path)}], <<>>).
negotiate_content_type(Req) ->
case get(jsonp) of
View
3  apps/couch_httpd/src/couch_httpd_auth.erl
@@ -127,7 +127,8 @@ proxy_auth_user(Req) ->
XHeaderToken = couch_config:get("couch_httpd_auth", "x_auth_token",
"X-Auth-CouchDB-Token"),
case header_value(Req, XHeaderUserName) of
- undefined -> nil;
+ undefined ->
+ nil;
UserName ->
Roles = case header_value(Req, XHeaderRoles) of
undefined -> [];
View
119 apps/couch_httpd/src/couch_httpd_config.erl
@@ -0,0 +1,119 @@
+%% @doc module to keep couch_httpd config
+%%
+-module(couch_httpd_config).
+
+-behaviour(gen_server).
+
+-export([set_protocol_options/0, get_protocol_options/1,
+ set_protocol_options/2, restart_httpd/0, restart_listener/1,
+ stop_listener/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.
+
+restart_httpd() ->
+ restart_listener(http),
+ case couch_config:get("ssl", "enable", "false") of
+ "true" ->
+ restart_listener(https);
+ _ ->
+ ok
+ end.
+
+restart_listener(Ref) ->
+ stop_listener(Ref),
+ supervisor:start_child(couch_httpd_sup, couch_httpd:child_spec(Ref)).
+
+
+stop_listener(Ref) ->
+ case supervisor:terminate_child(couch_httpd_sup,
+ {cowboy_listener_sup, Ref}) of
+ ok ->
+ supervisor:delete_child(couch_httpd_sup,
+ {cowboy_listener_sup, Ref});
+ {error, Reason} ->
+ {error, Reason}
+ end.
+
+
+%% @doc Return the current protocol options for the given listener.
+-spec get_protocol_options(any()) -> any().
+get_protocol_options(Ref) ->
+ ListenerPid = ref_to_listener_pid(Ref),
+ {ok, ProtoOpts} = cowboy_listener:get_protocol_options(ListenerPid),
+ ProtoOpts.
+
+%% @doc Upgrade the protocol options for the given listener.
+%%
+%% The upgrade takes place at the acceptor level, meaning that only the
+%% newly accepted connections receive the new protocol options. This has
+%% no effect on the currently opened connections.
+-spec set_protocol_options(any(), any()) -> ok.
+set_protocol_options(Ref, ProtoOpts) ->
+ ListenerPid = ref_to_listener_pid(Ref),
+ ok = cowboy_listener:set_protocol_options(ListenerPid, ProtoOpts).
+
+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}.
+
+handle_call(_Msg, _From, State) ->
+ {noreply, State}.
+
+handle_cast(_Msg, State) ->
+ {noreply, State}.
+
+handle_info(_Info, State) ->
+ {noreply, State}.
+
+terminate(_Reason, _State) ->
+ ok.
+
+code_change(_OldVsn, State, _Extra) ->
+ {ok, State}.
+
+config_change("httpd", "bind_address") ->
+ restart_httpd();
+config_change("httpd", "port") ->
+ restart_listener(http);
+config_change("httpd", "default_handler") ->
+ set_protocol_options();
+config_change("httpd", "server_options") ->
+ set_protocol_options();
+config_change("httpd", "socket_options") ->
+ set_protocol_options();
+config_change("httpd", "authentication_handlers") ->
+ couch_httpd:set_auth_handlers();
+config_change("httpd_global_handlers", _) ->
+ set_protocol_options();
+config_change("httpd_db_handlers", _) ->
+ set_protocol_options();
+config_change("ssl", _) ->
+ restart_listener(https).
+
+
+-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.
View
55 apps/couch_httpd/src/couch_httpd_sup.erl
@@ -14,7 +14,7 @@
-behaviour(supervisor).
-export([start_link/0]).
--export([init/1, config_change/2]).
+-export([init/1]).
%% Helper macro for declaring children of supervisor
@@ -25,13 +25,8 @@
start_link() ->
{ok, Pid} = supervisor:start_link({local, ?MODULE}, ?MODULE, []),
-
- %% register to config changes
- ok = couch_config:register(fun ?MODULE:config_change/2, Pid),
-
%% announce HTTP uri
couch_httpd:display_uris(),
-
%% write uri file
write_uri_file(),
{ok, Pid}.
@@ -68,48 +63,6 @@ init([]) ->
_ ->
[]
end,
- {ok, {{one_for_one, 10, 3600}, [HTTP, VHost, AuthCache] ++ HTTPs}}.
-
-
-config_change("httpd", "bind_address") ->
- restart_httpd();
-config_change("httpd", "port") ->
- restart_listener(http);
-config_change("httpd", "default_handler") ->
- restart_httpd();
-config_change("httpd", "server_options") ->
- restart_httpd();
-config_change("httpd", "socket_options") ->
- restart_httpd();
-config_change("httpd", "authentication_handlers") ->
- couch_httpd:set_auth_handlers();
-config_change("httpd_global_handlers", _) ->
- restart_httpd();
-config_change("httpd_db_handlers", _) ->
- restart_httpd();
-config_change("ssl", _) ->
- restart_listener(https).
-
-
-restart_httpd() ->
- restart_listener(http),
- case couch_config:get("ssl", "enable", "false") of
- "true" ->
- restart_listener(https);
- _ ->
- ok
- end.
-
-restart_listener(Ref) ->
- stop_listener(Ref),
- supervisor:start_child(?MODULE, couch_httpd:child_spec(Ref)).
-
-
-stop_listener(Ref) ->
- case supervisor:terminate_child(?MODULE, {cowboy_listener_sup, Ref}) of
- ok ->
- supervisor:delete_child(?MODULE, {cowboy_listener_sup, Ref});
- {error, Reason} ->
- {error, Reason}
- end.
-
+ Config = ?CHILD(couch_httpd_config),
+ {ok, {{one_for_one, 10, 3600},
+ [VHost, AuthCache, Config, HTTP] ++ HTTPs}}.
Please sign in to comment.
Something went wrong with that request. Please try again.