summaryrefslogtreecommitdiff
path: root/src/couch_inets/mod_cgi.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_cgi.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_cgi.erl')
-rw-r--r--src/couch_inets/mod_cgi.erl331
1 files changed, 0 insertions, 331 deletions
diff --git a/src/couch_inets/mod_cgi.erl b/src/couch_inets/mod_cgi.erl
deleted file mode 100644
index 0e682d84..00000000
--- a/src/couch_inets/mod_cgi.erl
+++ /dev/null
@@ -1,331 +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$
-%%
-%% Implements The WWW Common Gateway Interface Version 1.1
-
--module(mod_cgi).
-
--export([env/3]).
-
-%%% Callback API
--export([do/1, load/2]).
-
--include("http_internal.hrl").
--include("httpd.hrl").
-
-%% We will not make the change to use base64 in stdlib in inets just yet.
-%% it will be included in the next major release of inets.
--compile({nowarn_deprecated_function, {http_base_64, encode, 1}}).
-
--define(VMODULE,"CGI").
-
--define(DEFAULT_CGI_TIMEOUT, 15000).
-
-%%%=========================================================================
-%%% API
-%%%=========================================================================
-%%--------------------------------------------------------------------------
-%% do(ModData, _, AfterScript) -> [{EnvVariable, Value}]
-%%
-%% AfterScript = string()
-%% ModData = #mod{}
-%% EnvVariable = string()
-%% Value = term()
-%% Description: Keep for now as it is documented in the man page
-%%-------------------------------------------------------------------------
-env(ModData, _Script, AfterScript) ->
- ScriptElements = script_elements(ModData, AfterScript),
- httpd_script_env:create_env(cgi, ModData, ScriptElements).
-
-%%%=========================================================================
-%%% 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
- %% A status code has been generated!
- {_StatusCode, _PhraseArgs, _Reason} ->
- {proceed, ModData#mod.data};
- %% No status code has been generated!
- 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
-%%-------------------------------------------------------------------------
-
-%% ScriptNoCache true|false, defines whether the server shall add
-%% header fields to stop proxies and
-%% clients from saving the page in history
-%% or cache
-%%
-load("ScriptNoCache " ++ CacheArg, [])->
- case catch list_to_atom(httpd_conf:clean(CacheArg)) of
- true ->
- {ok, [], {script_nocache, true}};
- false ->
- {ok, [], {script_nocache, false}};
- _ ->
- {error, ?NICE(httpd_conf:clean(CacheArg)++
- " is an invalid ScriptNoCache directive")}
- end;
-%% ScriptTimeout Seconds, The number of seconds that the server
-%% maximum will wait for the script to
-%% generate a part of the document
-load("ScriptTimeout " ++ Timeout, [])->
- case catch list_to_integer(httpd_conf:clean(Timeout)) of
- TimeoutSec when integer(TimeoutSec) ->
- {ok, [], {script_timeout,TimeoutSec*1000}};
- _ ->
- {error, ?NICE(httpd_conf:clean(Timeout)++
- " is an invalid ScriptTimeout")}
- end.
-
-%%%========================================================================
-%%% Internal functions
-%%%========================================================================
-generate_response(ModData) ->
- RequestURI =
- case httpd_util:key1search(ModData#mod.data, new_request_uri) of
- undefined ->
- ModData#mod.request_uri;
- Value ->
- Value
- end,
- ScriptAliases =
- httpd_util:multi_lookup(ModData#mod.config_db, script_alias),
- case mod_alias:real_script_name(ModData#mod.config_db, RequestURI,
- ScriptAliases) of
- {Script, AfterScript} ->
- exec_script(ModData, Script, AfterScript,
- RequestURI);
- not_a_script ->
- {proceed, ModData#mod.data}
- end.
-
-is_executable(File) ->
- Dir = filename:dirname(File),
- FileName = filename:basename(File),
- case os:type() of
- {win32,_} ->
- %% temporary (hopefully) fix for win32 OTP-3627
- is_win32_executable(Dir,FileName);
- _ ->
- is_executable(Dir, FileName)
- end.
-
-is_executable(Dir, FilName) ->
- case os:find_executable(FilName, Dir) of
- false ->
- false;
- _ ->
- true
- end.
-
-%% Start temporary (hopefully) fix for win32 OTP-3627
-%% ---------------------------------
-is_win32_executable(Dir, FileName) ->
- NewFileName = strip_extention(FileName, [".bat",".exe",".com", ".cmd"]),
- is_executable(Dir, NewFileName).
-
-strip_extention(FileName, []) ->
- FileName;
-strip_extention(FileName, [Extention | Extentions]) ->
- case filename:basename(FileName, Extention) of
- FileName ->
- strip_extention(FileName, Extentions);
- NewFileName ->
- NewFileName
- end.
-
-%% End fix
-%% ---------------------------------
-
-exec_script(ModData, Script, AfterScript, RequestURI) ->
- exec_script(is_executable(Script), ModData, Script,
- AfterScript, RequestURI).
-
-exec_script(true, ModData, Script, AfterScript, _RequestURI) ->
- process_flag(trap_exit,true),
- Dir = filename:dirname(Script),
- ScriptElements = script_elements(ModData, AfterScript),
- Env = (catch httpd_script_env:create_env(cgi, ModData, ScriptElements)),
-
- %% Run script
- Port = (catch open_port({spawn, Script},[binary, stream,
- {cd, Dir}, {env, Env}])),
- case Port of
- Port when is_port(Port) ->
- send_request_body_to_script(ModData, Port),
- deliver_webpage(ModData, Port); % Take care of script output
- Error ->
- exit({open_port_failed, Error,
- [{mod,?MODULE},
- {uri,ModData#mod.request_uri}, {script,Script},
- {env,Env},{dir,Dir}]})
- end;
-
-exec_script(false, ModData, _Script, _AfterScript, _RequestURI) ->
- {proceed,
- [{status,
- {404,ModData#mod.request_uri,
- ?NICE("You don't have permission to execute " ++
- ModData#mod.request_uri ++ " on this server")}}|
- ModData#mod.data]}.
-
-send_request_body_to_script(ModData, Port) ->
- case ModData#mod.entity_body of
- [] ->
- ok;
- EntityBody ->
- port_command(Port, EntityBody)
- end.
-
-deliver_webpage(#mod{config_db = Db} = ModData, Port) ->
- Timeout = cgi_timeout(Db),
- case receive_headers(Port, httpd_cgi, parse_headers,
- [<<>>, [], []], Timeout) of
- {Headers, Body} ->
- case httpd_cgi:handle_headers(Headers) of
- {proceed, AbsPath} ->
- {proceed, [{real_name,
- httpd_util:split_path(AbsPath)} |
- ModData#mod.data]};
- {ok, HTTPHeaders, Status} ->
- IsDisableChunkedSend =
- httpd_response:is_disable_chunked_send(Db),
- case (ModData#mod.http_version =/= "HTTP/1.1") or
- (IsDisableChunkedSend) of
- true ->
- send_headers(ModData, Status,
- [{"connection", "close"}
- | HTTPHeaders]);
- false ->
- send_headers(ModData, Status,
- [{"transfer-encoding",
- "chunked"} | HTTPHeaders])
- end,
- handle_body(Port, ModData, Body, Timeout, size(Body),
- IsDisableChunkedSend)
- end;
- {'EXIT', Port, Reason} ->
- process_flag(trap_exit, false),
- {proceed, [{status, {400, none, reason(Reason)}} |
- ModData#mod.data]};
- timeout ->
- (catch port_close(Port)), % KILL the port !!!!
- send_headers(ModData, {504, "Timeout"}, []),
- 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(Port, Module, Function, Args, Timeout) ->
- receive
- {Port, {data, Response}} when is_port(Port) ->
- case Module:Function([Response | Args]) of
- {NewModule, NewFunction, NewArgs} ->
- receive_headers(Port, NewModule,
- NewFunction, NewArgs, Timeout);
- {ok, {Headers, Body}} ->
- {Headers, Body}
- end;
- {'EXIT', Port, Reason} when is_port(Port) ->
- {'EXIT', Port, Reason};
- {'EXIT', Pid, Reason} when is_pid(Pid) ->
- exit({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(Port, #mod{method = "HEAD"} = ModData, _, _, Size, _) ->
- (catch port_close(Port)), % KILL the port !!!!
- process_flag(trap_exit,false),
- {proceed, [{response, {already_sent, 200, Size}} | ModData#mod.data]};
-
-handle_body(Port, ModData, Body, Timeout, Size, IsDisableChunkedSend) ->
- httpd_response:send_chunk(ModData, Body, IsDisableChunkedSend),
- receive
- {Port, {data, Data}} when port(Port) ->
- handle_body(Port, ModData, Data, Timeout, Size + size(Data),
- IsDisableChunkedSend);
- {'EXIT', Port, normal} when is_port(Port) ->
- httpd_response:send_final_chunk(ModData, IsDisableChunkedSend),
- process_flag(trap_exit,false),
- {proceed, [{response, {already_sent, 200, Size}} |
- ModData#mod.data]};
- {'EXIT', Port, Reason} when is_port(Port) ->
- process_flag(trap_exit, false),
- {proceed, [{status, {400, none, reason(Reason)}} |
- ModData#mod.data]};
- {'EXIT', Pid, Reason} when is_pid(Pid) ->
- exit({mod_cgi_linked_process_died, Pid, Reason})
- after Timeout ->
- (catch port_close(Port)), % KILL the port !!!!
- process_flag(trap_exit,false),
- {proceed,[{response, {already_sent, 200, Size}} |
- ModData#mod.data]}
- end.
-
-script_elements(#mod{method = "GET"}, {[], QueryString}) ->
- [{query_string, QueryString}];
-script_elements(#mod{method = "GET"}, {PathInfo, []}) ->
- [{path_info, PathInfo}];
-script_elements(#mod{method = "GET"}, {PathInfo, QueryString}) ->
- [{query_string, QueryString}, {path_info, PathInfo}];
-script_elements(#mod{method = "POST", entity_body = Body}, _) ->
- [{entity_body, Body}];
-script_elements(_, _) ->
- [].
-
-cgi_timeout(Db) ->
- httpd_util:lookup(Db, cgi_timeout, ?DEFAULT_CGI_TIMEOUT).
-
-%% Convert error to printable string
-%%
-reason({error,emfile}) -> ": To many open files";
-reason({error,{enfile,_}}) -> ": File/port table overflow";
-reason({error,enomem}) -> ": Not enough memory";
-reason({error,eagain}) -> ": No more available OS processes";
-reason(Reason) -> lists:flatten(io_lib:format("Reason: ~p~n", [Reason])).