Source

ehttp / src / ehttp.erl

Full commit
%%%-------------------------------------------------------------------
%%% @author Torbjorn Tornkvist <tobbe@tornkvist.org>
%%% @copyright (C) 2010, Torbjorn Tornkvist
%%% @doc Repository browser.
%%%
%%% Browsing a hg repo with ehttp is pointless when there is 'hg serv'.
%%% This code is just a reason for playing around with the inets/httpd.
%%%
%%% @end
%%%-------------------------------------------------------------------
-module(ehttp).
-export([start/0, stop/0, do/1]).
-include("httpd.hrl").

-define(DEFAULT_PORT, 8081).

%-define(dbg(Fs,As), true).
-define(dbg(Fs,As), io:format("~p(~p): "++Fs, [?MODULE,?LINE|As])).

start() ->
    inets_start().

stop() ->
    RegName = list_to_atom("httpd_instance_sup_"++
                           integer_to_list(?DEFAULT_PORT)),
    inets_stop(whereis(RegName)).
   
inets_start() ->
    inets:start(),
    {ok, CWD} = file:get_cwd(),
    Hgweb_cgi_path = setup_hgweb_cgi(CWD, "ehttp"),
    {ok, Pid} = 
        inets:start(httpd, 
                    [{port, ?DEFAULT_PORT},
                     {server_name, hostname()},
                     {server_root, CWD},
                     {document_root, CWD},
                     {alias, {"/icons", inets_icons_dir()}},
                     {script_alias, {"/hgweb.cgi", Hgweb_cgi_path}},
                     {modules, [?MODULE, mod_cgi, mod_alias, 
                                mod_dir, mod_get, mod_head]}
                    ]),
    link(Pid),
    {ok, Pid}.

hostname() ->
    {ok,Host} = inet:gethostname(),
    Host.

inets_icons_dir() ->
     [$/|filename:join(
           lists:reverse(
             ["icons","server_root","examples"
              |tl(tl(lists:reverse(
                       string:tokens(
                         code:which(httpd),"/"))))]))].

inets_stop(Pid) ->
    inets:stop(httpd, Pid).

do(Info) ->
    try dodo(Info)
    catch _:Err -> io:format("Error(~p): ~p~n", 
                             [Err, erlang:get_stacktrace()]) 
    end.

dodo(Info) ->
    case proplists:get_value(status, Info#mod.data) of
        %% A status code has been generated!
        {_StatusCode, _PhraseArgs, _Reason} ->
            {proceed,Info#mod.data};
        %% No status code has been generated!
        undefined ->
            case proplists:get_value(response, Info#mod.data) of
                %% No response has been generated!
                undefined ->
                    Path = Info#mod.request_uri,
                    case re:run(Path, "hgweb.cgi", []) of
                        {match,_} -> maybe_hgweb_rewrite(Info, Path);
                        _         -> {proceed, Info#mod.data} 
                    end
            end;
	%% A response has been generated or sent!
	_Response ->
            {proceed, Info#mod.data}
    end.

maybe_hgweb_rewrite(Info, Path) ->
    case hgweb_rewrite(Path) of
        Path ->
            {proceed, Info#mod.data};
        NewPath ->
            ServerName = httpd_util:lookup(Info#mod.config_db, server_name),
            URL = "http://" ++ ServerName ++ ":" ++ i2l(?DEFAULT_PORT) ++ NewPath,
            ReasonPhrase = httpd_util:reason_phrase(301),
            Message = httpd_util:message(301, URL, Info#mod.config_db),
            {proceed,
             [{response, {301, 
                          ["Location: ", URL, "\r\n",
                           "Content-Type: text/html\r\n",
                           "\r\n",
                           "<HTML>\n<HEAD>\n<TITLE>",ReasonPhrase,
                           "</TITLE>\n</HEAD>\n"
                           "<BODY>\n<H1>",ReasonPhrase,
                           "</H1>\n", Message, 
                           "\n</BODY>\n</HTML>\n"]}}
              | Info#mod.data]}
    end.
	
hgweb_rewrite(Path) ->
    hgweb_rewrite(Path,
                  [fun  hg_static_rewrite/1
                   ,fun hg_branches_rewrite/1
                   ,fun hg_graph_rewrite/1
                   ,fun hg_tags_rewrite/1
                   ,fun hg_shortlog_rewrite/1
                   ,fun hg_rev_rewrite/1
                   ,fun hg_file_rewrite/1
                  ]).

hgweb_rewrite(Path, [])     -> Path;
hgweb_rewrite(Path, [F|Fs]) ->
    case F(Path) of
        Path    -> hgweb_rewrite(Path, Fs);
        NewPath -> NewPath
    end.

hg_static_rewrite(Path) ->
    r(Path, "(.*hgweb.cgi)(.*)(/static.*)", "\\1\\3").

hg_branches_rewrite(Path) ->
    r(Path, "(.*hgweb.cgi)(.*)(/branches.*)", "\\1\\3").

hg_graph_rewrite(Path) ->
    r(Path, "(.*hgweb.cgi)(.*)(/graph.*)", "\\1\\3").

hg_tags_rewrite(Path) ->
    r(Path, "(.*hgweb.cgi)(.*)(/tags.*)", "\\1\\3").

hg_shortlog_rewrite(Path) ->
    r(Path, "(.*hgweb.cgi)(.*)(/shortlog)(.*)", "\\1\\4").

hg_rev_rewrite(Path) ->
    r(Path, "(.*hgweb.cgi)(.*)(/rev.*)", "\\1\\3").

hg_file_rewrite(Path) ->
    r(Path, "(.*hgweb.cgi)(.*)(/file.*)", "\\1\\3").

r(Path, RegExp, Return) ->
    re:replace(Path, RegExp, Return, [{return, list}, global]).

i2l(I) when is_integer(I) -> integer_to_list(I).

setup_hgweb_cgi(Root, Name) ->
    TmpDir = "/tmp/ehttp",
    os:cmd("mkdir -p "++TmpDir),
    Hgweb_cgi_path = TmpDir++"/hgweb.cgi",
    file:write_file(Hgweb_cgi_path,
                    list_to_binary(hgweb_cgi(Root, Name))),
    os:cmd("chmod +x "++Hgweb_cgi_path),
    Hgweb_cgi_path.
    
hgweb_cgi(Root, Name) ->
    ["#!/usr/bin/env python\n"
     "from mercurial import demandimport; demandimport.enable()\n"
     "from mercurial.hgweb.hgweb_mod import hgweb\n"
     "import mercurial.hgweb.wsgicgi as wsgicgi\n"
     "application = hgweb(\"",Root,"\", \"",Name,"\")\n"
     "wsgicgi.launch(application)\n"].