You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@couchdb.apache.org by fd...@apache.org on 2011/07/10 14:22:05 UTC

svn commit: r1144848 - /couchdb/trunk/src/couchdb/couch_httpd.erl

Author: fdmanana
Date: Sun Jul 10 12:22:04 2011
New Revision: 1144848

URL: http://svn.apache.org/viewvc?rev=1144848&view=rev
Log:
Cheaper request authentication

Parsing the auth handlers on every request is more expensive then
it needs to be. Just the regexp split operation by itself is rather
expensive:

2> Conf = couch_config:get("httpd", "authentication_handlers").
"{couch_httpd_oauth, oauth_authentication_handler}, {couch_httpd_auth, cookie_authentication_handler}, {couch_httpd_auth, default_authentication_handler}"
3>
3> timer:tc(re, split, [Conf, "(?<=})\\s*,\\s*(?={)", [{return, list}]]).
{289,
 ["{couch_httpd_oauth, oauth_authentication_handler}",
  "{couch_httpd_auth, cookie_authentication_handler}",
  "{couch_httpd_auth, default_authentication_handler}"]}
4> timer:tc(re, split, [Conf, "(?<=})\\s*,\\s*(?={)", [{return, list}]]).
{292,
 ["{couch_httpd_oauth, oauth_authentication_handler}",
  "{couch_httpd_auth, cookie_authentication_handler}",
  "{couch_httpd_auth, default_authentication_handler}"]}
5> timer:tc(re, split, [Conf, "(?<=})\\s*,\\s*(?={)", [{return, list}]]).
{297,
 ["{couch_httpd_oauth, oauth_authentication_handler}",
  "{couch_httpd_auth, cookie_authentication_handler}",
  "{couch_httpd_auth, default_authentication_handler}"]}
6> timer:tc(re, split, [Conf, "(?<=})\\s*,\\s*(?={)", [{return, list}]]).

And so is the parsing of each auth handler function:

