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.