summaryrefslogtreecommitdiff
path: root/src/couch_inets/mod_esi.erl
diff options
context:
space:
mode:
authorChristopher Lenz <cmlenz@apache.org>2008-04-15 12:21:00 +0000
committerChristopher Lenz <cmlenz@apache.org>2008-04-15 12:21:00 +0000
commit39de3072bcf9fdeec6d3faeb125924c401242205 (patch)
treeda55307c8762f9ff16f7a7e478d971c0f352d281 /src/couch_inets/mod_esi.erl
parent53968ddfd93bfe1aa403478de715ae0ac77db177 (diff)
Merged mochiweb branch back into trunk.
git-svn-id: https://svn.apache.org/repos/asf/incubator/couchdb/trunk@648222 13f79535-47bb-0310-9956-ffa450edef68
Diffstat (limited to 'src/couch_inets/mod_esi.erl')
-rw-r--r--src/couch_inets/mod_esi.erl432
1 files changed, 0 insertions, 432 deletions
diff --git a/src/couch_inets/mod_esi.erl b/src/couch_inets/mod_esi.erl
deleted file mode 100644
index f0e8ae80..00000000
--- a/src/couch_inets/mod_esi.erl
+++ /dev/null
@@ -1,432 +0,0 @@
-%% ``The contents of this file are subject to the Erlang Public License,
-%% Version 1.1, (the "License"); you may not use this file except in
-%% compliance with the License. You should have received a copy of the
-%% Erlang Public License along with this software. If not, it can be
-%% retrieved via the world wide web at http://www.erlang.org/.
-%%
-%% Software distributed under the License is distributed on an "AS IS"
-%% basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See
-%% the License for the specific language governing rights and limitations
-%% under the License.
-%%
-%% The Initial Developer of the Original Code is Ericsson Utvecklings AB.
-%% Portions created by Ericsson are Copyright 1999, Ericsson Utvecklings
-%% AB. All Rights Reserved.''
-%%
-%% $Id$
-%%
--module(mod_esi).
-
-%% API
-%% Functions provided to help erl scheme alias programmer to
-%% Create dynamic webpages that are sent back to the user during
-%% Generation
--export([deliver/2]).
-
-%% Callback API
--export([do/1, load/2]).
-
--include("httpd.hrl").
-
--define(VMODULE,"ESI").
--define(DEFAULT_ERL_TIMEOUT,15000).
-
-%%%=========================================================================
-%%% API
-%%%=========================================================================
-%%--------------------------------------------------------------------------
-%% deliver(SessionID, Data) -> ok | {error, bad_sessionID}
-%% SessionID = pid()
-%% Data = string() | io_list() (first call must send a string that
-%% contains all header information including "\r\n\r\n", unless there
-%% is no header information at all.)
-%%
-%% Description: Send <Data> (Html page generated sofar) to the server
-%% request handling process so it can forward it to the client.
-%%-------------------------------------------------------------------------
-deliver(SessionID, Data) when pid(SessionID) ->
- SessionID ! {ok, Data},
- ok;
-deliver(_SessionID, _Data) ->
- {error, bad_sessionID}.
-
-%%%=========================================================================
-%%% CALLBACK API
-%%%=========================================================================
-%%--------------------------------------------------------------------------
-%% do(ModData) -> {proceed, OldData} | {proceed, NewData} | {break, NewData}
-%% | done
-%% ModData = #mod{}
-%%
-%% Description: See httpd(3) ESWAPI CALLBACK FUNCTIONS
-%%-------------------------------------------------------------------------
-do(ModData) ->
- case httpd_util:key1search(ModData#mod.data, status) of
- {_StatusCode, _PhraseArgs, _Reason} ->
- {proceed, ModData#mod.data};
- undefined ->
- case httpd_util:key1search(ModData#mod.data, response) of
- undefined ->
- generate_response(ModData);
- _Response ->
- {proceed, ModData#mod.data}
- end
- end.
-%%--------------------------------------------------------------------------
-%% load(Line, Context) -> eof | ok | {ok, NewContext} |
-%% {ok, NewContext, Directive} |
-%% {ok, NewContext, DirectiveList} | {error, Reason}
-%% Line = string()
-%% Context = NewContext = DirectiveList = [Directive]
-%% Directive = {DirectiveKey , DirectiveValue}
-%% DirectiveKey = DirectiveValue = term()
-%% Reason = term()
-%%
-%% Description: See httpd(3) ESWAPI CALLBACK FUNCTIONS
-%%-------------------------------------------------------------------------
-load("ErlScriptAlias " ++ ErlScriptAlias, []) ->
- case regexp:split(ErlScriptAlias," ") of
- {ok, [ErlName | Modules]} ->
- {ok, [], {erl_script_alias, {ErlName,Modules}}};
- {ok, _} ->
- {error, ?NICE(httpd_conf:clean(ErlScriptAlias) ++
- " is an invalid ErlScriptAlias")}
- end;
-load("EvalScriptAlias " ++ EvalScriptAlias, []) ->
- case regexp:split(EvalScriptAlias, " ") of
- {ok, [EvalName|Modules]} ->
- {ok, [], {eval_script_alias, {EvalName, Modules}}};
- {ok, _} ->
- {error, ?NICE(httpd_conf:clean(EvalScriptAlias) ++
- " is an invalid EvalScriptAlias")}
- end;
-load("ErlScriptTimeout " ++ Timeout, [])->
- case catch list_to_integer(httpd_conf:clean(Timeout)) of
- TimeoutSec when integer(TimeoutSec) ->
- {ok, [], {erl_script_timeout, TimeoutSec * 1000}};
- _ ->
- {error, ?NICE(httpd_conf:clean(Timeout) ++
- " is an invalid ErlScriptTimeout")}
- end;
-load("ErlScriptNoCache " ++ CacheArg, [])->
- case catch list_to_atom(httpd_conf:clean(CacheArg)) of
- true ->
- {ok, [], {erl_script_nocache, true}};
- false ->
- {ok, [], {erl_script_nocache, false}};
- _ ->
- {error, ?NICE(httpd_conf:clean(CacheArg)++
- " is an invalid ErlScriptNoCache directive")}
- end.
-
-%%%========================================================================
-%%% Internal functions
-%%%========================================================================
-generate_response(ModData) ->
- case scheme(ModData#mod.request_uri, ModData#mod.config_db) of
- {eval, ESIBody, Modules} ->
- eval(ModData, ESIBody, Modules);
- {erl, ESIBody, Modules} ->
- erl(ModData, ESIBody, Modules);
- no_scheme ->
- {proceed, ModData#mod.data}
- end.
-
-scheme(RequestURI, ConfigDB) ->
- case match_script(RequestURI, ConfigDB, erl_script_alias) of
- no_match ->
- case match_script(RequestURI, ConfigDB, eval_script_alias) of
- no_match ->
- no_scheme;
- {EsiBody, ScriptModules} ->
- {eval, EsiBody, ScriptModules}
- end;
- {EsiBody, ScriptModules} ->
- {erl, EsiBody, ScriptModules}
- end.
-
-match_script(RequestURI, ConfigDB, AliasType) ->
- case httpd_util:multi_lookup(ConfigDB, AliasType) of
- [] ->
- no_match;
- AliasAndMods ->
- match_esi_script(RequestURI, AliasAndMods, AliasType)
- end.
-
-match_esi_script(_, [], _) ->
- no_match;
-match_esi_script(RequestURI, [{Alias,Modules} | Rest], AliasType) ->
- AliasMatchStr = alias_match_str(Alias, AliasType),
- case regexp:first_match(RequestURI, AliasMatchStr) of
- {match, 1, Length} ->
- {string:substr(RequestURI, Length + 1), Modules};
- nomatch ->
- match_esi_script(RequestURI, Rest, AliasType)
- end.
-
-alias_match_str(Alias, erl_script_alias) ->
- "^" ++ Alias ++ "/";
-alias_match_str(Alias, eval_script_alias) ->
- "^" ++ Alias ++ "\\?".
-
-
-%%------------------------ Erl mechanism --------------------------------
-
-erl(#mod{method = Method} = ModData, ESIBody, Modules)
- when Method == "GET"; Method == "HEAD"->
- case httpd_util:split(ESIBody,":|%3A|/",2) of
- {ok, [Module, FuncAndInput]} ->
- case httpd_util:split(FuncAndInput,"[\?/]",2) of
- {ok, [FunctionName, Input]} ->
- generate_webpage(ModData, ESIBody, Modules,
- Module, FunctionName, Input,
- script_elements(FunctionName, Input));
- {ok, [FunctionName]} ->
- generate_webpage(ModData, ESIBody, Modules,
- Module, FunctionName, "",
- script_elements(FunctionName, ""));
- {ok, BadRequest} ->
- {proceed,[{status,{400,none, BadRequest}} |
- ModData#mod.data]}
- end;
- {ok, BadRequest} ->
- {proceed, [{status,{400, none, BadRequest}} | ModData#mod.data]}
- end;
-
-erl(#mod{method = "POST", entity_body = Body} = ModData, ESIBody, Modules) ->
- case httpd_util:split(ESIBody,":|%3A|/",2) of
- {ok,[Module, Function]} ->
- generate_webpage(ModData, ESIBody, Modules, Module,
- Function, Body, [{entity_body, Body}]);
- {ok, BadRequest} ->
- {proceed,[{status, {400, none, BadRequest}} | ModData#mod.data]}
- end.
-
-generate_webpage(ModData, ESIBody, ["all"], ModuleName, FunctionName,
- Input, ScriptElements) ->
- generate_webpage(ModData, ESIBody, [ModuleName], ModuleName,
- FunctionName, Input, ScriptElements);
-generate_webpage(ModData, ESIBody, Modules, ModuleName, FunctionName,
- Input, ScriptElements) ->
- case lists:member(ModuleName, Modules) of
- true ->
- Env = httpd_script_env:create_env(esi, ModData, ScriptElements),
- Module = list_to_atom(ModuleName),
- Function = list_to_atom(FunctionName),
- case erl_scheme_webpage_chunk(Module, Function,
- Env, Input, ModData) of
- {error, erl_scheme_webpage_chunk_undefined} ->
- erl_scheme_webpage_whole(Module, Function, Env, Input,
- ModData);
- ResponseResult ->
- ResponseResult
- end;
- false ->
- {proceed, [{status, {403, ModData#mod.request_uri,
- ?NICE("Client not authorized to evaluate: "
- ++ ESIBody)}} | ModData#mod.data]}
- end.
-
-%% Old API that waits for the dymnamic webpage to be totally generated
-%% before anythig is sent back to the client.
-erl_scheme_webpage_whole(Module, Function, Env, Input, ModData) ->
- case (catch Module:Function(Env, Input)) of
- {'EXIT',Reason} ->
- {proceed, [{status, {500, none, Reason}} |
- ModData#mod.data]};
- Response ->
- {Headers, Body} =
- httpd_esi:parse_headers(lists:flatten(Response)),
- Length = httpd_util:flatlength(Body),
- case httpd_esi:handle_headers(Headers) of
- {proceed, AbsPath} ->
- {proceed, [{real_name, httpd_util:split_path(AbsPath)}
- | ModData#mod.data]};
- {ok, NewHeaders, StatusCode} ->
- send_headers(ModData, StatusCode,
- [{"content-length",
- integer_to_list(Length)}| NewHeaders]),
- case ModData#mod.method of
- "HEAD" ->
- {proceed, [{response, {already_sent, 200, 0}} |
- ModData#mod.data]};
- _ ->
- httpd_response:send_body(ModData,
- StatusCode, Body),
- {proceed, [{response, {already_sent, 200,
- Length}} |
- ModData#mod.data]}
- end
- end
- end.
-
-%% New API that allows the dynamic wepage to be sent back to the client
-%% in small chunks at the time during generation.
-erl_scheme_webpage_chunk(Mod, Func, Env, Input, ModData) ->
- process_flag(trap_exit, true),
- Self = self(),
- %% Spawn worker that generates the webpage.
- %% It would be nicer to use erlang:function_exported/3 but if the
- %% Module isn't loaded the function says that it is not loaded
- Pid = spawn_link(
- fun() ->
- case catch Mod:Func(Self, Env, Input) of
- {'EXIT',{undef,_}} ->
- %% Will force fallback on the old API
- exit(erl_scheme_webpage_chunk_undefined);
- _ ->
- ok
- end
- end),
-
- Response = deliver_webpage_chunk(ModData, Pid),
-
- process_flag(trap_exit,false),
- Response.
-
-deliver_webpage_chunk(#mod{config_db = Db} = ModData, Pid) ->
- Timeout = erl_script_timeout(Db),
- deliver_webpage_chunk(ModData, Pid, Timeout).
-
-deliver_webpage_chunk(#mod{config_db = Db} = ModData, Pid, Timeout) ->
- case receive_headers(Timeout) of
- {error, Reason} ->
- %% Happens when webpage generator callback/3 is undefined
- {error, Reason};
- {Headers, Body} ->
- case httpd_esi:handle_headers(Headers) of
- {proceed, AbsPath} ->
- {proceed, [{real_name, httpd_util:split_path(AbsPath)}
- | ModData#mod.data]};
- {ok, NewHeaders, StatusCode} ->
- IsDisableChunkedSend =
- httpd_response:is_disable_chunked_send(Db),
- case (ModData#mod.http_version =/= "HTTP/1.1") or
- (IsDisableChunkedSend) of
- true ->
- send_headers(ModData, StatusCode,
- [{"connection", "close"} |
- NewHeaders]);
- false ->
- send_headers(ModData, StatusCode,
- [{"transfer-encoding",
- "chunked"} | NewHeaders])
- end,
- handle_body(Pid, ModData, Body, Timeout, length(Body),
- IsDisableChunkedSend)
- end;
- timeout ->
- send_headers(ModData, {504, "Timeout"},[{"connection", "close"}]),
- httpd_socket:close(ModData#mod.socket_type, ModData#mod.socket),
- process_flag(trap_exit,false),
- {proceed,[{response, {already_sent, 200, 0}} | ModData#mod.data]}
- end.
-
-receive_headers(Timeout) ->
- receive
- {ok, Chunk} ->
- httpd_esi:parse_headers(lists:flatten(Chunk));
- {'EXIT', Pid, erl_scheme_webpage_chunk_undefined} when is_pid(Pid) ->
- {error, erl_scheme_webpage_chunk_undefined};
- {'EXIT', Pid, Reason} when is_pid(Pid) ->
- exit({mod_esi_linked_process_died, Pid, Reason})
- after Timeout ->
- timeout
- end.
-
-send_headers(ModData, StatusCode, HTTPHeaders) ->
- ExtraHeaders = httpd_response:cache_headers(ModData),
- httpd_response:send_header(ModData, StatusCode,
- ExtraHeaders ++ HTTPHeaders).
-
-handle_body(_, #mod{method = "HEAD"} = ModData, _, _, Size, _) ->
- process_flag(trap_exit,false),
- {proceed, [{response, {already_sent, 200, Size}} | ModData#mod.data]};
-
-handle_body(Pid, ModData, Body, Timeout, Size, IsDisableChunkedSend) ->
- httpd_response:send_chunk(ModData, Body, IsDisableChunkedSend),
- receive
- {ok, Data} ->
- handle_body(Pid, ModData, Data, Timeout, Size + length(Data),
- IsDisableChunkedSend);
- {'EXIT', Pid, normal} when is_pid(Pid) ->
- httpd_response:send_final_chunk(ModData, IsDisableChunkedSend),
- {proceed, [{response, {already_sent, 200, Size}} |
- ModData#mod.data]};
- {'EXIT', Pid, Reason} when is_pid(Pid) ->
- exit({mod_esi_linked_process_died, Pid, Reason})
- after Timeout ->
- process_flag(trap_exit,false),
- {proceed,[{response, {already_sent, 200, Size}} |
- ModData#mod.data]}
- end.
-
-erl_script_timeout(Db) ->
- httpd_util:lookup(Db, erl_script_timeout, ?DEFAULT_ERL_TIMEOUT).
-
-script_elements(FuncAndInput, Input) ->
- case input_type(FuncAndInput) of
- path_info ->
- [{path_info, Input}];
- query_string ->
- [{query_string, Input}];
- _ ->
- []
- end.
-
-input_type([]) ->
- no_input;
-input_type([$/|_Rest]) ->
- path_info;
-input_type([$?|_Rest]) ->
- query_string;
-input_type([_First|Rest]) ->
- input_type(Rest).
-
-%%------------------------ Eval mechanism --------------------------------
-
-eval(#mod{request_uri = ReqUri, method = "POST",
- http_version = Version, data = Data}, _ESIBody, _Modules) ->
- {proceed,[{status,{501,{"POST", ReqUri, Version},
- ?NICE("Eval mechanism doesn't support method POST")}}|
- Data]};
-
-eval(#mod{method = Method} = ModData, ESIBody, Modules)
- when Method == "GET"; Method == "HEAD" ->
- case is_authorized(ESIBody, Modules) of
- true ->
- case generate_webpage(ESIBody) of
- {error, Reason} ->
- {proceed, [{status, {500, none, Reason}} |
- ModData#mod.data]};
- {ok, Response} ->
- {Headers, _} =
- httpd_esi:parse_headers(lists:flatten(Response)),
- case httpd_esi:handle_headers(Headers) of
- {ok, _, StatusCode} ->
- {proceed,[{response, {StatusCode, Response}} |
- ModData#mod.data]};
- {proceed, AbsPath} ->
- {proceed, [{real_name, AbsPath} |
- ModData#mod.data]}
- end
- end;
- false ->
- {proceed,[{status,
- {403, ModData#mod.request_uri,
- ?NICE("Client not authorized to evaluate: "
- ++ ESIBody)}} | ModData#mod.data]}
- end.
-
-generate_webpage(ESIBody) ->
- (catch lib:eval_str(string:concat(ESIBody,". "))).
-
-is_authorized(_ESIBody, ["all"]) ->
- true;
-is_authorized(ESIBody, Modules) ->
- case regexp:match(ESIBody, "^[^\:(%3A)]*") of
- {match, Start, Length} ->
- lists:member(string:substr(ESIBody, Start, Length), Modules);
- nomatch ->
- false
- end.