1> timer:tc(couch_httpd, make_arity_1_fun, ["{couch_httpd_auth, default_authentication_handler}"]).
{63,#Fun<couch_httpd.6.41794127>}
2> timer:tc(couch_httpd, make_arity_1_fun, ["{couch_httpd_auth, default_authentication_handler}"]).
{58,#Fun<couch_httpd.6.41794127>}
3> timer:tc(couch_httpd, make_arity_1_fun, ["{couch_httpd_auth, default_authentication_handler}"]).
{56,#Fun<couch_httpd.6.41794127>}
4> timer:tc(couch_httpd, make_arity_1_fun, ["{couch_httpd_auth, default_authentication_handler}"]).
{58,#Fun<couch_httpd.6.41794127>}
5> timer:tc(couch_httpd, make_arity_1_fun, ["{couch_httpd_auth, default_authentication_handler}"]).
{59,#Fun<couch_httpd.6.41794127>}

Getting the preprocessed auth handler functions from the application
environment is however much cheaper:

6> timer:tc(application, get_env, [couch, auth_handlers]).
{13,
 {ok,[{#Fun<couch_httpd.6.41794127>,
       <<"{couch_httpd_oauth, oauth_authentication_handler}">>},
      {#Fun<couch_httpd.6.41794127>,
       <<"{couch_httpd_auth, cookie_authentication_handler}">>},
      {#Fun<couch_httpd.6.41794127>,
       <<"{couch_httpd_auth, default_authentication_handler}">>}]}}
7> timer:tc(application, get_env, [couch, auth_handlers]).
{14,
 {ok,[{#Fun<couch_httpd.6.41794127>,
       <<"{couch_httpd_oauth, oauth_authentication_handler}">>},
      {#Fun<couch_httpd.6.41794127>,
       <<"{couch_httpd_auth, cookie_authentication_handler}">>},
      {#Fun<couch_httpd.6.41794127>,
       <<"{couch_httpd_auth, default_authentication_handler}">>}]}}
8> timer:tc(application, get_env, [couch, auth_handlers]).
{13,
 {ok,[{#Fun<couch_httpd.6.41794127>,
       <<"{couch_httpd_oauth, oauth_authentication_handler}">>},
      {#Fun<couch_httpd.6.41794127>,
       <<"{couch_httpd_auth, cookie_authentication_handler}">>},
      {#Fun<couch_httpd.6.41794127>,
       <<"{couch_httpd_auth, default_authentication_handler}">>}]}}

The application environment is backed by an ets and created when
the couch OTP application is started.


Modified:
    couchdb/trunk/src/couchdb/couch_httpd.erl

Modified: couchdb/trunk/src/couchdb/couch_httpd.erl
URL: http://svn.apache.org/viewvc/couchdb/trunk/src/couchdb/couch_httpd.erl?rev=1144848&r1=1144847&r2=1144848&view=diff
==============================================================================
--- couchdb/trunk/src/couchdb/couch_httpd.erl (original)
+++ couchdb/trunk/src/couchdb/couch_httpd.erl Sun Jul 10 12:22:04 2011
@@ -124,6 +124,8 @@ start_link(Name, Options) ->
     {ok, SocketOptions} = couch_util:parse_term(
         couch_config:get("httpd", "socket_options", "[]")),
 
+    set_auth_handlers(),
+
     Loop = fun(Req)->
         case SocketOptions of
         [] ->
@@ -168,6 +170,8 @@ config_change("httpd", "server_options")
     ?MODULE:stop();
 config_change("httpd", "socket_options") ->
     ?MODULE:stop();
+config_change("httpd", "authentication_handlers") ->
+    set_auth_handlers();
 config_change("httpd_global_handlers", _) ->
     ?MODULE:stop();
 config_change("httpd_db_handlers", _) ->
@@ -175,6 +179,13 @@ config_change("httpd_db_handlers", _) ->
 config_change("ssl", _) ->
     ?MODULE:stop().
 
+set_auth_handlers() ->
+    AuthenticationSrcs = make_fun_spec_strs(
+        couch_config:get("httpd", "authentication_handlers", "")),
+    AuthHandlers = lists:map(
+        fun(A) -> {make_arity_1_fun(A), ?l2b(A)} end, AuthenticationSrcs),
+    ok = application:set_env(couch, auth_handlers, AuthHandlers).
+
 % SpecStr is a string like "{my_module, my_fun}"
 %  or "{my_module, my_fun, <<"my_arg">>}"
 make_arity_1_fun(SpecStr) ->
@@ -216,8 +227,6 @@ handle_request(MochiReq, DefaultFun, Url
 handle_request_int(MochiReq, DefaultFun,
             UrlHandlers, DbUrlHandlers, DesignUrlHandlers) ->
     Begin = now(),
-    AuthenticationSrcs = make_fun_spec_strs(
-            couch_config:get("httpd", "authentication_handlers")),
     % for the path, use the raw path with the query string and fragment
     % removed, but URL quoting left intact
     RawUri = MochiReq:get(raw_path),
@@ -293,10 +302,11 @@ handle_request_int(MochiReq, DefaultFun,
     },
 
     HandlerFun = couch_util:dict_find(HandlerKey, UrlHandlers, DefaultFun),
+    {ok, AuthHandlers} = application:get_env(couch, auth_handlers),
 
     {ok, Resp} =
     try
-        case authenticate_request(HttpReq, AuthenticationSrcs) of
+        case authenticate_request(HttpReq, AuthHandlers) of
         #httpd{} = Req ->
             HandlerFun(Req);
         Response ->
@@ -347,7 +357,7 @@ handle_request_int(MochiReq, DefaultFun,
 % Try authentication handlers in order until one sets a user_ctx
 % the auth funs also have the option of returning a response
 % move this to couch_httpd_auth?
-authenticate_request(#httpd{user_ctx=#user_ctx{}} = Req, _AuthSrcs) ->
+authenticate_request(#httpd{user_ctx=#user_ctx{}} = Req, _AuthHandlers) ->
     Req;
 authenticate_request(#httpd{} = Req, []) ->
     case couch_config:get("couch_httpd_auth", "require_valid_user", "false") of
@@ -356,14 +366,13 @@ authenticate_request(#httpd{} = Req, [])
     "false" ->
         Req#httpd{user_ctx=#user_ctx{}}
     end;
-authenticate_request(#httpd{} = Req, [AuthSrc|Rest]) ->
-    AuthFun = make_arity_1_fun(AuthSrc),
+authenticate_request(#httpd{} = Req, [{AuthFun, AuthSrc} | RestAuthHandlers]) ->
     R = case AuthFun(Req) of
         #httpd{user_ctx=#user_ctx{}=UserCtx}=Req2 ->
-            Req2#httpd{user_ctx=UserCtx#user_ctx{handler=?l2b(AuthSrc)}};
+            Req2#httpd{user_ctx=UserCtx#user_ctx{handler=AuthSrc}};
         Else -> Else
     end,
-    authenticate_request(R, Rest);
+    authenticate_request(R, RestAuthHandlers);
 authenticate_request(Response, _AuthSrcs) ->
     Response.