diff --git a/web/api/flukso/Emakefile b/web/api/flukso/Emakefile new file mode 100644 index 0000000..7ee92ef --- /dev/null +++ b/web/api/flukso/Emakefile @@ -0,0 +1,6 @@ +% -*- mode: erlang -*- +{["src/*"], + [{i, "include"}, + {outdir, "ebin"}, + debug_info] +}. diff --git a/web/api/flukso/Makefile b/web/api/flukso/Makefile new file mode 100644 index 0000000..361dbd2 --- /dev/null +++ b/web/api/flukso/Makefile @@ -0,0 +1,23 @@ +ERL ?= erl +EBIN_DIRS := $(wildcard deps/*/ebin) +APP := flukso + +all: erl ebin/$(APP).app erlrrd + +erl: + @$(ERL) -pa $(EBIN_DIRS) -noinput +B \ + -eval 'case make:all() of up_to_date -> halt(0); error -> halt(1) end.' + +docs: + @erl -noshell -run edoc_run application '$(APP)' '"."' '[]' + +clean: + @echo "removing:" + @rm -fv ebin/*.beam ebin/*.app + +ebin/$(APP).app: src/$(APP).app + @cp -v src/$(APP).app $@ + +erlrrd: + @(cd deps/erlrrd;$(MAKE)) + diff --git a/web/api/flukso/deps/erlrrd/AUTHORS b/web/api/flukso/deps/erlrrd/AUTHORS new file mode 100644 index 0000000..b04776e --- /dev/null +++ b/web/api/flukso/deps/erlrrd/AUTHORS @@ -0,0 +1 @@ +fess diff --git a/web/api/flukso/deps/erlrrd/ChangeLog b/web/api/flukso/deps/erlrrd/ChangeLog new file mode 100644 index 0000000..3c0e605 --- /dev/null +++ b/web/api/flukso/deps/erlrrd/ChangeLog @@ -0,0 +1,10 @@ +Version 0.3.0 + * enable a timeout env_var for setting the port timeout + * change app spec to use a port time out of 30s. + +Version 0.2.2 + * catch port exit and exit the gen_server + +VERSION 0.2.1 + * rebuild with latest framewerk to get erlrc hooks + * specify dependency on rrdtool diff --git a/web/api/flukso/deps/erlrrd/Makefile b/web/api/flukso/deps/erlrrd/Makefile new file mode 100644 index 0000000..2f6cace --- /dev/null +++ b/web/api/flukso/deps/erlrrd/Makefile @@ -0,0 +1,11 @@ +all: + (cd src;$(MAKE) all) + +edoc: + (cd src;$(MAKE) edoc) + +test: + (cd src;$(MAKE) test) + +clean: + (cd src;$(MAKE) clean) diff --git a/web/api/flukso/deps/erlrrd/Makefile.am.local b/web/api/flukso/deps/erlrrd/Makefile.am.local new file mode 100644 index 0000000..c6d5812 --- /dev/null +++ b/web/api/flukso/deps/erlrrd/Makefile.am.local @@ -0,0 +1 @@ +# put whatever (auto)make commands here, they will be included from Makefile.am diff --git a/web/api/flukso/deps/erlrrd/NEWS b/web/api/flukso/deps/erlrrd/NEWS new file mode 100644 index 0000000..e69de29 diff --git a/web/api/flukso/deps/erlrrd/README b/web/api/flukso/deps/erlrrd/README new file mode 100644 index 0000000..9912a33 --- /dev/null +++ b/web/api/flukso/deps/erlrrd/README @@ -0,0 +1,9 @@ + +This module provides a gen_server controlling a Port to "rrdtool -" + +rrdtool is a very nice graphing system as well as logger for time series data. +for more info on rrdtool check out: + + http://oss.oetiker.ch/rrdtool/ + +for more info on this, the erlrrd module see the doc directory. diff --git a/web/api/flukso/deps/erlrrd/README.maintainer b/web/api/flukso/deps/erlrrd/README.maintainer new file mode 100644 index 0000000..40e711d --- /dev/null +++ b/web/api/flukso/deps/erlrrd/README.maintainer @@ -0,0 +1,5 @@ + +if you're trying to build this package from the source repository +ie: not from a dist tarball you'll need to understand the "Framewerk +build system", and the usual autoconf, automake, make voodoo. Framewerk +is just more voodoo on top of that to make things "easier". diff --git a/web/api/flukso/deps/erlrrd/bootstrap b/web/api/flukso/deps/erlrrd/bootstrap new file mode 100755 index 0000000..452596b --- /dev/null +++ b/web/api/flukso/deps/erlrrd/bootstrap @@ -0,0 +1,17 @@ +#! /bin/sh + +if test -d fw/bin + then + PATH="`pwd`/fw/bin:$PATH" + export PATH + fi + +fwb=`which fw-bootstrap` + +if test -z "$fwb" + then + echo "bootstrap: fatal: fw-bootstrap not installed or not in PATH" 1>&2 + exit 1 + fi + +"$fwb" --fw_version "0.1.2" --name erlrrd --template erlang --revision svn --svn_project_path https://erlrrd.googlecode.com/svn/trunk/erlrrd "$@" diff --git a/web/api/flukso/deps/erlrrd/configure.ac.local b/web/api/flukso/deps/erlrrd/configure.ac.local new file mode 100644 index 0000000..e2d3347 --- /dev/null +++ b/web/api/flukso/deps/erlrrd/configure.ac.local @@ -0,0 +1,5 @@ +dnl -- include additional autoconf commands here +dnl -- do not include AC_OUTPUT, this is called for you + + +AC_CONFIG_FILES([doc/overview.edoc]) diff --git a/web/api/flukso/deps/erlrrd/doc/Makefile.am.local b/web/api/flukso/deps/erlrrd/doc/Makefile.am.local new file mode 100644 index 0000000..c6d5812 --- /dev/null +++ b/web/api/flukso/deps/erlrrd/doc/Makefile.am.local @@ -0,0 +1 @@ +# put whatever (auto)make commands here, they will be included from Makefile.am diff --git a/web/api/flukso/deps/erlrrd/doc/overview.edoc.in b/web/api/flukso/deps/erlrrd/doc/overview.edoc.in new file mode 100644 index 0000000..cf0eb75 --- /dev/null +++ b/web/api/flukso/deps/erlrrd/doc/overview.edoc.in @@ -0,0 +1,9 @@ +@author @FW_PACKAGE_MAINTAINER@ +@copyright 2007 +@version @VERSION@ +@title erlrrd - erlang bindings for rrdtool +@doc erlrrd is a gen_server controlling an erlang Port running the command "rrdtool -" + it provides functions for each of the + [http://oss.oetiker.ch/rrdtool/index.en.html rrdtool] + commands with + a bit of error checking to make sure the communication doesn't hang. diff --git a/web/api/flukso/deps/erlrrd/ebin/erlrrd.app b/web/api/flukso/deps/erlrrd/ebin/erlrrd.app new file mode 100644 index 0000000..af306e2 --- /dev/null +++ b/web/api/flukso/deps/erlrrd/ebin/erlrrd.app @@ -0,0 +1,12 @@ +{application, erlrrd, + [{description, "erlang rrdtool port"}, + {vsn, "1.0"}, + {modules, [ + erlrrd, + erlrrd_app, + erlrrd_sup + ]}, + {registered, [erlrrd, erlrrd_sup]}, + {mod, {erlrrd_app, []}}, + {env, []}, + {applications, [kernel, stdlib]}]}. diff --git a/web/api/flukso/deps/erlrrd/ebin/erlrrd.beam b/web/api/flukso/deps/erlrrd/ebin/erlrrd.beam new file mode 100644 index 0000000..f0d148d Binary files /dev/null and b/web/api/flukso/deps/erlrrd/ebin/erlrrd.beam differ diff --git a/web/api/flukso/deps/erlrrd/ebin/erlrrd_app.beam b/web/api/flukso/deps/erlrrd/ebin/erlrrd_app.beam new file mode 100644 index 0000000..bb2207a Binary files /dev/null and b/web/api/flukso/deps/erlrrd/ebin/erlrrd_app.beam differ diff --git a/web/api/flukso/deps/erlrrd/ebin/erlrrd_sup.beam b/web/api/flukso/deps/erlrrd/ebin/erlrrd_sup.beam new file mode 100644 index 0000000..4cb24f6 Binary files /dev/null and b/web/api/flukso/deps/erlrrd/ebin/erlrrd_sup.beam differ diff --git a/web/api/flukso/deps/erlrrd/fw-pkgin/Makefile.am.local b/web/api/flukso/deps/erlrrd/fw-pkgin/Makefile.am.local new file mode 100644 index 0000000..c6d5812 --- /dev/null +++ b/web/api/flukso/deps/erlrrd/fw-pkgin/Makefile.am.local @@ -0,0 +1 @@ +# put whatever (auto)make commands here, they will be included from Makefile.am diff --git a/web/api/flukso/deps/erlrrd/fw-pkgin/config b/web/api/flukso/deps/erlrrd/fw-pkgin/config new file mode 100644 index 0000000..966d761 --- /dev/null +++ b/web/api/flukso/deps/erlrrd/fw-pkgin/config @@ -0,0 +1,30 @@ +# The FW_PACKAGE_MAINTAINER field is populated with the +# environment variable FW_PACKAGE_DEFAULT_MAINTAINER if non-empty + +FW_PACKAGE_NAME="erlrrd" +FW_PACKAGE_VERSION="0.3.0" +FW_PACKAGE_MAINTAINER="fess " +FW_PACKAGE_SHORT_DESCRIPTION="rrdtool binding for erlang. [ via an erlang Port. ]" +FW_PACKAGE_DESCRIPTION="`cat README`" +FW_PACKAGE_ARCHITECTURE_DEPENDENT="0" + +FW_SUBVERSION_TAG_ROOT="https://erlrrd.googlecode.com/svn/tags/" + +# Dependency information. The native syntax corresponds to Debian, +# http://www.debian.org/doc/debian-policy/ch-relationships.html +# Section 7.1 "Syntax of Relationship Fields" +# +# For other packaging systems, the syntax is translated for you. + +FW_PACKAGE_DEPENDS="rrdtool" +FW_PACKAGE_CONFLICTS="" +FW_PACKAGE_PROVIDES="" +FW_PACKAGE_REPLACES="" + +FW_PACKAGE_BUILD_DEPENDS="eunit" +FW_PACKAGE_BUILD_CONFLICTS="" + +FW_ERL_APP_ENVIRONMENT="[ + {rrdtoolcmd, \"rrdtool -\"}, + {timeout, 30000 } + ]" diff --git a/web/api/flukso/deps/erlrrd/fw-pkgin/post-install b/web/api/flukso/deps/erlrrd/fw-pkgin/post-install new file mode 100755 index 0000000..2958228 --- /dev/null +++ b/web/api/flukso/deps/erlrrd/fw-pkgin/post-install @@ -0,0 +1,9 @@ +#! /bin/sh + +#--------------------------------------------------------------------- +# post-install +# +# Executed after the package is installed. +#--------------------------------------------------------------------- + +exit 0 diff --git a/web/api/flukso/deps/erlrrd/fw-pkgin/post-remove b/web/api/flukso/deps/erlrrd/fw-pkgin/post-remove new file mode 100755 index 0000000..6b8e5fc --- /dev/null +++ b/web/api/flukso/deps/erlrrd/fw-pkgin/post-remove @@ -0,0 +1,9 @@ +#! /bin/sh + +#--------------------------------------------------------------------- +# post-remove +# +# Executed after the package is removed. +#--------------------------------------------------------------------- + +exit 0 diff --git a/web/api/flukso/deps/erlrrd/fw-pkgin/pre-install b/web/api/flukso/deps/erlrrd/fw-pkgin/pre-install new file mode 100755 index 0000000..d8d1722 --- /dev/null +++ b/web/api/flukso/deps/erlrrd/fw-pkgin/pre-install @@ -0,0 +1,9 @@ +#! /bin/sh + +#--------------------------------------------------------------------- +# pre-install +# +# Executed before the package is installed. +#--------------------------------------------------------------------- + +exit 0 diff --git a/web/api/flukso/deps/erlrrd/fw-pkgin/pre-remove b/web/api/flukso/deps/erlrrd/fw-pkgin/pre-remove new file mode 100755 index 0000000..5dc7ae1 --- /dev/null +++ b/web/api/flukso/deps/erlrrd/fw-pkgin/pre-remove @@ -0,0 +1,9 @@ +#! /bin/sh + +#--------------------------------------------------------------------- +# pre-remove +# +# Executed before the package is removed. +#--------------------------------------------------------------------- + +exit 0 diff --git a/web/api/flukso/deps/erlrrd/fw-pkgin/start b/web/api/flukso/deps/erlrrd/fw-pkgin/start new file mode 100755 index 0000000..dc16300 --- /dev/null +++ b/web/api/flukso/deps/erlrrd/fw-pkgin/start @@ -0,0 +1,10 @@ +#! /bin/sh + +#--------------------------------------------------------------------- +# start +# +# Executed when the package (service) is started up. +# Not supported by all package formats. +#--------------------------------------------------------------------- + +exit 0 diff --git a/web/api/flukso/deps/erlrrd/fw-pkgin/stop b/web/api/flukso/deps/erlrrd/fw-pkgin/stop new file mode 100755 index 0000000..665484d --- /dev/null +++ b/web/api/flukso/deps/erlrrd/fw-pkgin/stop @@ -0,0 +1,10 @@ +#! /bin/sh + +#--------------------------------------------------------------------- +# start +# +# Executed when the package (service) is shut down. +# Not supported by all package formats. +#--------------------------------------------------------------------- + +exit 0 diff --git a/web/api/flukso/deps/erlrrd/src/Makefile b/web/api/flukso/deps/erlrrd/src/Makefile new file mode 100644 index 0000000..a191c8a --- /dev/null +++ b/web/api/flukso/deps/erlrrd/src/Makefile @@ -0,0 +1,20 @@ +include ../support/include.mk + +APPLICATION=erlrrd +DOC_OPTS={dir,\"../doc\"} + +all: $(EBIN_FILES) + +debug: + $(MAKE) DEBUG=-DDEBUG + +clean: + rm -rf $(EBIN_FILES) + +edoc: + $(ERL) -noshell -pa ../ebin \ + -eval "edoc:application($(APPLICATION), \".\", [$(DOC_OPTS)])" \ + -s init stop + +test: all + $(ERL) -noshell -pa ../ebin -s $(APPLICATION) test -s init stop diff --git a/web/api/flukso/deps/erlrrd/src/Makefile.am.local b/web/api/flukso/deps/erlrrd/src/Makefile.am.local new file mode 100644 index 0000000..7e4cf57 --- /dev/null +++ b/web/api/flukso/deps/erlrrd/src/Makefile.am.local @@ -0,0 +1,16 @@ +# put whatever (auto)make commands here, they will be included from Makefile.am + +DIALYZERFLAGS = -Wno_improper_lists + +dist_erlappsrc_DATA = \ + $(wildcard *.erl) + +dist_erlappinclude_DATA = \ + $(wildcard *.hrl) + +erlappebin_SCRIPTS = \ + @FW_PACKAGE_NAME@.app \ + $(patsubst %.erl, %.beam, $(dist_erlappsrc_DATA)) + +check_DATA = \ + .dialyzer_ok diff --git a/web/api/flukso/deps/erlrrd/src/erlrrd.app b/web/api/flukso/deps/erlrrd/src/erlrrd.app new file mode 100644 index 0000000..af306e2 --- /dev/null +++ b/web/api/flukso/deps/erlrrd/src/erlrrd.app @@ -0,0 +1,12 @@ +{application, erlrrd, + [{description, "erlang rrdtool port"}, + {vsn, "1.0"}, + {modules, [ + erlrrd, + erlrrd_app, + erlrrd_sup + ]}, + {registered, [erlrrd, erlrrd_sup]}, + {mod, {erlrrd_app, []}}, + {env, []}, + {applications, [kernel, stdlib]}]}. diff --git a/web/api/flukso/deps/erlrrd/src/erlrrd.erl b/web/api/flukso/deps/erlrrd/src/erlrrd.erl new file mode 100644 index 0000000..e8e9e96 --- /dev/null +++ b/web/api/flukso/deps/erlrrd/src/erlrrd.erl @@ -0,0 +1,865 @@ +-module(erlrrd). +-include_lib ("eunit/include/eunit.hrl"). + +-export([create/1, update/1, updatev/1, dump/1, restore/1, last/1, + first/1, info/1, fetch/1, tune/1, resize/1, xport/1, + graph/1, lastupdate/1, ls/0, cd/1, mkdir/1, pwd/0 + ]). + +-export([start_link/1, start_link/0]). +-export([start/0]). +-export([stop/0]). +-export([combine/1, c/1]). + +-behaviour(gen_server). +-export([init/1, handle_call/3, handle_cast/2, handle_info/2, + terminate/2, code_change/3]). + +-record( state2, { port, timeout } ). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% Public +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + + +%% @equiv erlrrd_app:start() +start() -> erlrrd_app:start(). +%% @equiv erlrrd_app:stop() +stop() -> erlrrd_app:stop(). + +%% @spec start_link(RRDToolCmd) -> Result +%% RRDToolCmd = string() +%% Result = {ok,Pid} | ignore | {error,Error} +%% Pid = pid() +%% Error = {already_started,Pid} | shutdown | term() +%% @doc calls gen_server:start_link +%% RRDToolCmd is the command passed to open_port() +%% usually "rrdtool -" +start_link(RRDToolCmd) when is_list(RRDToolCmd) -> + application:set_env(erlrrd, rrdtoolcmd, RRDToolCmd ), + start_link(). + +%% @equiv start_link("rrdtool -") +start_link() -> + gen_server:start_link({local, ?MODULE}, ?MODULE, [], []). + +%% @spec combine(List) -> List +%% List = [ term() ] +%% @doc "joins" and quotes the given arg list. +%% takes a list of arguments, and returns a deeplist with +%% each argument surrounded by double quotes +%% then separated by spaces. Note +%% it does not try to escape any double quotes +%% in the arguments. +%% +%% combine(["these", "are", "my args"]). -> +%% +%% [["\"","these","\""]," ",["\"","are","\""]," ",["\"","my args","\""]] +%% +%% it is intended as a convinence function to the +%% rrdtool commands which all take a single iodata() argument +%% which represents the string to be passed as the arguments +%% to the corresponding rrdtool command. +%% +%% erlrrd:xport(erlrrd:c(["DEF:foo=/path with/space/foo.rrd:foo:AVERAGE", "XPORT:foo"])). +combine(Args) -> + join([ [ "\"", X, "\"" ] || X <- Args ], " "). +%% @spec c(List) -> List +%% List = [ term() ] +% @equiv combine(Args) +c(Args) -> combine(Args). + + +% rrdtool commands + +%% @spec create(erlang:iodata()) -> { ok, Response } | +%% { error, Reason } +%% Reason = iolist() +%% Response = iolist() +%% @doc Set up a new Round Robin Database (RRD). Check +%% [http://oss.oetiker.ch/rrdtool/doc/rrdcreate.en.html rrdcreate]. +create (Args) when is_list(Args) -> do(create, Args). + +%% @spec update(erlang:iodata()) -> { ok, Response } | +%% { error, Reason } +%% Reason = iolist() +%% Response = iolist() +%% @doc Store new data values into an RRD. Check +%% [http://oss.oetiker.ch/rrdtool/doc/rrdupdate.en.html rrdupdate]. +update (Args) when is_list(Args) -> do(update, Args). + +%% @spec updatev(erlang:iodata()) -> { ok, Response } | +%% { error, Reason } +%% Reason = iolist() +%% Response = iolist() +%% @doc Operationally equivalent to update except for output. Check +%% [http://oss.oetiker.ch/rrdtool/doc/rrdupdate.en.html rrdupdate]. +updatev (Args) when is_list(Args) -> do(updatev, Args). + +%% @spec dump(erlang:iodata()) -> { ok, Response } | +%% { error, Reason } +%% Reason = iolist() +%% Response = iolist() +%% @doc Dump the contents of an RRD in plain ASCII. In connection with +%% restore you can use this to move an RRD from one computer +%% architecture to another. Check +%% [http://oss.oetiker.ch/rrdtool/doc/rrddump.en.html rrddump]. +dump (Args) when is_list(Args) -> do(dump, Args). + +%% @spec restore(erlang:iodata()) -> { ok, Response } | +%% { error, Reason } +%% Reason = iolist() +%% Response = iolist() +%% @doc Restore an RRD in XML format to a binary RRD. Check +%% [http://oss.oetiker.ch/rrdtool/doc/rrdrestore.en.html rrdrestore] +restore (Args) when is_list(Args) -> do(restore, Args). + +%% @spec last(erlang:iodata()) -> { ok, Response } | +%% { error, Reason } +%% Reason = iolist() +%% Response = integer() +%% @doc Return the date of the last data sample in an RRD. Check +%% [http://oss.oetiker.ch/rrdtool/doc/rrdlast.en.html rrdlast] +last (Args) when is_list(Args) -> + case do(last, Args) of + { error, Reason } -> { error, Reason }; + { ok, [[Response]] } -> { ok, erlang:list_to_integer(Response) } + end. + +%% @spec lastupdate(erlang:iodata()) -> { ok, Response } | +%% { error, Reason } +%% Reason = iolist() +%% Response = iolist() +%% @doc Return the most recent update to an RRD. Check +%% [http://oss.oetiker.ch/rrdtool/doc/rrdlastupdate.en.html rrdlastupdate] +lastupdate (Args) when is_list(Args) -> do(lastupdate, Args). + +%% @spec first(erlang:iodata()) -> { ok, Response } | +%% { error, Reason } +%% Reason = iolist() +%% Response = integer() +%% @doc Return the date of the first data sample in an RRA within an +%% RRD. Check +%% [http://oss.oetiker.ch/rrdtool/doc/rrdfirst.en.html rrdfirst] +first (Args) when is_list(Args) -> + case do(first, Args) of + { error, Reason } -> { error, Reason }; + { ok, [[Response]] } -> { ok, erlang:list_to_integer(Response) } + end. + +%% @spec info(erlang:iodata()) -> { ok, Response } | +%% { error, Reason } +%% Reason = iolist() +%% Response = iolist() +%% @doc Get information about an RRD. Check +%% [http://oss.oetiker.ch/rrdtool/doc/rrdinfo.en.html rrdinfo]. +info (Args) when is_list(Args) -> do(info, Args). + +%% @spec fetch(erlang:iodata()) -> { ok, Response } | +%% { error, Reason } +%% Reason = iolist() +%% Response = iolist() +%% @doc Get data for a certain time period from a RRD. The graph func- +%% tion uses fetch to retrieve its data from an RRD. Check +%% [http://oss.oetiker.ch/rrdtool/doc/rrdfetch.en.html rrdfetch]. +fetch (Args) when is_list(Args) -> do(fetch, Args). + +%% @spec tune(erlang:iodata()) -> { ok, Response } | +%% { error, Reason } +%% Reason = iolist() +%% Response = iolist() +%% @doc Alter setup of an RRD. Check +%% [http://oss.oetiker.ch/rrdtool/doc/rrdtune.en.html rrdtune]. +tune (Args) when is_list(Args) -> do(tune, Args). + +%% @spec resize(erlang:iodata()) -> { ok, Response } | +%% { error, Reason } +%% Reason = iolist() +%% Response = iolist() +%% @doc Change the size of individual RRAs. This is dangerous! Check +%% rrdresize. +resize (Args) when is_list(Args) -> do(resize, Args). + +%% @spec xport(erlang:iodata()) -> { ok, Response } | +%% { error, Reason } +%% Reason = iolist() +%% Response = iolist() +%% @doc Export data retrieved from one or several RRDs. Check +%% [http://oss.oetiker.ch/rrdtool/doc/rrdxport.en.html rrdxport] +%% +%% erlrrd:xport("'DEF:foo=/path with/space/foo.rrd:foo:AVERAGE' XPORT:foo"). +%% +%% erlrrd:xport(erlrrd:c(["DEF:foo=/path with/space/foo.rrd:foo:AVERAGE", "XPORT:foo"])). +xport (Args) when is_list(Args) -> do(xport, Args). + +%% @spec graph(erlang:iodata()) -> { ok, Response } | +%% { error, Reason } +%% Reason = iolist() +%% Response = iolist() +%% @doc Create a graph from data stored in one or several RRDs. Apart +%% from generating graphs, data can also be extracted to stdout. +%% Check +%% [http://oss.oetiker.ch/rrdtool/doc/rrdgraph.en.html rrdgraph]. +graph (Args) when is_list(Args) -> + % TODO: scan for this pattern w/out flattening the io_list? + % TODO: support graphing to stdout!!! :) + Flat = erlang:binary_to_list(erlang:iolist_to_binary(Args)), + case regexp:match(Flat, "(^| )-( |$)") of + { match, _, _ } -> + % graph to stdout will break this Ports parsing of reponses.. + { error, "Graphing to stdout not supported." }; + nomatch -> + do(graph, Args) + end. + +%%%%% rrd "remote" commands %%%% + +%% @spec cd(erlang:iodata()) -> ok | +%% { error, Reason } +%% Reason = iolist() +%% @doc ask the rrdtool unix process to change directories +%% +%% erlrrd:cd("/usr/share/rrd/data"). +%% +%% erlrrd:cd(erlrrd:combine(["/Users/foo/Library/Application Support/myapp/rrd"]). +cd (Arg) when is_list(Arg) -> + case do(cd, Arg) of + { error, Reason } -> { error, Reason }; + { ok, _ } -> ok + end. + +%% @spec mkdir(erlang:iodata()) -> { ok, Response } | +%% { error, Reason } +%% Reason = iolist() +%% Response = iolist() +%% @doc ask the rrdtool unix process to create a directory +mkdir (Arg) when is_list(Arg) -> do(mkdir, Arg). + +%% @spec ls() -> { ok, Response } | { error, Reason } +%% Reason = iolist() +%% Response = iolist() +%% @doc lists all *.rrd files in rrdtool unix process' +%% current working directory +ls () -> do(ls, [] ). + +%% @spec pwd() -> { ok, Response } | { error, Reason } +%% Reason = iolist() +%% Response = string() +%% @doc return the rrdtool unix process' +%% current working directory. +pwd () -> + case do(pwd, []) of + { error, Reason } -> { error, Reason }; + { ok, [[Response]] } -> { ok, Response } + end. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% Gen server interface poo +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%% @hidden +init([]) -> + RRDToolCmd = case application:get_env(erlrrd, rrdtoolcmd) of + { ok, Cmd } -> Cmd; + undefined -> "rrdtool -" + end, + Timeout = case application:get_env(erlrrd, timeout) of + { ok, T } -> T; + undefined -> 3000 + end, + process_flag(trap_exit, true), + Port = erlang:open_port( + {spawn, RRDToolCmd}, + [ {line, 10000}, eof, exit_status, stream ] + ), + {ok, #state2{port = Port, timeout = Timeout}}. + +%% handle_call +%% @hidden +handle_call( + {do, Action, Args }, + _From, + #state2{port = Port, timeout = Timeout } = State + ) -> + Line = [ erlang:atom_to_list(Action), " ", Args , "\n"], + port_command(Port, Line), + case collect_response(Port, Timeout) of + {response, Response} -> + {reply, { ok, Response }, State}; + { error, timeout } -> + {stop, port_timeout, State}; + { error, Error } -> + {reply, { error, Error }, State} + end. + +%% handle_cast +%% @hidden +handle_cast(_Msg, State) -> + % io:format(user, "Got unexpected cast msg: ~p~n", [Msg]), + %% TODO error/event loging in erlang style. + {noreply, State}. + +%% handle_info +%% @hidden +handle_info({ Port , {exit_status, Status}}, State) + when + Port =:= State#state2.port + -> + { stop, { port_exit, Status }, State}; + +handle_info(_Msg, State) -> + % io:format(user, "Got unexpected info msg: ~p~n", [Msg]), + %% TODO error/event loging in erlang style. + {noreply, State}. + +%% terminate +%% @hidden +terminate(_Reason, _State) -> ok. + +%% code_change +%% @hidden +code_change(_OldVsn, State, _Extra) -> + {ok, State}. + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +% Private poo +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +do(Command, Args) -> + case has_newline(Args) of + true -> { error, "No newlines" }; + false -> gen_server:call (?MODULE, { do, Command, Args } ) + end. + +join([Head | [] ], _Sep) -> + [Head]; +join([Head | Tail], Sep) -> + [ Head, Sep | join(Tail, Sep) ]. + +has_newline([]) -> false; +has_newline(<<>>) -> false; +has_newline([ H | T]) + when is_list(H); is_binary(H) -> + case has_newline(H) of + true -> true; + false -> has_newline(T) + end; +has_newline([ H | T]) when is_integer(H) -> + if + H =:= $\n -> true; + true -> has_newline(T) + end; +has_newline(<>) -> + if + H =:= $\n -> true; + true -> has_newline(T) + end. + + +collect_response(Port, Timeout ) -> + collect_response(Port, [], [], Timeout ). + +collect_response( Port, RespAcc, LineAcc, Timeout) -> + receive + {Port, {data, {eol, "OK u:" ++ _T }}} -> + {response, lists:reverse(RespAcc)}; + {Port, {data, {eol, "ERROR: " ++ Error }}} -> + {error, [ Error, lists:reverse(RespAcc)]}; + {Port, {data, {eol, Result}}} -> + Line = lists:reverse([Result | LineAcc]), + collect_response(Port, [Line | RespAcc], [], Timeout); + {Port, {data, {noeol, Result}}} -> + collect_response(Port, RespAcc, [Result | LineAcc], Timeout) + + %% Prevent the gen_server from hanging indefinitely in case the + %% spawned process is taking too long processing the request. + after Timeout -> + { error, timeout } + end. +-ifdef(EUNIT). +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% Test Helpers +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +test_start_stop_(StartFun, StopFun, Tag) -> + { spawn, + { inorder, + [ + check_stopped_(Tag), + { Tag, setup, + StartFun, + StopFun, + check_started_(Tag) + }, + check_stopped_(Tag) + ] + } + }. + +check_started_(Tag) -> + wrap_tag_(Tag, + [ + ?_test(ls()), + ?_test(pwd()), + ?_test(ok), + ?_test(ok) + ] + ). + +check_stopped_(Tag) -> + wrap_tag_(Tag, + [ + ?_assertExit( { noproc, _ }, pwd()), + ?_assertExit( { noproc, _ }, ls()) + ] + ). + +check_last_(RRDFile,Now) -> + fun () -> + { ok, When } = erlrrd:last(RRDFile), + When = Now + end. + +start_helper_() -> + application:unset_env(erlrrd, rrdtoolcmd), + application:unset_env(erlrrd, timeout), + {ok, Pid} = start_link(), + Pid. +stop_helper_(Pid) -> stop_helper_(Pid, 3000). +stop_helper_(Pid, Timeout) -> + true = exit(Pid, normal), + receive + {'EXIT', Pid, Reason} -> Reason + after Timeout -> + throw({ timeout, { Pid, "Pid not responding to EXIT?"} }) + end. + +% check if the dir we're in end's in /tests +check_cwd_helper_() -> + { ok, L } = file:get_cwd(), + "stset/" ++ _ = lists:reverse(L). + +p_func(X,Steps) -> + Pi = math:pi(), + 5 * ( + math:sin( 3*Pi/2 + X/(Steps / (8*Pi) ) ) + + 1 + ). + +time_since_epoch() -> + calendar:datetime_to_gregorian_seconds( erlang:universaltime()) + - ( 719528 * 86400 ). + +wrap_tag_(T,L) when is_list(L) -> + [ { T, X } || X <- L ]. + +cast(Blah) -> + gen_server:cast(?MODULE, Blah ). + +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% +%% Tests +%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%% + +%%%% test starting and stopping %%%% + +-define(spew(Args), io:format(user, "~p~n", [{Args}])). +-define(assertExists(File), + { ok, _ } = file:read_file_info(File)). +-define(assertEnoent(File), + { error, enoent } = file:read_file_info(File)). + + +start_link_test_() -> + test_start_stop_( + fun start_helper_/0, + fun stop_helper_/1, + "start_link test" + ). + +start_stop_test_() -> + test_start_stop_( + fun() -> ok = start() end, + fun(_) -> ok = stop() end, + "start/0 stop/0" + ). + +start_sup_test_() -> + test_start_stop_( + fun() -> + {ok,Pid} = erlrrd_sup:start_link(), + Pid + end, + fun stop_helper_/1, + "sup:start_link/0 exit/2" + ). + +start_sup2_test_() -> + test_start_stop_( + fun() -> + check_cwd_helper_(), + {ok,Pid} = erlrrd_sup:start_link("./dummyrrdtool"), + Pid + end, + fun stop_helper_/1, + "sup:start_link/1 exit/2" + ). + +start_app_test_() -> + test_start_stop_( + fun() -> ok = erlrrd_app:start() end, + fun(_) -> ok = erlrrd_app:stop() end, + "app:start/0 app:stop/0" + ). + +%%%% test interfaces %%%% + +datain_dataout_test_() -> + Prefix = "foo", + RRDFile = Prefix ++ ".rrd", + PNGFile = Prefix ++ ".png", + RRDDump = Prefix ++ ".rrd.xml", + RRDRestoredFile = Prefix ++ ".2.rrd", + Now = time_since_epoch(), + Then = Now - 86400, + StepSize = 60, + Steps = round((Now - Then) / StepSize), + { setup, + fun() -> + check_cwd_helper_(), + file:delete(RRDFile), + file:delete(RRDDump), + file:delete(RRDRestoredFile), + ?assertEnoent(RRDFile), + { ok, Pid } = start_link(), + Pid + end, + fun(Pid) -> + stop_helper_(Pid), + file:delete(RRDFile), + file:delete(RRDDump), + file:delete(RRDRestoredFile), + ok + end, + { inorder, + [ + % create an rrd + fun() -> + {ok, _ } = erlrrd:create([ + io_lib:fwrite("~s --start ~B", [RRDFile, Then]), + io_lib:fwrite(" --step ~B DS:thedata:GAUGE:~B:U:U", + [ StepSize, StepSize ]), + io_lib:fwrite(" RRA:AVERAGE:0.5:1:~B",[Steps]) + ]) + end, + + % write sin wave to rrd + fun() -> + lists:foreach( + fun(X) -> + P = p_func(X,Steps), + %io:format(user, "~s ~B:~f~n", [ RRDFile, Then + X * StepSize, P ]), + {ok, _ } = erlrrd:update( + io_lib:format("~s ~B:~f", [ RRDFile, Then + X * StepSize, P ]) + ) + end, + lists:seq(1, Steps) + ) + end, + + % check the update times + check_last_(RRDFile, Now), + fun() -> + { ok, When } = erlrrd:first(RRDFile), + RoundThen = (erlang:trunc(Then/StepSize) + 1) * StepSize, + When = RoundThen + end, + ?_test(?assertMatch({error, _}, erlrrd:last("/somenothing/file"))), + ?_test(?assertMatch({error, _}, erlrrd:first("/somenothing/file"))), + + % make a graph!! :) + fun() -> + { ok, _ } = erlrrd:graph([ + "-l 0 -r", " ", + "-w 700 -h 200 -a PNG ", PNGFile, + " DEF:thedata=", RRDFile, ":thedata:AVERAGE AREA:thedata#CC9945", + io_lib:format(" --start ~B --end ~B", [ Then, Now ]) + ]) + % ok, now how can we check the graph??? hmm. + end, + + % run fetch + fun() -> + { ok, _Data } = erlrrd:fetch([ + RRDFile, " AVERAGE ", + io_lib:format(" --start ~B --end ~B", [ Then, Now ]) + ]), + %?spew(Data), + %TODO check data + ok + end, + + % dump the rrd + fun() -> + ?assertEnoent(RRDDump), + { ok, _ } = erlrrd:dump( RRDFile ++ " " ++ RRDDump ), + ?assertExists(RRDDump) + end, + + % restore the rrd + fun() -> + ?assertExists(RRDDump), + ?assertEnoent(RRDRestoredFile), + { ok, _ } = erlrrd:restore( RRDDump ++ " " ++ RRDRestoredFile ), + ?assertExists(RRDRestoredFile) + end, + check_last_(RRDRestoredFile, Now), + + % xport + { "xport", + fun() -> + ?assertExists(RRDRestoredFile), + {ok, Response} = xport([ + "DEF:c=", RRDRestoredFile, ":thedata:AVERAGE", + " XPORT:c", + io_lib:format(" --start ~B --end ~B", [ Then, Now ]) + ]), + %% TODO check response + Response + end + }, + + %lastupdate + fun() -> + { ok, [ _, _, [Last]] } = lastupdate(RRDRestoredFile), + ?assertMatch( + {match, _, _ }, + regexp:match(Last, + % TODO make the regex match beginning of line? why no work? + io_lib:format("~B", [ Now ]) + ) + ), + ok + end, + + % info + fun() -> + { ok, Info } = info(RRDFile), + [ ["filename = " ++ _L] | _T ] = Info + end, + + fun() -> commas_are_cool end + ] + } + }. + +remote_cmds_test_() -> + Dir = "makadir", + { setup, + fun() -> + check_cwd_helper_(), + ?assertEnoent(Dir), + start_helper_() + end, + fun(P) -> + file:del_dir(Dir), + stop_helper_(P) + end, + { inorder, + [ + % pwd + { "pwd1", fun() -> + { ok, Cwd } = erlrrd:pwd(), + "stset/" ++ _ = lists:reverse(Cwd) + end}, + { "mkdir", fun() -> erlrrd:mkdir(Dir) end }, + { "cd ", fun() -> ok = erlrrd:cd(Dir) end}, + % pwd + { "pwd2", + fun() -> + { ok, Cwd } = erlrrd:pwd(), + { match, _, _ } = + regexp:match(Cwd, "/tests/" ++ Dir ++ "$") + end + }, + { "ls", + fun() -> + % relys on '.' and '..' always returning first? + % is that a bad idea? + { ok, List } = ls(), + io:format(user," ~p~n", [List]), + ?assert( lists:any(fun(X) -> X =:= ["d .."] end, List)) + end }, + fun() -> commas_are_cool end + ] + } + }. + +c_test_() -> + [ + ?_test( + [ + ["\"", "these", "\""], " ", + ["\"", "are", "\""], " ", + ["\"", "my", "\""], " ", + ["\"", "args", "\""] + ] = c(["these", "are", "my", "args"])), + ?_test([[ "\"", "a", "\""]] = c(["a"])) + ]. + +pass_newlines_test_() -> + M = "No newlines", + { setup, + fun start_helper_/0, + fun stop_helper_/1, + [ + ?_assertMatch( { error, M }, cd("..\n")), + ?_assertMatch( { error, M }, info("fart.rrd\n")), + ?_assertMatch( { error, M }, + create(["foo.rrd bar baz", [[[[<<"blahdedah\n">>]]]], "haha"]) + ), + fun() -> ok end + ] + }. + +graph_to_stdout_saftey_test_() -> + M = { error, "Graphing to stdout not supported." }, + [ + ?_assertMatch( M, graph(" -")), + ?_assertMatch( M, graph("-")), + ?_assertMatch( M, graph(" - ")), + ?_assertMatch( M, graph([" ", "-"," "])), + ?_assertMatch( M, graph([" ", [[["-"]]]," "])), + ?_assertMatch( M, graph([" ", [[["-"]]]])), + ?_assertMatch( M, graph([[[["-"]]]," "])), + ?_assertMatch( M, graph([" ", [[[<<"-">>]]]," "])), + ?_assertMatch( M, graph([" ", [[[<<"-">>]]]])), + ?_assertMatch( M, graph([[[[<<"-">>]]]," "])), + fun() -> ok end + ]. + +%%%% tests for corner cases %%%% + +cause_long_response_test_() -> + { setup, + fun() -> + check_cwd_helper_(), + { ok, Pid } = start_link("./dummyrrdtool -"), + Pid + end, + fun stop_helper_/1, + ?_test(do(longresponse, [])) + }. + +cause_timeout_test_() -> + { setup, + fun() -> + check_cwd_helper_(), + ok = application:set_env(erlrrd, timeout, 1), + { ok, Pid } = erlrrd_sup:start_link("./dummyrrdtool -"), + Pid + end, + fun stop_helper_/1, + ?_assertExit({port_timeout, _}, do(timeout, [])) + }. + +%%%% tests of privates %%%% + +join_test() -> + [ "a", " ", "b", " ", "c"] = join(["a", "b", "c"], " "). +join_test_() -> + [ + ?_test([ "a", " ", "b", " ", "c"] = join(["a", "b", "c"], " ")), + ?_assertNot([ "a", "b", " ", "c"] =:= join(["a", "b", "c"], " ")) + ]. + +has_newline_test_() -> + [ + ?_test( true = has_newline("\n")), + ?_test( true = has_newline(["\n"])), + ?_test( true = has_newline( + ["these", ["are", [ "my args" ] | <<"newline\n">> ], "so", "there"])), + ?_test( false = has_newline( + ["these", ["are", [ "my args" ] | <<"newline">> ], "so", "there"])), + ?_test( true = has_newline( + ["these\n", ["are", [ "my args" ] | <<"newline">> ], "so", "there"])), + ?_test( true = has_newline( + ["these", ["are", + [ "my args" | [[[[[[[[ "blah\n"]]]]]]]] ] | <<"newline">> ], + "so", "there"])), + ?_test( false = has_newline("")), + ?_test( false = has_newline([])), + ?_test( false = has_newline(<<>>)) + ]. + +%%%%% tests to get coverage %%%%% + +should_be_done_better_test_() -> + { setup, + fun start_helper_/0, + fun stop_helper_/1, + [ + ?_assertMatch( {error, _ }, tune("blah") ), + ?_assertMatch( {error, _ }, resize("blah") ), + ?_assertMatch( {error, _ }, updatev("blah") ), + ?_test(ok) + ] + }. + +handle_cast_test_() -> + { setup, + fun start_helper_/0, + fun stop_helper_/1, + ?_test(cast(blah)) + }. + +handle_info_test_() -> + { setup, + fun start_helper_/0, + fun stop_helper_/1, + ?_test(?MODULE ! yo) + }. + +stop_helper_test_() -> + { setup, + % start fun + fun() -> + F = fun(F) -> + receive + Any -> io:format(user,"~p Hey, got: ~p~n", [ self(), Any ]) + end, + F(F) + end, + spawn( + fun() -> + process_flag(trap_exit, true), + F(F) + end + ) + end, + % stop fun, + fun(P) -> exit(P, kill) end, + % test gen + fun(P) -> + ?_assertThrow({timeout,_}, stop_helper_(P, 1)) + end + }. + +port_exit_test_() -> + ?_assert( + begin + io:format(user, "~n==== test: expect erlrrd exit~n", []), + {ok,Pid} = start_link("./dummyrrdtool"), + {ok, _} = do(die, []), + receive + { 'EXIT', Pid, {port_exit, 1} } -> true + after 4000 -> + exit(Pid,kill), + false + end + end + ). + +code_change_test() -> + { ok, state } = code_change( oldvsn, state, extra ). + +-endif. diff --git a/web/api/flukso/deps/erlrrd/src/erlrrd_app.erl b/web/api/flukso/deps/erlrrd/src/erlrrd_app.erl new file mode 100644 index 0000000..c03baa5 --- /dev/null +++ b/web/api/flukso/deps/erlrrd/src/erlrrd_app.erl @@ -0,0 +1,18 @@ +-module(erlrrd_app). + +-export([start/0, stop/0]). +-behavior(application). +-export([start/2, stop/1]). + + +start() -> + application:start(erlrrd). + +start(_Type, _Args) -> + erlrrd_sup:start_link(). + +stop() -> + application:stop(erlrrd). + +stop(_State) -> + ok. diff --git a/web/api/flukso/deps/erlrrd/src/erlrrd_sup.erl b/web/api/flukso/deps/erlrrd/src/erlrrd_sup.erl new file mode 100644 index 0000000..8c2ab09 --- /dev/null +++ b/web/api/flukso/deps/erlrrd/src/erlrrd_sup.erl @@ -0,0 +1,42 @@ +-module(erlrrd_sup). + +-export([start_link/1, start_link/0]). + +-behavior(supervisor). + +-export([init/1]). + + +%% @spec start_link(RRDToolCmd) -> Result +%% RRDToolCmd = string() +%% Result = {ok,Pid} | ignore | {error,Error} +%% Pid = pid() +%% Error = {already_started,Pid} | shutdown | term() +start_link(RRDToolCmd) -> + application:set_env(erlrrd_sup, rrdtoolcmd, RRDToolCmd), + supervisor:start_link(erlrrd_sup, []). + +%% @spec start_link() -> Result +%% Result = {ok,Pid} | ignore | {error,Error} +%% Pid = pid() +%% Error = {already_started,Pid} | shutdown | term() +start_link() -> + supervisor:start_link(erlrrd_sup, []). + +init(_) -> + { + ok, + { + {one_for_one, 5, 10 }, + [ + { + erlrrd, + { erlrrd, start_link, [] }, + permanent, + 3000, + worker, + [ erlrrd ] + } + ] + } + }. diff --git a/web/api/flukso/deps/erlrrd/support/include.mk b/web/api/flukso/deps/erlrrd/support/include.mk new file mode 100644 index 0000000..065e409 --- /dev/null +++ b/web/api/flukso/deps/erlrrd/support/include.mk @@ -0,0 +1,39 @@ +## -*- makefile -*- + +###################################################################### +## Erlang + +ERL := erl +ERLC := $(ERL)c + +INCLUDE_DIRS := ../include $(wildcard ../deps/*/include) +EBIN_DIRS := $(wildcard ../deps/*/ebin) +ERLC_FLAGS := -W $(INCLUDE_DIRS:../%=-I ../%) $(EBIN_DIRS:%=-pa %) + +ifndef no_debug_info + ERLC_FLAGS += +debug_info +endif + +ifdef debug + ERLC_FLAGS += -Ddebug +endif + +EBIN_DIR := ../ebin +EMULATOR := beam + +ERL_SOURCES := $(wildcard *.erl) +ERL_HEADERS := $(wildcard *.hrl) $(wildcard ../include/*.hrl) +ERL_OBJECTS := $(ERL_SOURCES:%.erl=$(EBIN_DIR)/%.$(EMULATOR)) +ERL_OBJECTS_LOCAL := $(ERL_SOURCES:%.erl=./%.$(EMULATOR)) +APP_FILES := $(wildcard *.app) +EBIN_FILES = $(ERL_OBJECTS) $(APP_FILES:%.app=../ebin/%.app) +MODULES = $(ERL_SOURCES:%.erl=%) + +../ebin/%.app: %.app + cp $< $@ + +$(EBIN_DIR)/%.$(EMULATOR): %.erl + $(ERLC) $(ERLC_FLAGS) -o $(EBIN_DIR) $< + +./%.$(EMULATOR): %.erl + $(ERLC) $(ERLC_FLAGS) -o . $< diff --git a/web/api/flukso/deps/erlrrd/tests/Makefile.am.local b/web/api/flukso/deps/erlrrd/tests/Makefile.am.local new file mode 100644 index 0000000..05642be --- /dev/null +++ b/web/api/flukso/deps/erlrrd/tests/Makefile.am.local @@ -0,0 +1,4 @@ +TESTS = \ + module-erlrrd + + diff --git a/web/api/flukso/deps/erlrrd/tests/dummyrrdtool b/web/api/flukso/deps/erlrrd/tests/dummyrrdtool new file mode 100755 index 0000000..e7d0e61 --- /dev/null +++ b/web/api/flukso/deps/erlrrd/tests/dummyrrdtool @@ -0,0 +1,26 @@ +#!/bin/sh + +while read cmd rest +do + case $cmd in + pwd) + echo "ERROR: pwd disabled for testing" + ;; + timeout) + sleep 1 + echo "ERROR: timed out" + ;; + die) + echo "OK u:0.01 s:0.02 r:0.03" + exit 1 + ;; + longresponse) + perl -le 'print "a234567890123456789012345678901234567890" x (255)' + echo "OK u:0.01 s:0.02 r:0.03" + ;; + *) + echo $cmd $rest + echo "OK u:0.01 s:0.02 r:0.03" + ;; + esac +done diff --git a/web/api/flukso/deps/webmachine b/web/api/flukso/deps/webmachine new file mode 120000 index 0000000..a54f816 --- /dev/null +++ b/web/api/flukso/deps/webmachine @@ -0,0 +1 @@ +../../webmachine \ No newline at end of file diff --git a/web/api/flukso/priv/dispatch.conf b/web/api/flukso/priv/dispatch.conf new file mode 100644 index 0000000..dae5389 --- /dev/null +++ b/web/api/flukso/priv/dispatch.conf @@ -0,0 +1 @@ +{["sensor", sensor], flukso_resource, []}. diff --git a/web/api/flukso/priv/www/index.html b/web/api/flukso/priv/www/index.html new file mode 100644 index 0000000..8e7a2c6 --- /dev/null +++ b/web/api/flukso/priv/www/index.html @@ -0,0 +1,8 @@ + + +It Worked + + +MochiWeb running. + + diff --git a/web/api/flukso/src/flukso.app b/web/api/flukso/src/flukso.app new file mode 100644 index 0000000..b139907 --- /dev/null +++ b/web/api/flukso/src/flukso.app @@ -0,0 +1,14 @@ +{application, flukso, + [{description, "flukso"}, + {vsn, "0.1"}, + {modules, [ + flukso, + flukso_app, + flukso_sup, + flukso_deps, + flukso_resource + ]}, + {registered, []}, + {mod, {flukso_app, []}}, + {env, []}, + {applications, [kernel, stdlib, crypto]}]}. diff --git a/web/api/flukso/src/flukso.erl b/web/api/flukso/src/flukso.erl new file mode 100644 index 0000000..698849e --- /dev/null +++ b/web/api/flukso/src/flukso.erl @@ -0,0 +1,44 @@ +%% @author author +%% @copyright YYYY author. + +%% @doc TEMPLATE. + +-module(flukso). +-author('author '). +-export([start/0, start_link/0, stop/0]). + +ensure_started(App) -> + case application:start(App) of + ok -> + ok; + {error, {already_started, App}} -> + ok + end. + +%% @spec start_link() -> {ok,Pid::pid()} +%% @doc Starts the app for inclusion in a supervisor tree +start_link() -> + flukso_deps:ensure(), + ensure_started(erlrrd), + ensure_started(crypto), + ensure_started(webmachine), + flukso_sup:start_link(). + +%% @spec start() -> ok +%% @doc Start the flukso server. +start() -> + flukso_deps:ensure(), + ensure_started(erlrrd), + ensure_started(crypto), + ensure_started(webmachine), + application:start(flukso). + +%% @spec stop() -> ok +%% @doc Stop the flukso server. +stop() -> + Res = application:stop(flukso), + application:stop(erlrrd), + application:stop(webmachine), + application:stop(crypto), + Res. + diff --git a/web/api/flukso/src/flukso.hrl b/web/api/flukso/src/flukso.hrl new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/web/api/flukso/src/flukso.hrl @@ -0,0 +1 @@ + diff --git a/web/api/flukso/src/flukso_app.erl b/web/api/flukso/src/flukso_app.erl new file mode 100644 index 0000000..ef7928d --- /dev/null +++ b/web/api/flukso/src/flukso_app.erl @@ -0,0 +1,22 @@ +%% @author author +%% @copyright YYYY author. + +%% @doc Callbacks for the flukso application. + +-module(flukso_app). +-author('author '). + +-behaviour(application). +-export([start/2,stop/1]). + + +%% @spec start(_Type, _StartArgs) -> ServerRet +%% @doc application start callback for flukso. +start(_Type, _StartArgs) -> + flukso_deps:ensure(), + flukso_sup:start_link(). + +%% @spec stop(_State) -> ServerRet +%% @doc application stop callback for flukso. +stop(_State) -> + ok. diff --git a/web/api/flukso/src/flukso_deps.erl b/web/api/flukso/src/flukso_deps.erl new file mode 100644 index 0000000..7467e49 --- /dev/null +++ b/web/api/flukso/src/flukso_deps.erl @@ -0,0 +1,84 @@ +%% @author author +%% @copyright YYYY author. + +%% @doc Ensure that the relatively-installed dependencies are on the code +%% loading path, and locate resources relative +%% to this application's path. + +-module(flukso_deps). +-author('author '). + +-export([ensure/0, ensure/1]). +-export([get_base_dir/0, get_base_dir/1]). +-export([local_path/1, local_path/2]). +-export([deps_on_path/0, new_siblings/1]). + +%% @spec deps_on_path() -> [ProjNameAndVers] +%% @doc List of project dependencies on the path. +deps_on_path() -> + F = fun (X, Acc) -> + ProjDir = filename:dirname(X), + case {filename:basename(X), + filename:basename(filename:dirname(ProjDir))} of + {"ebin", "deps"} -> + [filename:basename(ProjDir) | Acc]; + _ -> + Acc + end + end, + ordsets:from_list(lists:foldl(F, [], code:get_path())). + +%% @spec new_siblings(Module) -> [Dir] +%% @doc Find new siblings paths relative to Module that aren't already on the +%% code path. +new_siblings(Module) -> + Existing = deps_on_path(), + SiblingEbin = filelib:wildcard(local_path(["deps", "*", "ebin"], Module)), + Siblings = [filename:dirname(X) || X <- SiblingEbin, + ordsets:is_element( + filename:basename(filename:dirname(X)), + Existing) =:= false], + lists:filter(fun filelib:is_dir/1, + lists:append([[filename:join([X, "ebin"]), + filename:join([X, "include"])] || + X <- Siblings])). + + +%% @spec ensure(Module) -> ok +%% @doc Ensure that all ebin and include paths for dependencies +%% of the application for Module are on the code path. +ensure(Module) -> + code:add_paths(new_siblings(Module)), + code:clash(), + ok. + +%% @spec ensure() -> ok +%% @doc Ensure that the ebin and include paths for dependencies of +%% this application are on the code path. Equivalent to +%% ensure(?Module). +ensure() -> + ensure(?MODULE). + +%% @spec get_base_dir(Module) -> string() +%% @doc Return the application directory for Module. It assumes Module is in +%% a standard OTP layout application in the ebin or src directory. +get_base_dir(Module) -> + {file, Here} = code:is_loaded(Module), + filename:dirname(filename:dirname(Here)). + +%% @spec get_base_dir() -> string() +%% @doc Return the application directory for this application. Equivalent to +%% get_base_dir(?MODULE). +get_base_dir() -> + get_base_dir(?MODULE). + +%% @spec local_path([string()], Module) -> string() +%% @doc Return an application-relative directory from Module's application. +local_path(Components, Module) -> + filename:join([get_base_dir(Module) | Components]). + +%% @spec local_path(Components) -> string() +%% @doc Return an application-relative directory for this application. +%% Equivalent to local_path(Components, ?MODULE). +local_path(Components) -> + local_path(Components, ?MODULE). diff --git a/web/api/flukso/src/flukso_resource.erl b/web/api/flukso/src/flukso_resource.erl new file mode 100644 index 0000000..3499a7c --- /dev/null +++ b/web/api/flukso/src/flukso_resource.erl @@ -0,0 +1,78 @@ +%% @author icarus75 +%% @copyright 2009-2010 flukso.net +%% @doc Flukso webmachine_resource. + +-module(flukso_resource). +-export([init/1, allowed_methods/2, malformed_request/2, content_types_provided/2, to_json/2]). + +-include_lib("webmachine/include/webmachine.hrl"). + +init([]) -> + {ok, undefined}. + +allowed_methods(ReqData, State) -> + {['GET'], ReqData, State}. + +malformed_request(ReqData, State) -> + {RrdSensor, ValidSensor} = rrd_sensor(wrq:path_info(sensor, ReqData)), + {RrdTime, ValidInterval} = rrd_time(wrq:get_qs_value("interval", ReqData)), + {RrdFactor, ValidUnit} = rrd_factor(wrq:get_qs_value("unit", ReqData)), + + {case {ValidSensor, ValidInterval, ValidUnit} of + {true, true, true} -> false; + _ -> true + end, + ReqData, {RrdSensor , RrdTime, RrdFactor}}. + +content_types_provided(ReqData, State) -> + {[{"application/json", to_json}], ReqData, State}. + +to_json(ReqData, State) -> + {RrdSensor , RrdTime, RrdFactor} = State, + + case wrq:path_info(interval, ReqData) of + "night" -> Path = "var/data/night/"; + _Interval -> Path = "var/data/base/" + end, + + case erlrrd:fetch(erlrrd:c([[Path, [RrdSensor|".rrd"]], "AVERAGE", ["-s",RrdTime]])) of + {ok, Response} -> + Filtered = [re:split(X, "[:][ ]", [{return,list}]) || [X] <- Response, string:str(X, ":") == 11], + Datapoints = [[list_to_integer(X), round(list_to_float(Y) * RrdFactor)] || [X, Y] <- Filtered, string:len(Y) /= 3], + Nans = [[list_to_integer(X), list_to_binary(Y)] || [X, Y] <- Filtered, string:len(Y) == 3], + Final = lists:merge(Datapoints, Nans), + {mochijson2:encode(Final), ReqData, State}; + + {error, Reason} -> + {{halt, 404}, ReqData, State} + end. + +rrd_sensor(Sensor) -> + case re:run(Sensor, "[0-9a-f]+", []) of + {match, [{0,32}]} -> {Sensor, true}; + _ -> {false, false} + end. + +rrd_time(Interval) -> + Intervals = [{"hour", "end-1h"}, + {"day", "end-1d"}, + {"month", "end-30d"}, + {"year", "end-1y"}, + {"night", "end-30d"}], + + case lists:keyfind(Interval, 1, Intervals) of + false -> {false, false}; + {_Interval, RrdTime} -> {RrdTime, true} + end. + +rrd_factor(Unit) -> + Units = [{"watt", 3600}, + {"kwhperyear", 31536}, + {"eurperyear", 5676}, + {"audperyear", 5991}], + + case lists:keyfind(Unit, 1, Units) of + false -> {false, false}; + {_Unit, RrdFactor} -> {RrdFactor, true} + end. + diff --git a/web/api/flukso/src/flukso_sup.erl b/web/api/flukso/src/flukso_sup.erl new file mode 100644 index 0000000..3713ac6 --- /dev/null +++ b/web/api/flukso/src/flukso_sup.erl @@ -0,0 +1,57 @@ +%% @author author +%% @copyright YYYY author. + +%% @doc Supervisor for the flukso application. + +-module(flukso_sup). +-author('author '). + +-behaviour(supervisor). + +%% External exports +-export([start_link/0, upgrade/0]). + +%% supervisor callbacks +-export([init/1]). + +%% @spec start_link() -> ServerRet +%% @doc API for starting the supervisor. +start_link() -> + supervisor:start_link({local, ?MODULE}, ?MODULE, []). + +%% @spec upgrade() -> ok +%% @doc Add processes if necessary. +upgrade() -> + {ok, {_, Specs}} = init([]), + + Old = sets:from_list( + [Name || {Name, _, _, _} <- supervisor:which_children(?MODULE)]), + New = sets:from_list([Name || {Name, _, _, _, _, _} <- Specs]), + Kill = sets:subtract(Old, New), + + sets:fold(fun (Id, ok) -> + supervisor:terminate_child(?MODULE, Id), + supervisor:delete_child(?MODULE, Id), + ok + end, ok, Kill), + + [supervisor:start_child(?MODULE, Spec) || Spec <- Specs], + ok. + +%% @spec init([]) -> SupervisorTree +%% @doc supervisor callback. +init([]) -> + Ip = case os:getenv("WEBMACHINE_IP") of false -> "127.0.0.1"; Any -> Any end, + {ok, Dispatch} = file:consult(filename:join( + [filename:dirname(code:which(?MODULE)), + "..", "priv", "dispatch.conf"])), + WebConfig = [ + {ip, Ip}, + {port, 9999}, + {log_dir, "var/log"}, + {dispatch, Dispatch}], + Web = {webmachine_mochiweb, + {webmachine_mochiweb, start, [WebConfig]}, + permanent, 5000, worker, dynamic}, + Processes = [Web], + {ok, {{one_for_one, 10, 10}, Processes}}. diff --git a/web/api/flukso/start-dev.sh b/web/api/flukso/start-dev.sh new file mode 100755 index 0000000..95637a2 --- /dev/null +++ b/web/api/flukso/start-dev.sh @@ -0,0 +1,3 @@ +#!/bin/sh +cd `dirname $0` +exec erl -smp auto +K true -sname flukso -setcookie mycookie -pa $PWD/ebin $PWD/deps/*/ebin $PWD/deps/*/deps/*/ebin -boot start_sasl -s reloader -s flukso diff --git a/web/api/flukso/start.sh b/web/api/flukso/start.sh new file mode 100755 index 0000000..e2f8457 --- /dev/null +++ b/web/api/flukso/start.sh @@ -0,0 +1,3 @@ +#!/bin/sh +cd `dirname $0` +exec erl -detached -smp auto +K true -sname flukso -setcookie mycookie -pa $PWD/ebin $PWD/deps/*/ebin $PWD/deps/*/deps/*/ebin -boot start_sasl -s reloader -s flukso diff --git a/web/api/flukso/var/data b/web/api/flukso/var/data new file mode 120000 index 0000000..e4aa74f --- /dev/null +++ b/web/api/flukso/var/data @@ -0,0 +1 @@ +../../../public/sites/all/modules/logger/data \ No newline at end of file diff --git a/web/api/webmachine/.hg/00changelog.i b/web/api/webmachine/.hg/00changelog.i new file mode 100644 index 0000000..d3a8311 Binary files /dev/null and b/web/api/webmachine/.hg/00changelog.i differ diff --git a/web/api/webmachine/.hg/branch b/web/api/webmachine/.hg/branch new file mode 100644 index 0000000..4ad96d5 --- /dev/null +++ b/web/api/webmachine/.hg/branch @@ -0,0 +1 @@ +default diff --git a/web/api/webmachine/.hg/branch.cache b/web/api/webmachine/.hg/branch.cache new file mode 100644 index 0000000..c7f1a04 --- /dev/null +++ b/web/api/webmachine/.hg/branch.cache @@ -0,0 +1,2 @@ +b3575cbf3376171f0f040609a7a49cdb9979b480 85 +b3575cbf3376171f0f040609a7a49cdb9979b480 default diff --git a/web/api/webmachine/.hg/dirstate b/web/api/webmachine/.hg/dirstate new file mode 100644 index 0000000..0a840ef Binary files /dev/null and b/web/api/webmachine/.hg/dirstate differ diff --git a/web/api/webmachine/.hg/hgrc b/web/api/webmachine/.hg/hgrc new file mode 100644 index 0000000..8bdfa77 --- /dev/null +++ b/web/api/webmachine/.hg/hgrc @@ -0,0 +1,2 @@ +[paths] +default = http://bitbucket.org/justin/webmachine/ diff --git a/web/api/webmachine/.hg/requires b/web/api/webmachine/.hg/requires new file mode 100644 index 0000000..57601b5 --- /dev/null +++ b/web/api/webmachine/.hg/requires @@ -0,0 +1,2 @@ +revlogv1 +store diff --git a/web/api/webmachine/.hg/store/00changelog.i b/web/api/webmachine/.hg/store/00changelog.i new file mode 100644 index 0000000..3f0f1e9 Binary files /dev/null and b/web/api/webmachine/.hg/store/00changelog.i differ diff --git a/web/api/webmachine/.hg/store/00manifest.i b/web/api/webmachine/.hg/store/00manifest.i new file mode 100644 index 0000000..c29793b Binary files /dev/null and b/web/api/webmachine/.hg/store/00manifest.i differ diff --git a/web/api/webmachine/.hg/store/data/.hgignore.i b/web/api/webmachine/.hg/store/data/.hgignore.i new file mode 100644 index 0000000..46baa94 Binary files /dev/null and b/web/api/webmachine/.hg/store/data/.hgignore.i differ diff --git a/web/api/webmachine/.hg/store/data/.hgtags.i b/web/api/webmachine/.hg/store/data/.hgtags.i new file mode 100644 index 0000000..512663f Binary files /dev/null and b/web/api/webmachine/.hg/store/data/.hgtags.i differ diff --git a/web/api/webmachine/.hg/store/data/_emakefile.i b/web/api/webmachine/.hg/store/data/_emakefile.i new file mode 100644 index 0000000..794c115 Binary files /dev/null and b/web/api/webmachine/.hg/store/data/_emakefile.i differ diff --git a/web/api/webmachine/.hg/store/data/_l_i_c_e_n_s_e.i b/web/api/webmachine/.hg/store/data/_l_i_c_e_n_s_e.i new file mode 100644 index 0000000..1c8d017 Binary files /dev/null and b/web/api/webmachine/.hg/store/data/_l_i_c_e_n_s_e.i differ diff --git a/web/api/webmachine/.hg/store/data/_makefile.i b/web/api/webmachine/.hg/store/data/_makefile.i new file mode 100644 index 0000000..abf4cb1 Binary files /dev/null and b/web/api/webmachine/.hg/store/data/_makefile.i differ diff --git a/web/api/webmachine/.hg/store/data/_t_h_a_n_k_s.i b/web/api/webmachine/.hg/store/data/_t_h_a_n_k_s.i new file mode 100644 index 0000000..84ee6c7 Binary files /dev/null and b/web/api/webmachine/.hg/store/data/_t_h_a_n_k_s.i differ diff --git a/web/api/webmachine/.hg/store/data/demo/_emakefile.i b/web/api/webmachine/.hg/store/data/demo/_emakefile.i new file mode 100644 index 0000000..2bb5e9a Binary files /dev/null and b/web/api/webmachine/.hg/store/data/demo/_emakefile.i differ diff --git a/web/api/webmachine/.hg/store/data/demo/_makefile.i b/web/api/webmachine/.hg/store/data/demo/_makefile.i new file mode 100644 index 0000000..5c08713 Binary files /dev/null and b/web/api/webmachine/.hg/store/data/demo/_makefile.i differ diff --git a/web/api/webmachine/.hg/store/data/demo/ebin/.hg__empty__dir.i b/web/api/webmachine/.hg/store/data/demo/ebin/.hg__empty__dir.i new file mode 100644 index 0000000..2431023 Binary files /dev/null and b/web/api/webmachine/.hg/store/data/demo/ebin/.hg__empty__dir.i differ diff --git a/web/api/webmachine/.hg/store/data/demo/mochiweb.i b/web/api/webmachine/.hg/store/data/demo/mochiweb.i new file mode 100644 index 0000000..5c92063 Binary files /dev/null and b/web/api/webmachine/.hg/store/data/demo/mochiweb.i differ diff --git a/web/api/webmachine/.hg/store/data/demo/priv/.hg__empty__dir.i b/web/api/webmachine/.hg/store/data/demo/priv/.hg__empty__dir.i new file mode 100644 index 0000000..2431023 Binary files /dev/null and b/web/api/webmachine/.hg/store/data/demo/priv/.hg__empty__dir.i differ diff --git a/web/api/webmachine/.hg/store/data/demo/src/demo__fs__resource.erl.i b/web/api/webmachine/.hg/store/data/demo/src/demo__fs__resource.erl.i new file mode 100644 index 0000000..22e7e49 Binary files /dev/null and b/web/api/webmachine/.hg/store/data/demo/src/demo__fs__resource.erl.i differ diff --git a/web/api/webmachine/.hg/store/data/demo/src/webmachine__demo.app.i b/web/api/webmachine/.hg/store/data/demo/src/webmachine__demo.app.i new file mode 100644 index 0000000..3c7ff1a Binary files /dev/null and b/web/api/webmachine/.hg/store/data/demo/src/webmachine__demo.app.i differ diff --git a/web/api/webmachine/.hg/store/data/demo/src/webmachine__demo.erl.i b/web/api/webmachine/.hg/store/data/demo/src/webmachine__demo.erl.i new file mode 100644 index 0000000..56d5198 Binary files /dev/null and b/web/api/webmachine/.hg/store/data/demo/src/webmachine__demo.erl.i differ diff --git a/web/api/webmachine/.hg/store/data/demo/src/webmachine__demo.hrl.i b/web/api/webmachine/.hg/store/data/demo/src/webmachine__demo.hrl.i new file mode 100644 index 0000000..f9b5fb9 Binary files /dev/null and b/web/api/webmachine/.hg/store/data/demo/src/webmachine__demo.hrl.i differ diff --git a/web/api/webmachine/.hg/store/data/demo/src/webmachine__demo__app.erl.i b/web/api/webmachine/.hg/store/data/demo/src/webmachine__demo__app.erl.i new file mode 100644 index 0000000..dcc3c21 Binary files /dev/null and b/web/api/webmachine/.hg/store/data/demo/src/webmachine__demo__app.erl.i differ diff --git a/web/api/webmachine/.hg/store/data/demo/src/webmachine__demo__resource.erl.i b/web/api/webmachine/.hg/store/data/demo/src/webmachine__demo__resource.erl.i new file mode 100644 index 0000000..ba4b0ac Binary files /dev/null and b/web/api/webmachine/.hg/store/data/demo/src/webmachine__demo__resource.erl.i differ diff --git a/web/api/webmachine/.hg/store/data/demo/src/webmachine__demo__sup.erl.i b/web/api/webmachine/.hg/store/data/demo/src/webmachine__demo__sup.erl.i new file mode 100644 index 0000000..5ef873b Binary files /dev/null and b/web/api/webmachine/.hg/store/data/demo/src/webmachine__demo__sup.erl.i differ diff --git a/web/api/webmachine/.hg/store/data/demo/start.sh.i b/web/api/webmachine/.hg/store/data/demo/start.sh.i new file mode 100644 index 0000000..f008cc0 Binary files /dev/null and b/web/api/webmachine/.hg/store/data/demo/start.sh.i differ diff --git a/web/api/webmachine/.hg/store/data/demo/webmachine.i b/web/api/webmachine/.hg/store/data/demo/webmachine.i new file mode 100644 index 0000000..ba1f03c Binary files /dev/null and b/web/api/webmachine/.hg/store/data/demo/webmachine.i differ diff --git a/web/api/webmachine/.hg/store/data/deps/mochiweb/_l_i_c_e_n_s_e.i b/web/api/webmachine/.hg/store/data/deps/mochiweb/_l_i_c_e_n_s_e.i new file mode 100644 index 0000000..4cdcbc5 Binary files /dev/null and b/web/api/webmachine/.hg/store/data/deps/mochiweb/_l_i_c_e_n_s_e.i differ diff --git a/web/api/webmachine/.hg/store/data/deps/mochiweb/_makefile.i b/web/api/webmachine/.hg/store/data/deps/mochiweb/_makefile.i new file mode 100644 index 0000000..df6e8e4 Binary files /dev/null and b/web/api/webmachine/.hg/store/data/deps/mochiweb/_makefile.i differ diff --git a/web/api/webmachine/.hg/store/data/deps/mochiweb/_r_e_a_d_m_e.i b/web/api/webmachine/.hg/store/data/deps/mochiweb/_r_e_a_d_m_e.i new file mode 100644 index 0000000..5a17b43 Binary files /dev/null and b/web/api/webmachine/.hg/store/data/deps/mochiweb/_r_e_a_d_m_e.i differ diff --git a/web/api/webmachine/.hg/store/data/deps/mochiweb/ebin/.hg__empty__dir.i b/web/api/webmachine/.hg/store/data/deps/mochiweb/ebin/.hg__empty__dir.i new file mode 100644 index 0000000..2431023 Binary files /dev/null and b/web/api/webmachine/.hg/store/data/deps/mochiweb/ebin/.hg__empty__dir.i differ diff --git a/web/api/webmachine/.hg/store/data/deps/mochiweb/priv/skel/_makefile.i b/web/api/webmachine/.hg/store/data/deps/mochiweb/priv/skel/_makefile.i new file mode 100644 index 0000000..5dcdd1e Binary files /dev/null and b/web/api/webmachine/.hg/store/data/deps/mochiweb/priv/skel/_makefile.i differ diff --git a/web/api/webmachine/.hg/store/data/deps/mochiweb/priv/skel/priv/www/index.html.i b/web/api/webmachine/.hg/store/data/deps/mochiweb/priv/skel/priv/www/index.html.i new file mode 100644 index 0000000..e07d12d Binary files /dev/null and b/web/api/webmachine/.hg/store/data/deps/mochiweb/priv/skel/priv/www/index.html.i differ diff --git a/web/api/webmachine/.hg/store/data/deps/mochiweb/priv/skel/src/_makefile.i b/web/api/webmachine/.hg/store/data/deps/mochiweb/priv/skel/src/_makefile.i new file mode 100644 index 0000000..914faa1 Binary files /dev/null and b/web/api/webmachine/.hg/store/data/deps/mochiweb/priv/skel/src/_makefile.i differ diff --git a/web/api/webmachine/.hg/store/data/deps/mochiweb/priv/skel/src/skel.app.i b/web/api/webmachine/.hg/store/data/deps/mochiweb/priv/skel/src/skel.app.i new file mode 100644 index 0000000..e518564 Binary files /dev/null and b/web/api/webmachine/.hg/store/data/deps/mochiweb/priv/skel/src/skel.app.i differ diff --git a/web/api/webmachine/.hg/store/data/deps/mochiweb/priv/skel/src/skel.erl.i b/web/api/webmachine/.hg/store/data/deps/mochiweb/priv/skel/src/skel.erl.i new file mode 100644 index 0000000..34aaabf Binary files /dev/null and b/web/api/webmachine/.hg/store/data/deps/mochiweb/priv/skel/src/skel.erl.i differ diff --git a/web/api/webmachine/.hg/store/data/deps/mochiweb/priv/skel/src/skel.hrl.i b/web/api/webmachine/.hg/store/data/deps/mochiweb/priv/skel/src/skel.hrl.i new file mode 100644 index 0000000..f9b5fb9 Binary files /dev/null and b/web/api/webmachine/.hg/store/data/deps/mochiweb/priv/skel/src/skel.hrl.i differ diff --git a/web/api/webmachine/.hg/store/data/deps/mochiweb/priv/skel/src/skel__app.erl.i b/web/api/webmachine/.hg/store/data/deps/mochiweb/priv/skel/src/skel__app.erl.i new file mode 100644 index 0000000..ecf52a5 Binary files /dev/null and b/web/api/webmachine/.hg/store/data/deps/mochiweb/priv/skel/src/skel__app.erl.i differ diff --git a/web/api/webmachine/.hg/store/data/deps/mochiweb/priv/skel/src/skel__deps.erl.i b/web/api/webmachine/.hg/store/data/deps/mochiweb/priv/skel/src/skel__deps.erl.i new file mode 100644 index 0000000..47241df Binary files /dev/null and b/web/api/webmachine/.hg/store/data/deps/mochiweb/priv/skel/src/skel__deps.erl.i differ diff --git a/web/api/webmachine/.hg/store/data/deps/mochiweb/priv/skel/src/skel__sup.erl.i b/web/api/webmachine/.hg/store/data/deps/mochiweb/priv/skel/src/skel__sup.erl.i new file mode 100644 index 0000000..12d9f28 Binary files /dev/null and b/web/api/webmachine/.hg/store/data/deps/mochiweb/priv/skel/src/skel__sup.erl.i differ diff --git a/web/api/webmachine/.hg/store/data/deps/mochiweb/priv/skel/src/skel__web.erl.i b/web/api/webmachine/.hg/store/data/deps/mochiweb/priv/skel/src/skel__web.erl.i new file mode 100644 index 0000000..e486378 Binary files /dev/null and b/web/api/webmachine/.hg/store/data/deps/mochiweb/priv/skel/src/skel__web.erl.i differ diff --git a/web/api/webmachine/.hg/store/data/deps/mochiweb/priv/skel/start-dev.sh.i b/web/api/webmachine/.hg/store/data/deps/mochiweb/priv/skel/start-dev.sh.i new file mode 100644 index 0000000..b467f52 Binary files /dev/null and b/web/api/webmachine/.hg/store/data/deps/mochiweb/priv/skel/start-dev.sh.i differ diff --git a/web/api/webmachine/.hg/store/data/deps/mochiweb/priv/skel/start.sh.i b/web/api/webmachine/.hg/store/data/deps/mochiweb/priv/skel/start.sh.i new file mode 100644 index 0000000..6ceaf53 Binary files /dev/null and b/web/api/webmachine/.hg/store/data/deps/mochiweb/priv/skel/start.sh.i differ diff --git a/web/api/webmachine/.hg/store/data/deps/mochiweb/priv/skel/support/include.mk.i b/web/api/webmachine/.hg/store/data/deps/mochiweb/priv/skel/support/include.mk.i new file mode 100644 index 0000000..64b1f42 Binary files /dev/null and b/web/api/webmachine/.hg/store/data/deps/mochiweb/priv/skel/support/include.mk.i differ diff --git a/web/api/webmachine/.hg/store/data/deps/mochiweb/scripts/new__mochiweb.erl.i b/web/api/webmachine/.hg/store/data/deps/mochiweb/scripts/new__mochiweb.erl.i new file mode 100644 index 0000000..10d420b Binary files /dev/null and b/web/api/webmachine/.hg/store/data/deps/mochiweb/scripts/new__mochiweb.erl.i differ diff --git a/web/api/webmachine/.hg/store/data/deps/mochiweb/src/_makefile.i b/web/api/webmachine/.hg/store/data/deps/mochiweb/src/_makefile.i new file mode 100644 index 0000000..b34d82b Binary files /dev/null and b/web/api/webmachine/.hg/store/data/deps/mochiweb/src/_makefile.i differ diff --git a/web/api/webmachine/.hg/store/data/deps/mochiweb/src/mochifmt.erl.i b/web/api/webmachine/.hg/store/data/deps/mochiweb/src/mochifmt.erl.i new file mode 100644 index 0000000..d978fd2 Binary files /dev/null and b/web/api/webmachine/.hg/store/data/deps/mochiweb/src/mochifmt.erl.i differ diff --git a/web/api/webmachine/.hg/store/data/deps/mochiweb/src/mochifmt__records.erl.i b/web/api/webmachine/.hg/store/data/deps/mochiweb/src/mochifmt__records.erl.i new file mode 100644 index 0000000..2c70bba Binary files /dev/null and b/web/api/webmachine/.hg/store/data/deps/mochiweb/src/mochifmt__records.erl.i differ diff --git a/web/api/webmachine/.hg/store/data/deps/mochiweb/src/mochifmt__std.erl.i b/web/api/webmachine/.hg/store/data/deps/mochiweb/src/mochifmt__std.erl.i new file mode 100644 index 0000000..af5380d Binary files /dev/null and b/web/api/webmachine/.hg/store/data/deps/mochiweb/src/mochifmt__std.erl.i differ diff --git a/web/api/webmachine/.hg/store/data/deps/mochiweb/src/mochihex.erl.i b/web/api/webmachine/.hg/store/data/deps/mochiweb/src/mochihex.erl.i new file mode 100644 index 0000000..8d45110 Binary files /dev/null and b/web/api/webmachine/.hg/store/data/deps/mochiweb/src/mochihex.erl.i differ diff --git a/web/api/webmachine/.hg/store/data/deps/mochiweb/src/mochijson.erl.i b/web/api/webmachine/.hg/store/data/deps/mochiweb/src/mochijson.erl.i new file mode 100644 index 0000000..9835e68 Binary files /dev/null and b/web/api/webmachine/.hg/store/data/deps/mochiweb/src/mochijson.erl.i differ diff --git a/web/api/webmachine/.hg/store/data/deps/mochiweb/src/mochijson2.erl.i b/web/api/webmachine/.hg/store/data/deps/mochiweb/src/mochijson2.erl.i new file mode 100644 index 0000000..e444586 Binary files /dev/null and b/web/api/webmachine/.hg/store/data/deps/mochiweb/src/mochijson2.erl.i differ diff --git a/web/api/webmachine/.hg/store/data/deps/mochiweb/src/mochinum.erl.i b/web/api/webmachine/.hg/store/data/deps/mochiweb/src/mochinum.erl.i new file mode 100644 index 0000000..697dffb Binary files /dev/null and b/web/api/webmachine/.hg/store/data/deps/mochiweb/src/mochinum.erl.i differ diff --git a/web/api/webmachine/.hg/store/data/deps/mochiweb/src/mochiweb.app.i b/web/api/webmachine/.hg/store/data/deps/mochiweb/src/mochiweb.app.i new file mode 100644 index 0000000..7d1c823 Binary files /dev/null and b/web/api/webmachine/.hg/store/data/deps/mochiweb/src/mochiweb.app.i differ diff --git a/web/api/webmachine/.hg/store/data/deps/mochiweb/src/mochiweb.erl.i b/web/api/webmachine/.hg/store/data/deps/mochiweb/src/mochiweb.erl.i new file mode 100644 index 0000000..7aa20cb Binary files /dev/null and b/web/api/webmachine/.hg/store/data/deps/mochiweb/src/mochiweb.erl.i differ diff --git a/web/api/webmachine/.hg/store/data/deps/mochiweb/src/mochiweb__app.erl.i b/web/api/webmachine/.hg/store/data/deps/mochiweb/src/mochiweb__app.erl.i new file mode 100644 index 0000000..b9629d7 Binary files /dev/null and b/web/api/webmachine/.hg/store/data/deps/mochiweb/src/mochiweb__app.erl.i differ diff --git a/web/api/webmachine/.hg/store/data/deps/mochiweb/src/mochiweb__charref.erl.i b/web/api/webmachine/.hg/store/data/deps/mochiweb/src/mochiweb__charref.erl.i new file mode 100644 index 0000000..7343bb8 Binary files /dev/null and b/web/api/webmachine/.hg/store/data/deps/mochiweb/src/mochiweb__charref.erl.i differ diff --git a/web/api/webmachine/.hg/store/data/deps/mochiweb/src/mochiweb__cookies.erl.i b/web/api/webmachine/.hg/store/data/deps/mochiweb/src/mochiweb__cookies.erl.i new file mode 100644 index 0000000..a945b1d Binary files /dev/null and b/web/api/webmachine/.hg/store/data/deps/mochiweb/src/mochiweb__cookies.erl.i differ diff --git a/web/api/webmachine/.hg/store/data/deps/mochiweb/src/mochiweb__echo.erl.i b/web/api/webmachine/.hg/store/data/deps/mochiweb/src/mochiweb__echo.erl.i new file mode 100644 index 0000000..82939cc Binary files /dev/null and b/web/api/webmachine/.hg/store/data/deps/mochiweb/src/mochiweb__echo.erl.i differ diff --git a/web/api/webmachine/.hg/store/data/deps/mochiweb/src/mochiweb__headers.erl.i b/web/api/webmachine/.hg/store/data/deps/mochiweb/src/mochiweb__headers.erl.i new file mode 100644 index 0000000..81a9348 Binary files /dev/null and b/web/api/webmachine/.hg/store/data/deps/mochiweb/src/mochiweb__headers.erl.i differ diff --git a/web/api/webmachine/.hg/store/data/deps/mochiweb/src/mochiweb__html.erl.i b/web/api/webmachine/.hg/store/data/deps/mochiweb/src/mochiweb__html.erl.i new file mode 100644 index 0000000..20b60d2 Binary files /dev/null and b/web/api/webmachine/.hg/store/data/deps/mochiweb/src/mochiweb__html.erl.i differ diff --git a/web/api/webmachine/.hg/store/data/deps/mochiweb/src/mochiweb__http.erl.i b/web/api/webmachine/.hg/store/data/deps/mochiweb/src/mochiweb__http.erl.i new file mode 100644 index 0000000..5fb598c Binary files /dev/null and b/web/api/webmachine/.hg/store/data/deps/mochiweb/src/mochiweb__http.erl.i differ diff --git a/web/api/webmachine/.hg/store/data/deps/mochiweb/src/mochiweb__multipart.erl.i b/web/api/webmachine/.hg/store/data/deps/mochiweb/src/mochiweb__multipart.erl.i new file mode 100644 index 0000000..a44ad95 Binary files /dev/null and b/web/api/webmachine/.hg/store/data/deps/mochiweb/src/mochiweb__multipart.erl.i differ diff --git a/web/api/webmachine/.hg/store/data/deps/mochiweb/src/mochiweb__request.erl.i b/web/api/webmachine/.hg/store/data/deps/mochiweb/src/mochiweb__request.erl.i new file mode 100644 index 0000000..49f48b0 Binary files /dev/null and b/web/api/webmachine/.hg/store/data/deps/mochiweb/src/mochiweb__request.erl.i differ diff --git a/web/api/webmachine/.hg/store/data/deps/mochiweb/src/mochiweb__response.erl.i b/web/api/webmachine/.hg/store/data/deps/mochiweb/src/mochiweb__response.erl.i new file mode 100644 index 0000000..2ee9e49 Binary files /dev/null and b/web/api/webmachine/.hg/store/data/deps/mochiweb/src/mochiweb__response.erl.i differ diff --git a/web/api/webmachine/.hg/store/data/deps/mochiweb/src/mochiweb__skel.erl.i b/web/api/webmachine/.hg/store/data/deps/mochiweb/src/mochiweb__skel.erl.i new file mode 100644 index 0000000..c79c627 Binary files /dev/null and b/web/api/webmachine/.hg/store/data/deps/mochiweb/src/mochiweb__skel.erl.i differ diff --git a/web/api/webmachine/.hg/store/data/deps/mochiweb/src/mochiweb__socket__server.erl.i b/web/api/webmachine/.hg/store/data/deps/mochiweb/src/mochiweb__socket__server.erl.i new file mode 100644 index 0000000..9cae11b Binary files /dev/null and b/web/api/webmachine/.hg/store/data/deps/mochiweb/src/mochiweb__socket__server.erl.i differ diff --git a/web/api/webmachine/.hg/store/data/deps/mochiweb/src/mochiweb__sup.erl.i b/web/api/webmachine/.hg/store/data/deps/mochiweb/src/mochiweb__sup.erl.i new file mode 100644 index 0000000..b249f7f Binary files /dev/null and b/web/api/webmachine/.hg/store/data/deps/mochiweb/src/mochiweb__sup.erl.i differ diff --git a/web/api/webmachine/.hg/store/data/deps/mochiweb/src/mochiweb__util.erl.i b/web/api/webmachine/.hg/store/data/deps/mochiweb/src/mochiweb__util.erl.i new file mode 100644 index 0000000..6e51e30 Binary files /dev/null and b/web/api/webmachine/.hg/store/data/deps/mochiweb/src/mochiweb__util.erl.i differ diff --git a/web/api/webmachine/.hg/store/data/deps/mochiweb/src/reloader.erl.i b/web/api/webmachine/.hg/store/data/deps/mochiweb/src/reloader.erl.i new file mode 100644 index 0000000..ee70c69 Binary files /dev/null and b/web/api/webmachine/.hg/store/data/deps/mochiweb/src/reloader.erl.i differ diff --git a/web/api/webmachine/.hg/store/data/deps/mochiweb/support/include.mk.i b/web/api/webmachine/.hg/store/data/deps/mochiweb/support/include.mk.i new file mode 100644 index 0000000..4b1b92e Binary files /dev/null and b/web/api/webmachine/.hg/store/data/deps/mochiweb/support/include.mk.i differ diff --git a/web/api/webmachine/.hg/store/data/docs/http-headers-status-v3.png.d b/web/api/webmachine/.hg/store/data/docs/http-headers-status-v3.png.d new file mode 100644 index 0000000..09a8b0d Binary files /dev/null and b/web/api/webmachine/.hg/store/data/docs/http-headers-status-v3.png.d differ diff --git a/web/api/webmachine/.hg/store/data/docs/http-headers-status-v3.png.i b/web/api/webmachine/.hg/store/data/docs/http-headers-status-v3.png.i new file mode 100644 index 0000000..68810b7 Binary files /dev/null and b/web/api/webmachine/.hg/store/data/docs/http-headers-status-v3.png.i differ diff --git a/web/api/webmachine/.hg/store/data/ebin/.hg__empty__dir.i b/web/api/webmachine/.hg/store/data/ebin/.hg__empty__dir.i new file mode 100644 index 0000000..2431023 Binary files /dev/null and b/web/api/webmachine/.hg/store/data/ebin/.hg__empty__dir.i differ diff --git a/web/api/webmachine/.hg/store/data/include/webmachine.hrl.i b/web/api/webmachine/.hg/store/data/include/webmachine.hrl.i new file mode 100644 index 0000000..409c286 Binary files /dev/null and b/web/api/webmachine/.hg/store/data/include/webmachine.hrl.i differ diff --git a/web/api/webmachine/.hg/store/data/include/wm__reqdata.hrl.i b/web/api/webmachine/.hg/store/data/include/wm__reqdata.hrl.i new file mode 100644 index 0000000..f275c49 Binary files /dev/null and b/web/api/webmachine/.hg/store/data/include/wm__reqdata.hrl.i differ diff --git a/web/api/webmachine/.hg/store/data/priv/skel/_emakefile.i b/web/api/webmachine/.hg/store/data/priv/skel/_emakefile.i new file mode 100644 index 0000000..794c115 Binary files /dev/null and b/web/api/webmachine/.hg/store/data/priv/skel/_emakefile.i differ diff --git a/web/api/webmachine/.hg/store/data/priv/skel/_makefile.i b/web/api/webmachine/.hg/store/data/priv/skel/_makefile.i new file mode 100644 index 0000000..5c6ed44 Binary files /dev/null and b/web/api/webmachine/.hg/store/data/priv/skel/_makefile.i differ diff --git a/web/api/webmachine/.hg/store/data/priv/skel/deps/.empty.i b/web/api/webmachine/.hg/store/data/priv/skel/deps/.empty.i new file mode 100644 index 0000000..2431023 Binary files /dev/null and b/web/api/webmachine/.hg/store/data/priv/skel/deps/.empty.i differ diff --git a/web/api/webmachine/.hg/store/data/priv/skel/ebin/.empty.i b/web/api/webmachine/.hg/store/data/priv/skel/ebin/.empty.i new file mode 100644 index 0000000..2431023 Binary files /dev/null and b/web/api/webmachine/.hg/store/data/priv/skel/ebin/.empty.i differ diff --git a/web/api/webmachine/.hg/store/data/priv/skel/priv/dispatch.conf.i b/web/api/webmachine/.hg/store/data/priv/skel/priv/dispatch.conf.i new file mode 100644 index 0000000..9e02450 Binary files /dev/null and b/web/api/webmachine/.hg/store/data/priv/skel/priv/dispatch.conf.i differ diff --git a/web/api/webmachine/.hg/store/data/priv/skel/priv/www/index.html.i b/web/api/webmachine/.hg/store/data/priv/skel/priv/www/index.html.i new file mode 100644 index 0000000..e07d12d Binary files /dev/null and b/web/api/webmachine/.hg/store/data/priv/skel/priv/www/index.html.i differ diff --git a/web/api/webmachine/.hg/store/data/priv/skel/src/skel.app.i b/web/api/webmachine/.hg/store/data/priv/skel/src/skel.app.i new file mode 100644 index 0000000..3e9d610 Binary files /dev/null and b/web/api/webmachine/.hg/store/data/priv/skel/src/skel.app.i differ diff --git a/web/api/webmachine/.hg/store/data/priv/skel/src/skel.erl.i b/web/api/webmachine/.hg/store/data/priv/skel/src/skel.erl.i new file mode 100644 index 0000000..6b131af Binary files /dev/null and b/web/api/webmachine/.hg/store/data/priv/skel/src/skel.erl.i differ diff --git a/web/api/webmachine/.hg/store/data/priv/skel/src/skel.hrl.i b/web/api/webmachine/.hg/store/data/priv/skel/src/skel.hrl.i new file mode 100644 index 0000000..f9b5fb9 Binary files /dev/null and b/web/api/webmachine/.hg/store/data/priv/skel/src/skel.hrl.i differ diff --git a/web/api/webmachine/.hg/store/data/priv/skel/src/skel__app.erl.i b/web/api/webmachine/.hg/store/data/priv/skel/src/skel__app.erl.i new file mode 100644 index 0000000..ecf52a5 Binary files /dev/null and b/web/api/webmachine/.hg/store/data/priv/skel/src/skel__app.erl.i differ diff --git a/web/api/webmachine/.hg/store/data/priv/skel/src/skel__deps.erl.i b/web/api/webmachine/.hg/store/data/priv/skel/src/skel__deps.erl.i new file mode 100644 index 0000000..47241df Binary files /dev/null and b/web/api/webmachine/.hg/store/data/priv/skel/src/skel__deps.erl.i differ diff --git a/web/api/webmachine/.hg/store/data/priv/skel/src/skel__resource.erl.i b/web/api/webmachine/.hg/store/data/priv/skel/src/skel__resource.erl.i new file mode 100644 index 0000000..fff5800 Binary files /dev/null and b/web/api/webmachine/.hg/store/data/priv/skel/src/skel__resource.erl.i differ diff --git a/web/api/webmachine/.hg/store/data/priv/skel/src/skel__sup.erl.i b/web/api/webmachine/.hg/store/data/priv/skel/src/skel__sup.erl.i new file mode 100644 index 0000000..9198432 Binary files /dev/null and b/web/api/webmachine/.hg/store/data/priv/skel/src/skel__sup.erl.i differ diff --git a/web/api/webmachine/.hg/store/data/priv/skel/start-dev.sh.i b/web/api/webmachine/.hg/store/data/priv/skel/start-dev.sh.i new file mode 100644 index 0000000..390f621 Binary files /dev/null and b/web/api/webmachine/.hg/store/data/priv/skel/start-dev.sh.i differ diff --git a/web/api/webmachine/.hg/store/data/priv/skel/start.sh.i b/web/api/webmachine/.hg/store/data/priv/skel/start.sh.i new file mode 100644 index 0000000..5c02194 Binary files /dev/null and b/web/api/webmachine/.hg/store/data/priv/skel/start.sh.i differ diff --git a/web/api/webmachine/.hg/store/data/priv/www/index.html.i b/web/api/webmachine/.hg/store/data/priv/www/index.html.i new file mode 100644 index 0000000..e1ac79c Binary files /dev/null and b/web/api/webmachine/.hg/store/data/priv/www/index.html.i differ diff --git a/web/api/webmachine/.hg/store/data/scripts/new__webmachine.erl.i b/web/api/webmachine/.hg/store/data/scripts/new__webmachine.erl.i new file mode 100644 index 0000000..aab366c Binary files /dev/null and b/web/api/webmachine/.hg/store/data/scripts/new__webmachine.erl.i differ diff --git a/web/api/webmachine/.hg/store/data/src/webmachine.app.i b/web/api/webmachine/.hg/store/data/src/webmachine.app.i new file mode 100644 index 0000000..0d8b000 Binary files /dev/null and b/web/api/webmachine/.hg/store/data/src/webmachine.app.i differ diff --git a/web/api/webmachine/.hg/store/data/src/webmachine.erl.i b/web/api/webmachine/.hg/store/data/src/webmachine.erl.i new file mode 100644 index 0000000..aa5f007 Binary files /dev/null and b/web/api/webmachine/.hg/store/data/src/webmachine.erl.i differ diff --git a/web/api/webmachine/.hg/store/data/src/webmachine__app.erl.i b/web/api/webmachine/.hg/store/data/src/webmachine__app.erl.i new file mode 100644 index 0000000..68cab2e Binary files /dev/null and b/web/api/webmachine/.hg/store/data/src/webmachine__app.erl.i differ diff --git a/web/api/webmachine/.hg/store/data/src/webmachine__decision__core.erl.i b/web/api/webmachine/.hg/store/data/src/webmachine__decision__core.erl.i new file mode 100644 index 0000000..d7397d2 Binary files /dev/null and b/web/api/webmachine/.hg/store/data/src/webmachine__decision__core.erl.i differ diff --git a/web/api/webmachine/.hg/store/data/src/webmachine__deps.erl.i b/web/api/webmachine/.hg/store/data/src/webmachine__deps.erl.i new file mode 100644 index 0000000..3b03573 Binary files /dev/null and b/web/api/webmachine/.hg/store/data/src/webmachine__deps.erl.i differ diff --git a/web/api/webmachine/.hg/store/data/src/webmachine__dispatcher.erl.i b/web/api/webmachine/.hg/store/data/src/webmachine__dispatcher.erl.i new file mode 100644 index 0000000..14b50e9 Binary files /dev/null and b/web/api/webmachine/.hg/store/data/src/webmachine__dispatcher.erl.i differ diff --git a/web/api/webmachine/.hg/store/data/src/webmachine__error__handler.erl.i b/web/api/webmachine/.hg/store/data/src/webmachine__error__handler.erl.i new file mode 100644 index 0000000..66d84f3 Binary files /dev/null and b/web/api/webmachine/.hg/store/data/src/webmachine__error__handler.erl.i differ diff --git a/web/api/webmachine/.hg/store/data/src/webmachine__logger.erl.i b/web/api/webmachine/.hg/store/data/src/webmachine__logger.erl.i new file mode 100644 index 0000000..fe4f7bb Binary files /dev/null and b/web/api/webmachine/.hg/store/data/src/webmachine__logger.erl.i differ diff --git a/web/api/webmachine/.hg/store/data/src/webmachine__logger.hrl.i b/web/api/webmachine/.hg/store/data/src/webmachine__logger.hrl.i new file mode 100644 index 0000000..50cdd78 Binary files /dev/null and b/web/api/webmachine/.hg/store/data/src/webmachine__logger.hrl.i differ diff --git a/web/api/webmachine/.hg/store/data/src/webmachine__mochiweb.erl.i b/web/api/webmachine/.hg/store/data/src/webmachine__mochiweb.erl.i new file mode 100644 index 0000000..06a191a Binary files /dev/null and b/web/api/webmachine/.hg/store/data/src/webmachine__mochiweb.erl.i differ diff --git a/web/api/webmachine/.hg/store/data/src/webmachine__multipart.erl.i b/web/api/webmachine/.hg/store/data/src/webmachine__multipart.erl.i new file mode 100644 index 0000000..af882a6 Binary files /dev/null and b/web/api/webmachine/.hg/store/data/src/webmachine__multipart.erl.i differ diff --git a/web/api/webmachine/.hg/store/data/src/webmachine__perf__logger.erl.i b/web/api/webmachine/.hg/store/data/src/webmachine__perf__logger.erl.i new file mode 100644 index 0000000..46b08ba Binary files /dev/null and b/web/api/webmachine/.hg/store/data/src/webmachine__perf__logger.erl.i differ diff --git a/web/api/webmachine/.hg/store/data/src/webmachine__request.erl.i b/web/api/webmachine/.hg/store/data/src/webmachine__request.erl.i new file mode 100644 index 0000000..fdfd1c4 Binary files /dev/null and b/web/api/webmachine/.hg/store/data/src/webmachine__request.erl.i differ diff --git a/web/api/webmachine/.hg/store/data/src/webmachine__request__srv.erl.i b/web/api/webmachine/.hg/store/data/src/webmachine__request__srv.erl.i new file mode 100644 index 0000000..ef54b54 Binary files /dev/null and b/web/api/webmachine/.hg/store/data/src/webmachine__request__srv.erl.i differ diff --git a/web/api/webmachine/.hg/store/data/src/webmachine__resource.erl.i b/web/api/webmachine/.hg/store/data/src/webmachine__resource.erl.i new file mode 100644 index 0000000..d5fc96e Binary files /dev/null and b/web/api/webmachine/.hg/store/data/src/webmachine__resource.erl.i differ diff --git a/web/api/webmachine/.hg/store/data/src/webmachine__skel.erl.i b/web/api/webmachine/.hg/store/data/src/webmachine__skel.erl.i new file mode 100644 index 0000000..a3e3e7c Binary files /dev/null and b/web/api/webmachine/.hg/store/data/src/webmachine__skel.erl.i differ diff --git a/web/api/webmachine/.hg/store/data/src/webmachine__sup.erl.i b/web/api/webmachine/.hg/store/data/src/webmachine__sup.erl.i new file mode 100644 index 0000000..0fcec69 Binary files /dev/null and b/web/api/webmachine/.hg/store/data/src/webmachine__sup.erl.i differ diff --git a/web/api/webmachine/.hg/store/data/src/webmachine__util.erl.i b/web/api/webmachine/.hg/store/data/src/webmachine__util.erl.i new file mode 100644 index 0000000..c18d2cd Binary files /dev/null and b/web/api/webmachine/.hg/store/data/src/webmachine__util.erl.i differ diff --git a/web/api/webmachine/.hg/store/data/src/wmtrace__resource.erl.i b/web/api/webmachine/.hg/store/data/src/wmtrace__resource.erl.i new file mode 100644 index 0000000..c75b6b7 Binary files /dev/null and b/web/api/webmachine/.hg/store/data/src/wmtrace__resource.erl.i differ diff --git a/web/api/webmachine/.hg/store/data/src/wrq.erl.i b/web/api/webmachine/.hg/store/data/src/wrq.erl.i new file mode 100644 index 0000000..9715e4e Binary files /dev/null and b/web/api/webmachine/.hg/store/data/src/wrq.erl.i differ diff --git a/web/api/webmachine/.hg/store/data/start-dev.sh.i b/web/api/webmachine/.hg/store/data/start-dev.sh.i new file mode 100644 index 0000000..845d3b4 Binary files /dev/null and b/web/api/webmachine/.hg/store/data/start-dev.sh.i differ diff --git a/web/api/webmachine/.hg/store/data/start.sh.i b/web/api/webmachine/.hg/store/data/start.sh.i new file mode 100644 index 0000000..e06abe9 Binary files /dev/null and b/web/api/webmachine/.hg/store/data/start.sh.i differ diff --git a/web/api/webmachine/.hg/store/data/trace/wmtrace.css.i b/web/api/webmachine/.hg/store/data/trace/wmtrace.css.i new file mode 100644 index 0000000..ed0bbca Binary files /dev/null and b/web/api/webmachine/.hg/store/data/trace/wmtrace.css.i differ diff --git a/web/api/webmachine/.hg/store/data/trace/wmtrace.js.i b/web/api/webmachine/.hg/store/data/trace/wmtrace.js.i new file mode 100644 index 0000000..4686cbf Binary files /dev/null and b/web/api/webmachine/.hg/store/data/trace/wmtrace.js.i differ diff --git a/web/api/webmachine/.hg/store/data/www/blogs.html.i b/web/api/webmachine/.hg/store/data/www/blogs.html.i new file mode 100644 index 0000000..e8355db Binary files /dev/null and b/web/api/webmachine/.hg/store/data/www/blogs.html.i differ diff --git a/web/api/webmachine/.hg/store/data/www/contact.html.i b/web/api/webmachine/.hg/store/data/www/contact.html.i new file mode 100644 index 0000000..8e02498 Binary files /dev/null and b/web/api/webmachine/.hg/store/data/www/contact.html.i differ diff --git a/web/api/webmachine/.hg/store/data/www/css/style-1c.css.i b/web/api/webmachine/.hg/store/data/www/css/style-1c.css.i new file mode 100644 index 0000000..8bd8817 Binary files /dev/null and b/web/api/webmachine/.hg/store/data/www/css/style-1c.css.i differ diff --git a/web/api/webmachine/.hg/store/data/www/css/style.css.i b/web/api/webmachine/.hg/store/data/www/css/style.css.i new file mode 100644 index 0000000..726c706 Binary files /dev/null and b/web/api/webmachine/.hg/store/data/www/css/style.css.i differ diff --git a/web/api/webmachine/.hg/store/data/www/debugging.html.i b/web/api/webmachine/.hg/store/data/www/debugging.html.i new file mode 100644 index 0000000..c2a8cd8 Binary files /dev/null and b/web/api/webmachine/.hg/store/data/www/debugging.html.i differ diff --git a/web/api/webmachine/.hg/store/data/www/diagram.html.i b/web/api/webmachine/.hg/store/data/www/diagram.html.i new file mode 100644 index 0000000..01818a6 Binary files /dev/null and b/web/api/webmachine/.hg/store/data/www/diagram.html.i differ diff --git a/web/api/webmachine/.hg/store/data/www/dispatcher.html.i b/web/api/webmachine/.hg/store/data/www/dispatcher.html.i new file mode 100644 index 0000000..ec8c1e6 Binary files /dev/null and b/web/api/webmachine/.hg/store/data/www/dispatcher.html.i differ diff --git a/web/api/webmachine/.hg/store/data/www/docs.html.i b/web/api/webmachine/.hg/store/data/www/docs.html.i new file mode 100644 index 0000000..af1e263 Binary files /dev/null and b/web/api/webmachine/.hg/store/data/www/docs.html.i differ diff --git a/web/api/webmachine/.hg/store/data/www/example__resources.html.i b/web/api/webmachine/.hg/store/data/www/example__resources.html.i new file mode 100644 index 0000000..1d506fb Binary files /dev/null and b/web/api/webmachine/.hg/store/data/www/example__resources.html.i differ diff --git a/web/api/webmachine/.hg/store/data/www/favicon.ico.i b/web/api/webmachine/.hg/store/data/www/favicon.ico.i new file mode 100644 index 0000000..0c1e568 Binary files /dev/null and b/web/api/webmachine/.hg/store/data/www/favicon.ico.i differ diff --git a/web/api/webmachine/.hg/store/data/www/images/_w_m200-crop.png.i b/web/api/webmachine/.hg/store/data/www/images/_w_m200-crop.png.i new file mode 100644 index 0000000..f01b6c8 Binary files /dev/null and b/web/api/webmachine/.hg/store/data/www/images/_w_m200-crop.png.i differ diff --git a/web/api/webmachine/.hg/store/data/www/images/basho-landscape.gif.i b/web/api/webmachine/.hg/store/data/www/images/basho-landscape.gif.i new file mode 100644 index 0000000..046ceea Binary files /dev/null and b/web/api/webmachine/.hg/store/data/www/images/basho-landscape.gif.i differ diff --git a/web/api/webmachine/.hg/store/data/www/images/basic-trace-decision-tab.png.i b/web/api/webmachine/.hg/store/data/www/images/basic-trace-decision-tab.png.i new file mode 100644 index 0000000..f5bc939 Binary files /dev/null and b/web/api/webmachine/.hg/store/data/www/images/basic-trace-decision-tab.png.i differ diff --git a/web/api/webmachine/.hg/store/data/www/images/basic-trace-labeled.png.i b/web/api/webmachine/.hg/store/data/www/images/basic-trace-labeled.png.i new file mode 100644 index 0000000..10e5dd5 Binary files /dev/null and b/web/api/webmachine/.hg/store/data/www/images/basic-trace-labeled.png.i differ diff --git a/web/api/webmachine/.hg/store/data/www/images/basic-trace-request-tab.png.i b/web/api/webmachine/.hg/store/data/www/images/basic-trace-request-tab.png.i new file mode 100644 index 0000000..e0dcef5 Binary files /dev/null and b/web/api/webmachine/.hg/store/data/www/images/basic-trace-request-tab.png.i differ diff --git a/web/api/webmachine/.hg/store/data/www/images/basic-trace-response-tab.png.i b/web/api/webmachine/.hg/store/data/www/images/basic-trace-response-tab.png.i new file mode 100644 index 0000000..7dac1ec Binary files /dev/null and b/web/api/webmachine/.hg/store/data/www/images/basic-trace-response-tab.png.i differ diff --git a/web/api/webmachine/.hg/store/data/www/images/bg.gif.i b/web/api/webmachine/.hg/store/data/www/images/bg.gif.i new file mode 100644 index 0000000..309b2b9 Binary files /dev/null and b/web/api/webmachine/.hg/store/data/www/images/bg.gif.i differ diff --git a/web/api/webmachine/.hg/store/data/www/images/blankbox.gif.i b/web/api/webmachine/.hg/store/data/www/images/blankbox.gif.i new file mode 100644 index 0000000..914044b Binary files /dev/null and b/web/api/webmachine/.hg/store/data/www/images/blankbox.gif.i differ diff --git a/web/api/webmachine/.hg/store/data/www/images/chash.gif.i b/web/api/webmachine/.hg/store/data/www/images/chash.gif.i new file mode 100644 index 0000000..167b6ae Binary files /dev/null and b/web/api/webmachine/.hg/store/data/www/images/chash.gif.i differ diff --git a/web/api/webmachine/.hg/store/data/www/images/easy-ops.gif.i b/web/api/webmachine/.hg/store/data/www/images/easy-ops.gif.i new file mode 100644 index 0000000..c5a7d38 Binary files /dev/null and b/web/api/webmachine/.hg/store/data/www/images/easy-ops.gif.i differ diff --git a/web/api/webmachine/.hg/store/data/www/images/gossip4.gif.i b/web/api/webmachine/.hg/store/data/www/images/gossip4.gif.i new file mode 100644 index 0000000..b60ae28 Binary files /dev/null and b/web/api/webmachine/.hg/store/data/www/images/gossip4.gif.i differ diff --git a/web/api/webmachine/.hg/store/data/www/images/halfblankbox.gif.i b/web/api/webmachine/.hg/store/data/www/images/halfblankbox.gif.i new file mode 100644 index 0000000..99f861b Binary files /dev/null and b/web/api/webmachine/.hg/store/data/www/images/halfblankbox.gif.i differ diff --git a/web/api/webmachine/.hg/store/data/www/images/http-headers-status-v3.png.d b/web/api/webmachine/.hg/store/data/www/images/http-headers-status-v3.png.d new file mode 100644 index 0000000..09a8b0d Binary files /dev/null and b/web/api/webmachine/.hg/store/data/www/images/http-headers-status-v3.png.d differ diff --git a/web/api/webmachine/.hg/store/data/www/images/http-headers-status-v3.png.i b/web/api/webmachine/.hg/store/data/www/images/http-headers-status-v3.png.i new file mode 100644 index 0000000..7dccfaf Binary files /dev/null and b/web/api/webmachine/.hg/store/data/www/images/http-headers-status-v3.png.i differ diff --git a/web/api/webmachine/.hg/store/data/www/images/more.gif.i b/web/api/webmachine/.hg/store/data/www/images/more.gif.i new file mode 100644 index 0000000..f5d02e1 Binary files /dev/null and b/web/api/webmachine/.hg/store/data/www/images/more.gif.i differ diff --git a/web/api/webmachine/.hg/store/data/www/images/site.gif.i b/web/api/webmachine/.hg/store/data/www/images/site.gif.i new file mode 100644 index 0000000..b2cbb40 Binary files /dev/null and b/web/api/webmachine/.hg/store/data/www/images/site.gif.i differ diff --git a/web/api/webmachine/.hg/store/data/www/images/splash250.gif.i b/web/api/webmachine/.hg/store/data/www/images/splash250.gif.i new file mode 100644 index 0000000..a4e8bba Binary files /dev/null and b/web/api/webmachine/.hg/store/data/www/images/splash250.gif.i differ diff --git a/web/api/webmachine/.hg/store/data/www/images/vclock.gif.i b/web/api/webmachine/.hg/store/data/www/images/vclock.gif.i new file mode 100644 index 0000000..2cd7a77 Binary files /dev/null and b/web/api/webmachine/.hg/store/data/www/images/vclock.gif.i differ diff --git a/web/api/webmachine/.hg/store/data/www/index.html.i b/web/api/webmachine/.hg/store/data/www/index.html.i new file mode 100644 index 0000000..2876ba8 Binary files /dev/null and b/web/api/webmachine/.hg/store/data/www/index.html.i differ diff --git a/web/api/webmachine/.hg/store/data/www/intros.html.i b/web/api/webmachine/.hg/store/data/www/intros.html.i new file mode 100644 index 0000000..13711fa Binary files /dev/null and b/web/api/webmachine/.hg/store/data/www/intros.html.i differ diff --git a/web/api/webmachine/.hg/store/data/www/mechanics.html.i b/web/api/webmachine/.hg/store/data/www/mechanics.html.i new file mode 100644 index 0000000..5bf6f39 Binary files /dev/null and b/web/api/webmachine/.hg/store/data/www/mechanics.html.i differ diff --git a/web/api/webmachine/.hg/store/data/www/quickstart.html.i b/web/api/webmachine/.hg/store/data/www/quickstart.html.i new file mode 100644 index 0000000..b86f87a Binary files /dev/null and b/web/api/webmachine/.hg/store/data/www/quickstart.html.i differ diff --git a/web/api/webmachine/.hg/store/data/www/reftrans.html.i b/web/api/webmachine/.hg/store/data/www/reftrans.html.i new file mode 100644 index 0000000..81530d1 Binary files /dev/null and b/web/api/webmachine/.hg/store/data/www/reftrans.html.i differ diff --git a/web/api/webmachine/.hg/store/data/www/reqdata.html.i b/web/api/webmachine/.hg/store/data/www/reqdata.html.i new file mode 100644 index 0000000..f7a336a Binary files /dev/null and b/web/api/webmachine/.hg/store/data/www/reqdata.html.i differ diff --git a/web/api/webmachine/.hg/store/data/www/resources.html.i b/web/api/webmachine/.hg/store/data/www/resources.html.i new file mode 100644 index 0000000..ae7a5a6 Binary files /dev/null and b/web/api/webmachine/.hg/store/data/www/resources.html.i differ diff --git a/web/api/webmachine/.hg/store/data/www/streambody.html.i b/web/api/webmachine/.hg/store/data/www/streambody.html.i new file mode 100644 index 0000000..93ebf17 Binary files /dev/null and b/web/api/webmachine/.hg/store/data/www/streambody.html.i differ diff --git a/web/api/webmachine/.hg/store/undo b/web/api/webmachine/.hg/store/undo new file mode 100644 index 0000000..0ba1f44 Binary files /dev/null and b/web/api/webmachine/.hg/store/undo differ diff --git a/web/api/webmachine/.hg/undo.branch b/web/api/webmachine/.hg/undo.branch new file mode 100644 index 0000000..331d858 --- /dev/null +++ b/web/api/webmachine/.hg/undo.branch @@ -0,0 +1 @@ +default \ No newline at end of file diff --git a/web/api/webmachine/.hg/undo.dirstate b/web/api/webmachine/.hg/undo.dirstate new file mode 100644 index 0000000..e69de29 diff --git a/web/api/webmachine/.hgignore b/web/api/webmachine/.hgignore new file mode 100644 index 0000000..184ca18 --- /dev/null +++ b/web/api/webmachine/.hgignore @@ -0,0 +1,7 @@ +syntax:glob +*~ +*.beam +demo/priv/log/access.log.* +demo/ebin/webmachine_demo.app +deps/mochiweb/ebin/mochiweb.app +ebin/webmachine.app diff --git a/web/api/webmachine/.hgtags b/web/api/webmachine/.hgtags new file mode 100644 index 0000000..ee68409 --- /dev/null +++ b/web/api/webmachine/.hgtags @@ -0,0 +1,9 @@ +918e3d8d070c9cc6c7fa35a8654fb300af76bb7c webmachine-1.2 +8436ea7cceea89a3f4f31c7a7c50835ccea0b21e webmachine-1.3 +90d2a36b38b4d33b80d1c908eb926238c318886d webmachine-1.3 +23a5122e6343343158a242c63e33867c3e4a94ab webmachine-1.4 +23a5122e6343343158a242c63e33867c3e4a94ab webmachine-1.4 +14365a12cb1463eba5850c33617480ba2be8e686 webmachine-1.4 +14365a12cb1463eba5850c33617480ba2be8e686 webmachine-1.4 +616b08c30b4c29503063f23e9e6329c12feb3e90 webmachine-1.4 +e75bdcf6eb529d44c686fbea20dd305e6f169544 webmachine-1.5 diff --git a/web/api/webmachine/Emakefile b/web/api/webmachine/Emakefile new file mode 100644 index 0000000..7ee92ef --- /dev/null +++ b/web/api/webmachine/Emakefile @@ -0,0 +1,6 @@ +% -*- mode: erlang -*- +{["src/*"], + [{i, "include"}, + {outdir, "ebin"}, + debug_info] +}. diff --git a/web/api/webmachine/LICENSE b/web/api/webmachine/LICENSE new file mode 100644 index 0000000..e454a52 --- /dev/null +++ b/web/api/webmachine/LICENSE @@ -0,0 +1,178 @@ + + Apache License + Version 2.0, January 2004 + http://www.apache.org/licenses/ + + TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION + + 1. Definitions. + + "License" shall mean the terms and conditions for use, reproduction, + and distribution as defined by Sections 1 through 9 of this document. + + "Licensor" shall mean the copyright owner or entity authorized by + the copyright owner that is granting the License. + + "Legal Entity" shall mean the union of the acting entity and all + other entities that control, are controlled by, or are under common + control with that entity. For the purposes of this definition, + "control" means (i) the power, direct or indirect, to cause the + direction or management of such entity, whether by contract or + otherwise, or (ii) ownership of fifty percent (50%) or more of the + outstanding shares, or (iii) beneficial ownership of such entity. + + "You" (or "Your") shall mean an individual or Legal Entity + exercising permissions granted by this License. + + "Source" form shall mean the preferred form for making modifications, + including but not limited to software source code, documentation + source, and configuration files. + + "Object" form shall mean any form resulting from mechanical + transformation or translation of a Source form, including but + not limited to compiled object code, generated documentation, + and conversions to other media types. + + "Work" shall mean the work of authorship, whether in Source or + Object form, made available under the License, as indicated by a + copyright notice that is included in or attached to the work + (an example is provided in the Appendix below). + + "Derivative Works" shall mean any work, whether in Source or Object + form, that is based on (or derived from) the Work and for which the + editorial revisions, annotations, elaborations, or other modifications + represent, as a whole, an original work of authorship. For the purposes + of this License, Derivative Works shall not include works that remain + separable from, or merely link (or bind by name) to the interfaces of, + the Work and Derivative Works thereof. + + "Contribution" shall mean any work of authorship, including + the original version of the Work and any modifications or additions + to that Work or Derivative Works thereof, that is intentionally + submitted to Licensor for inclusion in the Work by the copyright owner + or by an individual or Legal Entity authorized to submit on behalf of + the copyright owner. For the purposes of this definition, "submitted" + means any form of electronic, verbal, or written communication sent + to the Licensor or its representatives, including but not limited to + communication on electronic mailing lists, source code control systems, + and issue tracking systems that are managed by, or on behalf of, the + Licensor for the purpose of discussing and improving the Work, but + excluding communication that is conspicuously marked or otherwise + designated in writing by the copyright owner as "Not a Contribution." + + "Contributor" shall mean Licensor and any individual or Legal Entity + on behalf of whom a Contribution has been received by Licensor and + subsequently incorporated within the Work. + + 2. Grant of Copyright License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + copyright license to reproduce, prepare Derivative Works of, + publicly display, publicly perform, sublicense, and distribute the + Work and such Derivative Works in Source or Object form. + + 3. Grant of Patent License. Subject to the terms and conditions of + this License, each Contributor hereby grants to You a perpetual, + worldwide, non-exclusive, no-charge, royalty-free, irrevocable + (except as stated in this section) patent license to make, have made, + use, offer to sell, sell, import, and otherwise transfer the Work, + where such license applies only to those patent claims licensable + by such Contributor that are necessarily infringed by their + Contribution(s) alone or by combination of their Contribution(s) + with the Work to which such Contribution(s) was submitted. If You + institute patent litigation against any entity (including a + cross-claim or counterclaim in a lawsuit) alleging that the Work + or a Contribution incorporated within the Work constitutes direct + or contributory patent infringement, then any patent licenses + granted to You under this License for that Work shall terminate + as of the date such litigation is filed. + + 4. Redistribution. You may reproduce and distribute copies of the + Work or Derivative Works thereof in any medium, with or without + modifications, and in Source or Object form, provided that You + meet the following conditions: + + (a) You must give any other recipients of the Work or + Derivative Works a copy of this License; and + + (b) You must cause any modified files to carry prominent notices + stating that You changed the files; and + + (c) You must retain, in the Source form of any Derivative Works + that You distribute, all copyright, patent, trademark, and + attribution notices from the Source form of the Work, + excluding those notices that do not pertain to any part of + the Derivative Works; and + + (d) If the Work includes a "NOTICE" text file as part of its + distribution, then any Derivative Works that You distribute must + include a readable copy of the attribution notices contained + within such NOTICE file, excluding those notices that do not + pertain to any part of the Derivative Works, in at least one + of the following places: within a NOTICE text file distributed + as part of the Derivative Works; within the Source form or + documentation, if provided along with the Derivative Works; or, + within a display generated by the Derivative Works, if and + wherever such third-party notices normally appear. The contents + of the NOTICE file are for informational purposes only and + do not modify the License. You may add Your own attribution + notices within Derivative Works that You distribute, alongside + or as an addendum to the NOTICE text from the Work, provided + that such additional attribution notices cannot be construed + as modifying the License. + + You may add Your own copyright statement to Your modifications and + may provide additional or different license terms and conditions + for use, reproduction, or distribution of Your modifications, or + for any such Derivative Works as a whole, provided Your use, + reproduction, and distribution of the Work otherwise complies with + the conditions stated in this License. + + 5. Submission of Contributions. Unless You explicitly state otherwise, + any Contribution intentionally submitted for inclusion in the Work + by You to the Licensor shall be under the terms and conditions of + this License, without any additional terms or conditions. + Notwithstanding the above, nothing herein shall supersede or modify + the terms of any separate license agreement you may have executed + with Licensor regarding such Contributions. + + 6. Trademarks. This License does not grant permission to use the trade + names, trademarks, service marks, or product names of the Licensor, + except as required for reasonable and customary use in describing the + origin of the Work and reproducing the content of the NOTICE file. + + 7. Disclaimer of Warranty. Unless required by applicable law or + agreed to in writing, Licensor provides the Work (and each + Contributor provides its Contributions) on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or + implied, including, without limitation, any warranties or conditions + of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A + PARTICULAR PURPOSE. You are solely responsible for determining the + appropriateness of using or redistributing the Work and assume any + risks associated with Your exercise of permissions under this License. + + 8. Limitation of Liability. In no event and under no legal theory, + whether in tort (including negligence), contract, or otherwise, + unless required by applicable law (such as deliberate and grossly + negligent acts) or agreed to in writing, shall any Contributor be + liable to You for damages, including any direct, indirect, special, + incidental, or consequential damages of any character arising as a + result of this License or out of the use or inability to use the + Work (including but not limited to damages for loss of goodwill, + work stoppage, computer failure or malfunction, or any and all + other commercial damages or losses), even if such Contributor + has been advised of the possibility of such damages. + + 9. Accepting Warranty or Additional Liability. While redistributing + the Work or Derivative Works thereof, You may choose to offer, + and charge a fee for, acceptance of support, warranty, indemnity, + or other liability obligations and/or rights consistent with this + License. However, in accepting such obligations, You may act only + on Your own behalf and on Your sole responsibility, not on behalf + of any other Contributor, and only if You agree to indemnify, + defend, and hold each Contributor harmless for any liability + incurred by, or claims asserted against, such Contributor by reason + of your accepting any such warranty or additional liability. + + END OF TERMS AND CONDITIONS + diff --git a/web/api/webmachine/Makefile b/web/api/webmachine/Makefile new file mode 100644 index 0000000..dcd9465 --- /dev/null +++ b/web/api/webmachine/Makefile @@ -0,0 +1,22 @@ +ERL ?= erl +EBIN_DIRS := $(wildcard deps/*/ebin) +APP := webmachine + +all: mochi erl erlrrd ebin/$(APP).app + +mochi: + @(cd deps/mochiweb;$(MAKE)) + +erl: + @$(ERL) -pa $(EBIN_DIRS) -noinput +B \ + -eval 'case make:all() of up_to_date -> halt(0); error -> halt(1) end.' + +edoc: + @$(ERL) -noshell -run edoc_run application '$(APP)' '"."' '[{preprocess, true},{includes, ["."]}]' + +clean: + @echo "removing:" + @rm -fv ebin/*.beam ebin/*.app + +ebin/$(APP).app: src/$(APP).app + @cp -v src/$(APP).app $@ diff --git a/web/api/webmachine/THANKS b/web/api/webmachine/THANKS new file mode 100644 index 0000000..8411a06 --- /dev/null +++ b/web/api/webmachine/THANKS @@ -0,0 +1,17 @@ +The following people have contributed to Webmachine: + +Andy Gross +Justin Sheehy +John Muellerleile +Robert Ahrens +Jeremy Latt +Bryan Fink +Ryan Tilder +Taavi Talvik +Marc Worrell +Seth Falcon +Tuncer Ayaz +Martin Scholl +Paul Mineiro +Dave Smith + diff --git a/web/api/webmachine/demo/Emakefile b/web/api/webmachine/demo/Emakefile new file mode 100644 index 0000000..7f81215 --- /dev/null +++ b/web/api/webmachine/demo/Emakefile @@ -0,0 +1,6 @@ +% -*- mode: erlang -*- +{["src/*"], + [{i, "include"}, + {outdir, "ebin"}, + debug_info] +}. diff --git a/web/api/webmachine/demo/Makefile b/web/api/webmachine/demo/Makefile new file mode 100644 index 0000000..7db1c0f --- /dev/null +++ b/web/api/webmachine/demo/Makefile @@ -0,0 +1,19 @@ +ERL ?= erl +EBIN_DIRS := $(wildcard webmachine/ebin) +APP := webmachine_demo + +all: erl ebin/$(APP).app + +erl: + @$(ERL) -pa $(EBIN_DIRS) -noinput +B \ + -eval 'case make:all() of up_to_date -> halt(0); error -> halt(1) end.' + +docs: + @erl -noshell -run edoc_run application '$(APP)' '"."' '[]' + +clean: + @echo "removing:" + @rm -fv ebin/*.beam ebin/*.app + +ebin/$(APP).app: src/$(APP).app + @cp -v src/$(APP).app $@ diff --git a/web/api/webmachine/demo/ebin/.hg_empty_dir b/web/api/webmachine/demo/ebin/.hg_empty_dir new file mode 100644 index 0000000..e69de29 diff --git a/web/api/webmachine/demo/mochiweb b/web/api/webmachine/demo/mochiweb new file mode 120000 index 0000000..9d09c64 --- /dev/null +++ b/web/api/webmachine/demo/mochiweb @@ -0,0 +1 @@ +../deps/mochiweb \ No newline at end of file diff --git a/web/api/webmachine/demo/priv/.hg_empty_dir b/web/api/webmachine/demo/priv/.hg_empty_dir new file mode 100644 index 0000000..e69de29 diff --git a/web/api/webmachine/demo/src/demo_fs_resource.erl b/web/api/webmachine/demo/src/demo_fs_resource.erl new file mode 100644 index 0000000..7331bdd --- /dev/null +++ b/web/api/webmachine/demo/src/demo_fs_resource.erl @@ -0,0 +1,157 @@ +%% @author Bryan Fink +%% @author Andy Gross +%% @author Justin Sheehy +%% @copyright 2008-2009 Basho Technologies, Inc. + +-module(demo_fs_resource). +-export([init/1]). +-export([allowed_methods/2, + resource_exists/2, + last_modified/2, + content_types_provided/2, + content_types_accepted/2, + delete_resource/2, + post_is_create/2, + create_path/2, + provide_content/2, + accept_content/2, + generate_etag/2]). + +-record(context, {root,response_body=undefined,metadata=[]}). + +-include_lib("kernel/include/file.hrl"). +-include_lib("webmachine/include/webmachine.hrl"). + +init(ConfigProps) -> + {root, Root} = proplists:lookup(root, ConfigProps), + {ok, #context{root=Root}}. + +allowed_methods(ReqData, Context) -> + {['HEAD', 'GET', 'PUT', 'DELETE', 'POST'], ReqData, Context}. + +file_path(Context, Name) -> + RelName = case hd(Name) of + "/" -> tl(Name); + _ -> Name + end, + filename:join([Context#context.root, RelName]). + +file_exists(Context, Name) -> + NamePath = file_path(Context, Name), + case filelib:is_regular(NamePath) of + true -> + {true, NamePath}; + false -> + false + end. + +resource_exists(ReqData, Context) -> + Path = wrq:disp_path(ReqData), + case file_exists(Context, Path) of + {true, _} -> + {true, ReqData, Context}; + _ -> + case Path of + "p" -> {true, ReqData, Context}; + _ -> {false, ReqData, Context} + end + end. + +maybe_fetch_object(Context, Path) -> + % if returns {true, NewContext} then NewContext has response_body + case Context#context.response_body of + undefined -> + case file_exists(Context, Path) of + {true, FullPath} -> + {ok, Value} = file:read_file(FullPath), + {true, Context#context{response_body=Value}}; + false -> + {false, Context} + end; + _Body -> + {true, Context} + end. + +content_types_provided(ReqData, Context) -> + CT = webmachine_util:guess_mime(wrq:disp_path(ReqData)), + {[{CT, provide_content}], ReqData, + Context#context{metadata=[{'content-type', CT}|Context#context.metadata]}}. + +content_types_accepted(ReqData, Context) -> + CT = case wrq:get_req_header("content-type", ReqData) of + undefined -> "application/octet-stream"; + X -> X + end, + {MT, _Params} = webmachine_util:media_type_to_detail(CT), + {[{MT, accept_content}], ReqData, + Context#context{metadata=[{'content-type', MT}|Context#context.metadata]}}. + +accept_content(ReqData, Context) -> + Path = wrq:disp_path(ReqData), + FP = file_path(Context, Path), + ok = filelib:ensure_dir(filename:dirname(FP)), + ReqData1 = case file_exists(Context, Path) of + {true, _} -> + ReqData; + _ -> + LOC = "http://" ++ + wrq:get_req_header("host", ReqData) ++ + "/fs/" ++ Path, + wrq:set_resp_header("Location", LOC, ReqData) + end, + Value = wrq:req_body(ReqData1), + case file:write_file(FP, Value) of + ok -> + {true, wrq:set_resp_body(Value, ReqData1), Context}; + Err -> + {{error, Err}, ReqData1, Context} + end. + +post_is_create(ReqData, Context) -> + {true, ReqData, Context}. + +create_path(ReqData, Context) -> + case wrq:get_req_header("slug", ReqData) of + undefined -> {undefined, ReqData, Context}; + Slug -> + case file_exists(Context, Slug) of + {true, _} -> {undefined, ReqData, Context}; + _ -> {Slug, ReqData, Context} + end + end. + +delete_resource(ReqData, Context) -> + case file:delete(file_path( + Context, wrq:disp_path(ReqData))) of + ok -> {true, ReqData, Context}; + _ -> {false, ReqData, Context} + end. + +provide_content(ReqData, Context) -> + case maybe_fetch_object(Context, wrq:disp_path(ReqData)) of + {true, NewContext} -> + Body = NewContext#context.response_body, + {Body, ReqData, Context}; + {false, NewContext} -> + {error, ReqData, NewContext} + end. + +last_modified(ReqData, Context) -> + {true, FullPath} = file_exists(Context, + wrq:disp_path(ReqData)), + LMod = filelib:last_modified(FullPath), + {LMod, ReqData, Context#context{metadata=[{'last-modified', + httpd_util:rfc1123_date(LMod)}|Context#context.metadata]}}. + +hash_body(Body) -> mochihex:to_hex(binary_to_list(crypto:sha(Body))). + +generate_etag(ReqData, Context) -> + case maybe_fetch_object(Context, wrq:disp_path(ReqData)) of + {true, BodyContext} -> + ETag = hash_body(BodyContext#context.response_body), + {ETag, ReqData, + BodyContext#context{metadata=[{etag,ETag}| + BodyContext#context.metadata]}}; + _ -> + {undefined, ReqData, Context} + end. diff --git a/web/api/webmachine/demo/src/webmachine_demo.app b/web/api/webmachine/demo/src/webmachine_demo.app new file mode 100644 index 0000000..a075b21 --- /dev/null +++ b/web/api/webmachine/demo/src/webmachine_demo.app @@ -0,0 +1,14 @@ +{application, webmachine_demo, + [{description, "webmachine_demo"}, + {vsn, "0.1"}, + {modules, [ + webmachine_demo, + webmachine_demo_app, + webmachine_demo_sup, + webmachine_demo_resource, + demo_fs_resource + ]}, + {registered, []}, + {mod, {webmachine_demo_app, []}}, + {env, []}, + {applications, [kernel, stdlib, crypto]}]}. diff --git a/web/api/webmachine/demo/src/webmachine_demo.erl b/web/api/webmachine/demo/src/webmachine_demo.erl new file mode 100644 index 0000000..aff8dc2 --- /dev/null +++ b/web/api/webmachine/demo/src/webmachine_demo.erl @@ -0,0 +1,27 @@ +-module(webmachine_demo). +-author('Andy Gross '). +-author('Justin Sheehy '). +-export([start/0, stop/0]). + +ensure_started(App) -> + case application:start(App) of + ok -> + ok; + {error, {already_started, App}} -> + ok + end. + +%% @spec start() -> ok +%% @doc Start the webmachine_demo server. +start() -> + ensure_started(crypto), + ensure_started(webmachine), + application:start(webmachine_demo). + +%% @spec stop() -> ok +%% @doc Stop the webmachine_demo server. +stop() -> + Res = application:stop(webmachine_demo), + application:stop(webmachine), + application:stop(crypto), + Res. diff --git a/web/api/webmachine/demo/src/webmachine_demo.hrl b/web/api/webmachine/demo/src/webmachine_demo.hrl new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/web/api/webmachine/demo/src/webmachine_demo.hrl @@ -0,0 +1 @@ + diff --git a/web/api/webmachine/demo/src/webmachine_demo_app.erl b/web/api/webmachine/demo/src/webmachine_demo_app.erl new file mode 100644 index 0000000..f36f22e --- /dev/null +++ b/web/api/webmachine/demo/src/webmachine_demo_app.erl @@ -0,0 +1,20 @@ +%% @author Andy Gross +%% @author Justin Sheehy + +%% @doc Callbacks for the webmachine_demo application. + +-module(webmachine_demo_app). + +-behaviour(application). +-export([start/2,stop/1]). + + +%% @spec start(_Type, _StartArgs) -> ServerRet +%% @doc application start callback for webmachine_demo. +start(_Type, _StartArgs) -> + webmachine_demo_sup:start_link(). + +%% @spec stop(_State) -> ServerRet +%% @doc application stop callback for webmachine_demo. +stop(_State) -> + ok. diff --git a/web/api/webmachine/demo/src/webmachine_demo_resource.erl b/web/api/webmachine/demo/src/webmachine_demo_resource.erl new file mode 100644 index 0000000..ccf73fc --- /dev/null +++ b/web/api/webmachine/demo/src/webmachine_demo_resource.erl @@ -0,0 +1,49 @@ +%% @author Justin Sheehy +%% @copyright 2007-2009 Basho Technologies, Inc. All Rights Reserved. +%% @doc Example webmachine_resource. + +-module(webmachine_demo_resource). +-author('Justin Sheehy '). +-export([init/1, to_html/2, to_text/2, content_types_provided/2, + is_authorized/2, generate_etag/2, expires/2]). + +-include_lib("webmachine/include/webmachine.hrl"). + +init([]) -> {ok, undefined}. + +content_types_provided(ReqData, Context) -> + {[{"text/html", to_html},{"text/plain",to_text}], ReqData, Context}. + +to_text(ReqData, Context) -> + Path = wrq:disp_path(ReqData), + Body = io_lib:format("Hello ~s from webmachine.~n", [Path]), + {Body, ReqData, Context}. + +to_html(ReqData, Context) -> + {Body, _RD, Ctx2} = to_text(ReqData, Context), + HBody = io_lib:format("~s~n", + [erlang:iolist_to_binary(Body)]), + {HBody, ReqData, Ctx2}. + +is_authorized(ReqData, Context) -> + case wrq:disp_path(ReqData) of + "authdemo" -> + case wrq:get_req_header("authorization", ReqData) of + "Basic "++Base64 -> + Str = base64:mime_decode_to_string(Base64), + case string:tokens(Str, ":") of + ["authdemo", "demo1"] -> + {true, ReqData, Context}; + _ -> + {"Basic realm=webmachine", ReqData, Context} + end; + _ -> + {"Basic realm=webmachine", ReqData, Context} + end; + _ -> {true, ReqData, Context} + end. + +expires(ReqData, Context) -> {{{2021,1,1},{0,0,0}}, ReqData, Context}. + +generate_etag(ReqData, Context) -> {wrq:raw_path(ReqData), ReqData, Context}. + diff --git a/web/api/webmachine/demo/src/webmachine_demo_sup.erl b/web/api/webmachine/demo/src/webmachine_demo_sup.erl new file mode 100644 index 0000000..8980c30 --- /dev/null +++ b/web/api/webmachine/demo/src/webmachine_demo_sup.erl @@ -0,0 +1,42 @@ +%% @author Justin Sheehy +%% @author Andy Gross +%% @copyright 2007-2008 Basho Technologies + +%% @doc Supervisor for the webmachine_demo application. + +-module(webmachine_demo_sup). +-author('Justin Sheehy '). +-author('Andy Gross '). + +-behaviour(supervisor). + +%% External exports +-export([start_link/0]). + +%% supervisor callbacks +-export([init/1]). + +%% @spec start_link() -> ServerRet +%% @doc API for starting the supervisor. +start_link() -> + supervisor:start_link({local, ?MODULE}, ?MODULE, []). + +dispatch_map() -> + [{["demo", '*'], webmachine_demo_resource, []}, + {["fs", '*'], demo_fs_resource, [{root, "/tmp/fs"}]} + ]. + +%% @doc supervisor callback. +init([]) -> + Ip = case os:getenv("WEBMACHINE_IP") of false -> "0.0.0.0"; Any -> Any end, + WebConfig = [ + {ip, Ip}, + {backlog, 1000}, + {port, 8000}, + {log_dir, "priv/log"}, + {dispatch, dispatch_map()}], + Web = {webmachine_mochiweb, + {webmachine_mochiweb, start, [WebConfig]}, + permanent, 5000, worker, dynamic}, + Processes = [Web], + {ok, {{one_for_one, 10, 10}, Processes}}. diff --git a/web/api/webmachine/demo/start.sh b/web/api/webmachine/demo/start.sh new file mode 100755 index 0000000..e0636d1 --- /dev/null +++ b/web/api/webmachine/demo/start.sh @@ -0,0 +1,3 @@ +#!/bin/sh +cd `dirname $0` +exec erl -pa $PWD/ebin $PWD/mochiweb/ebin $PWD/webmachine/ebin -boot start_sasl -s webmachine_demo diff --git a/web/api/webmachine/demo/webmachine b/web/api/webmachine/demo/webmachine new file mode 120000 index 0000000..a96aa0e --- /dev/null +++ b/web/api/webmachine/demo/webmachine @@ -0,0 +1 @@ +.. \ No newline at end of file diff --git a/web/api/webmachine/deps/mochiweb/LICENSE b/web/api/webmachine/deps/mochiweb/LICENSE new file mode 100644 index 0000000..c85b65a --- /dev/null +++ b/web/api/webmachine/deps/mochiweb/LICENSE @@ -0,0 +1,9 @@ +This is the MIT license. + +Copyright (c) 2007 Mochi Media, Inc. + +Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/web/api/webmachine/deps/mochiweb/Makefile b/web/api/webmachine/deps/mochiweb/Makefile new file mode 100644 index 0000000..2f6cace --- /dev/null +++ b/web/api/webmachine/deps/mochiweb/Makefile @@ -0,0 +1,11 @@ +all: + (cd src;$(MAKE) all) + +edoc: + (cd src;$(MAKE) edoc) + +test: + (cd src;$(MAKE) test) + +clean: + (cd src;$(MAKE) clean) diff --git a/web/api/webmachine/deps/mochiweb/README b/web/api/webmachine/deps/mochiweb/README new file mode 100644 index 0000000..f5d2c0d --- /dev/null +++ b/web/api/webmachine/deps/mochiweb/README @@ -0,0 +1 @@ +MochiWeb is an Erlang library for building lightweight HTTP servers. diff --git a/web/api/webmachine/deps/mochiweb/ebin/.hg_empty_dir b/web/api/webmachine/deps/mochiweb/ebin/.hg_empty_dir new file mode 100644 index 0000000..e69de29 diff --git a/web/api/webmachine/deps/mochiweb/ebin/mochifmt.beam b/web/api/webmachine/deps/mochiweb/ebin/mochifmt.beam new file mode 100644 index 0000000..dbdb285 Binary files /dev/null and b/web/api/webmachine/deps/mochiweb/ebin/mochifmt.beam differ diff --git a/web/api/webmachine/deps/mochiweb/ebin/mochifmt_records.beam b/web/api/webmachine/deps/mochiweb/ebin/mochifmt_records.beam new file mode 100644 index 0000000..81e34ec Binary files /dev/null and b/web/api/webmachine/deps/mochiweb/ebin/mochifmt_records.beam differ diff --git a/web/api/webmachine/deps/mochiweb/ebin/mochifmt_std.beam b/web/api/webmachine/deps/mochiweb/ebin/mochifmt_std.beam new file mode 100644 index 0000000..33f6245 Binary files /dev/null and b/web/api/webmachine/deps/mochiweb/ebin/mochifmt_std.beam differ diff --git a/web/api/webmachine/deps/mochiweb/ebin/mochihex.beam b/web/api/webmachine/deps/mochiweb/ebin/mochihex.beam new file mode 100644 index 0000000..9c19457 Binary files /dev/null and b/web/api/webmachine/deps/mochiweb/ebin/mochihex.beam differ diff --git a/web/api/webmachine/deps/mochiweb/ebin/mochijson.beam b/web/api/webmachine/deps/mochiweb/ebin/mochijson.beam new file mode 100644 index 0000000..7e24565 Binary files /dev/null and b/web/api/webmachine/deps/mochiweb/ebin/mochijson.beam differ diff --git a/web/api/webmachine/deps/mochiweb/ebin/mochijson2.beam b/web/api/webmachine/deps/mochiweb/ebin/mochijson2.beam new file mode 100644 index 0000000..4b28771 Binary files /dev/null and b/web/api/webmachine/deps/mochiweb/ebin/mochijson2.beam differ diff --git a/web/api/webmachine/deps/mochiweb/ebin/mochinum.beam b/web/api/webmachine/deps/mochiweb/ebin/mochinum.beam new file mode 100644 index 0000000..ed7a2e3 Binary files /dev/null and b/web/api/webmachine/deps/mochiweb/ebin/mochinum.beam differ diff --git a/web/api/webmachine/deps/mochiweb/ebin/mochiweb.app b/web/api/webmachine/deps/mochiweb/ebin/mochiweb.app new file mode 100644 index 0000000..cd8dbb2 --- /dev/null +++ b/web/api/webmachine/deps/mochiweb/ebin/mochiweb.app @@ -0,0 +1,32 @@ +{application, mochiweb, + [{description, "MochiMedia Web Server"}, + {vsn, "0.01"}, + {modules, [ + mochihex, + mochijson, + mochijson2, + mochinum, + mochiweb, + mochiweb_app, + mochiweb_charref, + mochiweb_cookies, + mochiweb_echo, + mochiweb_headers, + mochiweb_html, + mochiweb_http, + mochiweb_multipart, + mochiweb_request, + mochiweb_response, + mochiweb_skel, + mochiweb_socket_server, + mochiweb_sup, + mochiweb_util, + reloader, + mochifmt, + mochifmt_std, + mochifmt_records + ]}, + {registered, []}, + {mod, {mochiweb_app, []}}, + {env, []}, + {applications, [kernel, stdlib]}]}. diff --git a/web/api/webmachine/deps/mochiweb/ebin/mochiweb.beam b/web/api/webmachine/deps/mochiweb/ebin/mochiweb.beam new file mode 100644 index 0000000..eeb0f98 Binary files /dev/null and b/web/api/webmachine/deps/mochiweb/ebin/mochiweb.beam differ diff --git a/web/api/webmachine/deps/mochiweb/ebin/mochiweb_app.beam b/web/api/webmachine/deps/mochiweb/ebin/mochiweb_app.beam new file mode 100644 index 0000000..da34161 Binary files /dev/null and b/web/api/webmachine/deps/mochiweb/ebin/mochiweb_app.beam differ diff --git a/web/api/webmachine/deps/mochiweb/ebin/mochiweb_charref.beam b/web/api/webmachine/deps/mochiweb/ebin/mochiweb_charref.beam new file mode 100644 index 0000000..ecb05e9 Binary files /dev/null and b/web/api/webmachine/deps/mochiweb/ebin/mochiweb_charref.beam differ diff --git a/web/api/webmachine/deps/mochiweb/ebin/mochiweb_cookies.beam b/web/api/webmachine/deps/mochiweb/ebin/mochiweb_cookies.beam new file mode 100644 index 0000000..ca810ef Binary files /dev/null and b/web/api/webmachine/deps/mochiweb/ebin/mochiweb_cookies.beam differ diff --git a/web/api/webmachine/deps/mochiweb/ebin/mochiweb_echo.beam b/web/api/webmachine/deps/mochiweb/ebin/mochiweb_echo.beam new file mode 100644 index 0000000..171437c Binary files /dev/null and b/web/api/webmachine/deps/mochiweb/ebin/mochiweb_echo.beam differ diff --git a/web/api/webmachine/deps/mochiweb/ebin/mochiweb_headers.beam b/web/api/webmachine/deps/mochiweb/ebin/mochiweb_headers.beam new file mode 100644 index 0000000..b1a332c Binary files /dev/null and b/web/api/webmachine/deps/mochiweb/ebin/mochiweb_headers.beam differ diff --git a/web/api/webmachine/deps/mochiweb/ebin/mochiweb_html.beam b/web/api/webmachine/deps/mochiweb/ebin/mochiweb_html.beam new file mode 100644 index 0000000..00e1a2b Binary files /dev/null and b/web/api/webmachine/deps/mochiweb/ebin/mochiweb_html.beam differ diff --git a/web/api/webmachine/deps/mochiweb/ebin/mochiweb_http.beam b/web/api/webmachine/deps/mochiweb/ebin/mochiweb_http.beam new file mode 100644 index 0000000..fc8a978 Binary files /dev/null and b/web/api/webmachine/deps/mochiweb/ebin/mochiweb_http.beam differ diff --git a/web/api/webmachine/deps/mochiweb/ebin/mochiweb_multipart.beam b/web/api/webmachine/deps/mochiweb/ebin/mochiweb_multipart.beam new file mode 100644 index 0000000..b5b0bbe Binary files /dev/null and b/web/api/webmachine/deps/mochiweb/ebin/mochiweb_multipart.beam differ diff --git a/web/api/webmachine/deps/mochiweb/ebin/mochiweb_request.beam b/web/api/webmachine/deps/mochiweb/ebin/mochiweb_request.beam new file mode 100644 index 0000000..46d762d Binary files /dev/null and b/web/api/webmachine/deps/mochiweb/ebin/mochiweb_request.beam differ diff --git a/web/api/webmachine/deps/mochiweb/ebin/mochiweb_response.beam b/web/api/webmachine/deps/mochiweb/ebin/mochiweb_response.beam new file mode 100644 index 0000000..2bba8fd Binary files /dev/null and b/web/api/webmachine/deps/mochiweb/ebin/mochiweb_response.beam differ diff --git a/web/api/webmachine/deps/mochiweb/ebin/mochiweb_skel.beam b/web/api/webmachine/deps/mochiweb/ebin/mochiweb_skel.beam new file mode 100644 index 0000000..2dad632 Binary files /dev/null and b/web/api/webmachine/deps/mochiweb/ebin/mochiweb_skel.beam differ diff --git a/web/api/webmachine/deps/mochiweb/ebin/mochiweb_socket_server.beam b/web/api/webmachine/deps/mochiweb/ebin/mochiweb_socket_server.beam new file mode 100644 index 0000000..03e8a78 Binary files /dev/null and b/web/api/webmachine/deps/mochiweb/ebin/mochiweb_socket_server.beam differ diff --git a/web/api/webmachine/deps/mochiweb/ebin/mochiweb_sup.beam b/web/api/webmachine/deps/mochiweb/ebin/mochiweb_sup.beam new file mode 100644 index 0000000..d6a6947 Binary files /dev/null and b/web/api/webmachine/deps/mochiweb/ebin/mochiweb_sup.beam differ diff --git a/web/api/webmachine/deps/mochiweb/ebin/mochiweb_util.beam b/web/api/webmachine/deps/mochiweb/ebin/mochiweb_util.beam new file mode 100644 index 0000000..2d039ed Binary files /dev/null and b/web/api/webmachine/deps/mochiweb/ebin/mochiweb_util.beam differ diff --git a/web/api/webmachine/deps/mochiweb/ebin/reloader.beam b/web/api/webmachine/deps/mochiweb/ebin/reloader.beam new file mode 100644 index 0000000..75e8b16 Binary files /dev/null and b/web/api/webmachine/deps/mochiweb/ebin/reloader.beam differ diff --git a/web/api/webmachine/deps/mochiweb/priv/skel/Makefile b/web/api/webmachine/deps/mochiweb/priv/skel/Makefile new file mode 100644 index 0000000..7b44214 --- /dev/null +++ b/web/api/webmachine/deps/mochiweb/priv/skel/Makefile @@ -0,0 +1,5 @@ +all: + (cd src;$(MAKE)) + +clean: + (cd src;$(MAKE) clean) diff --git a/web/api/webmachine/deps/mochiweb/priv/skel/priv/www/index.html b/web/api/webmachine/deps/mochiweb/priv/skel/priv/www/index.html new file mode 100644 index 0000000..8e7a2c6 --- /dev/null +++ b/web/api/webmachine/deps/mochiweb/priv/skel/priv/www/index.html @@ -0,0 +1,8 @@ + + +It Worked + + +MochiWeb running. + + diff --git a/web/api/webmachine/deps/mochiweb/priv/skel/src/Makefile b/web/api/webmachine/deps/mochiweb/priv/skel/src/Makefile new file mode 100644 index 0000000..7a44a74 --- /dev/null +++ b/web/api/webmachine/deps/mochiweb/priv/skel/src/Makefile @@ -0,0 +1,9 @@ +include ../support/include.mk + +all: $(EBIN_FILES) + +debug: + $(MAKE) DEBUG=-DDEBUG + +clean: + rm -rf $(EBIN_FILES) diff --git a/web/api/webmachine/deps/mochiweb/priv/skel/src/skel.app b/web/api/webmachine/deps/mochiweb/priv/skel/src/skel.app new file mode 100644 index 0000000..fc16aae --- /dev/null +++ b/web/api/webmachine/deps/mochiweb/priv/skel/src/skel.app @@ -0,0 +1,14 @@ +{application, skel, + [{description, "skel"}, + {vsn, "0.01"}, + {modules, [ + skel, + skel_app, + skel_sup, + skel_web, + skel_deps + ]}, + {registered, []}, + {mod, {skel_app, []}}, + {env, []}, + {applications, [kernel, stdlib, crypto]}]}. diff --git a/web/api/webmachine/deps/mochiweb/priv/skel/src/skel.erl b/web/api/webmachine/deps/mochiweb/priv/skel/src/skel.erl new file mode 100644 index 0000000..49263c8 --- /dev/null +++ b/web/api/webmachine/deps/mochiweb/priv/skel/src/skel.erl @@ -0,0 +1,30 @@ +%% @author author +%% @copyright YYYY author. + +%% @doc TEMPLATE. + +-module(skel). +-author('author '). +-export([start/0, stop/0]). + +ensure_started(App) -> + case application:start(App) of + ok -> + ok; + {error, {already_started, App}} -> + ok + end. + +%% @spec start() -> ok +%% @doc Start the skel server. +start() -> + skel_deps:ensure(), + ensure_started(crypto), + application:start(skel). + +%% @spec stop() -> ok +%% @doc Stop the skel server. +stop() -> + Res = application:stop(skel), + application:stop(crypto), + Res. diff --git a/web/api/webmachine/deps/mochiweb/priv/skel/src/skel.hrl b/web/api/webmachine/deps/mochiweb/priv/skel/src/skel.hrl new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/web/api/webmachine/deps/mochiweb/priv/skel/src/skel.hrl @@ -0,0 +1 @@ + diff --git a/web/api/webmachine/deps/mochiweb/priv/skel/src/skel_app.erl b/web/api/webmachine/deps/mochiweb/priv/skel/src/skel_app.erl new file mode 100644 index 0000000..6b8228e --- /dev/null +++ b/web/api/webmachine/deps/mochiweb/priv/skel/src/skel_app.erl @@ -0,0 +1,22 @@ +%% @author author +%% @copyright YYYY author. + +%% @doc Callbacks for the skel application. + +-module(skel_app). +-author('author '). + +-behaviour(application). +-export([start/2,stop/1]). + + +%% @spec start(_Type, _StartArgs) -> ServerRet +%% @doc application start callback for skel. +start(_Type, _StartArgs) -> + skel_deps:ensure(), + skel_sup:start_link(). + +%% @spec stop(_State) -> ServerRet +%% @doc application stop callback for skel. +stop(_State) -> + ok. diff --git a/web/api/webmachine/deps/mochiweb/priv/skel/src/skel_deps.erl b/web/api/webmachine/deps/mochiweb/priv/skel/src/skel_deps.erl new file mode 100644 index 0000000..b53552d --- /dev/null +++ b/web/api/webmachine/deps/mochiweb/priv/skel/src/skel_deps.erl @@ -0,0 +1,84 @@ +%% @author author +%% @copyright YYYY author. + +%% @doc Ensure that the relatively-installed dependencies are on the code +%% loading path, and locate resources relative +%% to this application's path. + +-module(skel_deps). +-author('author '). + +-export([ensure/0, ensure/1]). +-export([get_base_dir/0, get_base_dir/1]). +-export([local_path/1, local_path/2]). +-export([deps_on_path/0, new_siblings/1]). + +%% @spec deps_on_path() -> [ProjNameAndVers] +%% @doc List of project dependencies on the path. +deps_on_path() -> + F = fun (X, Acc) -> + ProjDir = filename:dirname(X), + case {filename:basename(X), + filename:basename(filename:dirname(ProjDir))} of + {"ebin", "deps"} -> + [filename:basename(ProjDir) | Acc]; + _ -> + Acc + end + end, + ordsets:from_list(lists:foldl(F, [], code:get_path())). + +%% @spec new_siblings(Module) -> [Dir] +%% @doc Find new siblings paths relative to Module that aren't already on the +%% code path. +new_siblings(Module) -> + Existing = deps_on_path(), + SiblingEbin = filelib:wildcard(local_path(["deps", "*", "ebin"], Module)), + Siblings = [filename:dirname(X) || X <- SiblingEbin, + ordsets:is_element( + filename:basename(filename:dirname(X)), + Existing) =:= false], + lists:filter(fun filelib:is_dir/1, + lists:append([[filename:join([X, "ebin"]), + filename:join([X, "include"])] || + X <- Siblings])). + + +%% @spec ensure(Module) -> ok +%% @doc Ensure that all ebin and include paths for dependencies +%% of the application for Module are on the code path. +ensure(Module) -> + code:add_paths(new_siblings(Module)), + code:clash(), + ok. + +%% @spec ensure() -> ok +%% @doc Ensure that the ebin and include paths for dependencies of +%% this application are on the code path. Equivalent to +%% ensure(?Module). +ensure() -> + ensure(?MODULE). + +%% @spec get_base_dir(Module) -> string() +%% @doc Return the application directory for Module. It assumes Module is in +%% a standard OTP layout application in the ebin or src directory. +get_base_dir(Module) -> + {file, Here} = code:is_loaded(Module), + filename:dirname(filename:dirname(Here)). + +%% @spec get_base_dir() -> string() +%% @doc Return the application directory for this application. Equivalent to +%% get_base_dir(?MODULE). +get_base_dir() -> + get_base_dir(?MODULE). + +%% @spec local_path([string()], Module) -> string() +%% @doc Return an application-relative directory from Module's application. +local_path(Components, Module) -> + filename:join([get_base_dir(Module) | Components]). + +%% @spec local_path(Components) -> string() +%% @doc Return an application-relative directory for this application. +%% Equivalent to local_path(Components, ?MODULE). +local_path(Components) -> + local_path(Components, ?MODULE). diff --git a/web/api/webmachine/deps/mochiweb/priv/skel/src/skel_sup.erl b/web/api/webmachine/deps/mochiweb/priv/skel/src/skel_sup.erl new file mode 100644 index 0000000..92e0cae --- /dev/null +++ b/web/api/webmachine/deps/mochiweb/priv/skel/src/skel_sup.erl @@ -0,0 +1,54 @@ +%% @author author +%% @copyright YYYY author. + +%% @doc Supervisor for the skel application. + +-module(skel_sup). +-author('author '). + +-behaviour(supervisor). + +%% External exports +-export([start_link/0, upgrade/0]). + +%% supervisor callbacks +-export([init/1]). + +%% @spec start_link() -> ServerRet +%% @doc API for starting the supervisor. +start_link() -> + supervisor:start_link({local, ?MODULE}, ?MODULE, []). + +%% @spec upgrade() -> ok +%% @doc Add processes if necessary. +upgrade() -> + {ok, {_, Specs}} = init([]), + + Old = sets:from_list( + [Name || {Name, _, _, _} <- supervisor:which_children(?MODULE)]), + New = sets:from_list([Name || {Name, _, _, _, _, _} <- Specs]), + Kill = sets:subtract(Old, New), + + sets:fold(fun (Id, ok) -> + supervisor:terminate_child(?MODULE, Id), + supervisor:delete_child(?MODULE, Id), + ok + end, ok, Kill), + + [supervisor:start_child(?MODULE, Spec) || Spec <- Specs], + ok. + +%% @spec init([]) -> SupervisorTree +%% @doc supervisor callback. +init([]) -> + Ip = case os:getenv("MOCHIWEB_IP") of false -> "0.0.0.0"; Any -> Any end, + WebConfig = [ + {ip, Ip}, + {port, 8000}, + {docroot, skel_deps:local_path(["priv", "www"])}], + Web = {skel_web, + {skel_web, start, [WebConfig]}, + permanent, 5000, worker, dynamic}, + + Processes = [Web], + {ok, {{one_for_one, 10, 10}, Processes}}. diff --git a/web/api/webmachine/deps/mochiweb/priv/skel/src/skel_web.erl b/web/api/webmachine/deps/mochiweb/priv/skel/src/skel_web.erl new file mode 100644 index 0000000..3679670 --- /dev/null +++ b/web/api/webmachine/deps/mochiweb/priv/skel/src/skel_web.erl @@ -0,0 +1,43 @@ +%% @author author +%% @copyright YYYY author. + +%% @doc Web server for skel. + +-module(skel_web). +-author('author '). + +-export([start/1, stop/0, loop/2]). + +%% External API + +start(Options) -> + {DocRoot, Options1} = get_option(docroot, Options), + Loop = fun (Req) -> + ?MODULE:loop(Req, DocRoot) + end, + mochiweb_http:start([{name, ?MODULE}, {loop, Loop} | Options1]). + +stop() -> + mochiweb_http:stop(?MODULE). + +loop(Req, DocRoot) -> + "/" ++ Path = Req:get(path), + case Req:get(method) of + Method when Method =:= 'GET'; Method =:= 'HEAD' -> + case Path of + _ -> + Req:serve_file(Path, DocRoot) + end; + 'POST' -> + case Path of + _ -> + Req:not_found() + end; + _ -> + Req:respond({501, [], []}) + end. + +%% Internal API + +get_option(Option, Options) -> + {proplists:get_value(Option, Options), proplists:delete(Option, Options)}. diff --git a/web/api/webmachine/deps/mochiweb/priv/skel/start-dev.sh b/web/api/webmachine/deps/mochiweb/priv/skel/start-dev.sh new file mode 100755 index 0000000..6f02962 --- /dev/null +++ b/web/api/webmachine/deps/mochiweb/priv/skel/start-dev.sh @@ -0,0 +1,3 @@ +#!/bin/sh +cd `dirname $0` +exec erl -pa $PWD/ebin $PWD/deps/*/ebin -boot start_sasl -s reloader -s skel diff --git a/web/api/webmachine/deps/mochiweb/priv/skel/start.sh b/web/api/webmachine/deps/mochiweb/priv/skel/start.sh new file mode 100755 index 0000000..599f8e6 --- /dev/null +++ b/web/api/webmachine/deps/mochiweb/priv/skel/start.sh @@ -0,0 +1,3 @@ +#!/bin/sh +cd `dirname $0` +exec erl -pa $PWD/ebin $PWD/deps/*/ebin -boot start_sasl -s skel diff --git a/web/api/webmachine/deps/mochiweb/priv/skel/support/include.mk b/web/api/webmachine/deps/mochiweb/priv/skel/support/include.mk new file mode 100644 index 0000000..64b50fe --- /dev/null +++ b/web/api/webmachine/deps/mochiweb/priv/skel/support/include.mk @@ -0,0 +1,46 @@ +## -*- makefile -*- + +###################################################################### +## Erlang + +ERL := erl +ERLC := $(ERL)c + +INCLUDE_DIRS := ../include $(wildcard ../deps/*/include) +EBIN_DIRS := $(wildcard ../deps/*/ebin) +ERLC_FLAGS := -W $(INCLUDE_DIRS:../%=-I ../%) $(EBIN_DIRS:%=-pa %) + +ifndef no_debug_info + ERLC_FLAGS += +debug_info +endif + +ifdef debug + ERLC_FLAGS += -Ddebug +endif + +EBIN_DIR := ../ebin +DOC_DIR := ../doc +EMULATOR := beam + +ERL_SOURCES := $(wildcard *.erl) +ERL_HEADERS := $(wildcard *.hrl) $(wildcard ../include/*.hrl) +ERL_OBJECTS := $(ERL_SOURCES:%.erl=$(EBIN_DIR)/%.$(EMULATOR)) +ERL_DOCUMENTS := $(ERL_SOURCES:%.erl=$(DOC_DIR)/%.html) +ERL_OBJECTS_LOCAL := $(ERL_SOURCES:%.erl=./%.$(EMULATOR)) +APP_FILES := $(wildcard *.app) +EBIN_FILES = $(ERL_OBJECTS) $(ERL_DOCUMENTS) $(APP_FILES:%.app=../ebin/%.app) +EBIN_FILES_NO_DOCS = $(ERL_OBJECTS) $(APP_FILES:%.app=../ebin/%.app) +MODULES = $(ERL_SOURCES:%.erl=%) + +../ebin/%.app: %.app + cp $< $@ + +$(EBIN_DIR)/%.$(EMULATOR): %.erl + $(ERLC) $(ERLC_FLAGS) -o $(EBIN_DIR) $< + +./%.$(EMULATOR): %.erl + $(ERLC) $(ERLC_FLAGS) -o . $< + +$(DOC_DIR)/%.html: %.erl + $(ERL) -noshell -run edoc file $< -run init stop + mv *.html $(DOC_DIR) diff --git a/web/api/webmachine/deps/mochiweb/scripts/new_mochiweb.erl b/web/api/webmachine/deps/mochiweb/scripts/new_mochiweb.erl new file mode 100644 index 0000000..918f01e --- /dev/null +++ b/web/api/webmachine/deps/mochiweb/scripts/new_mochiweb.erl @@ -0,0 +1,27 @@ +#!/usr/bin/env escript +%% -*- mode: erlang -*- +-export([main/1]). + +%% External API + +main([Name]) -> + main([Name, "."]); +main([Name, Dest]) -> + ensure(), + DestDir = filename:absname(Dest), + ok = mochiweb_skel:skelcopy(DestDir, Name); +main(_) -> + usage(). + +%% Internal API + +ensure() -> + code:add_patha(filename:join(filename:dirname(escript:script_name()), + "../ebin")). + +usage() -> + io:format("usage: ~s name [destdir]~n", + [filename:basename(escript:script_name())]), + halt(1). + + diff --git a/web/api/webmachine/deps/mochiweb/src/Makefile b/web/api/webmachine/deps/mochiweb/src/Makefile new file mode 100644 index 0000000..8e05398 --- /dev/null +++ b/web/api/webmachine/deps/mochiweb/src/Makefile @@ -0,0 +1,20 @@ +include ../support/include.mk + +APPLICATION=mochiweb +DOC_OPTS={dir,\"../doc\"} + +all: $(EBIN_FILES) + +debug: + $(MAKE) DEBUG=-DDEBUG + +clean: + rm -rf $(EBIN_FILES) + +edoc: + $(ERL) -noshell -pa ../ebin \ + -eval "edoc:application($(APPLICATION), \".\", [$(DOC_OPTS)])" \ + -s init stop + +test: all + $(ERL) -noshell -pa ../ebin -s $(APPLICATION) test -s init stop diff --git a/web/api/webmachine/deps/mochiweb/src/mochifmt.erl b/web/api/webmachine/deps/mochiweb/src/mochifmt.erl new file mode 100644 index 0000000..da0a133 --- /dev/null +++ b/web/api/webmachine/deps/mochiweb/src/mochifmt.erl @@ -0,0 +1,426 @@ +%% @author Bob Ippolito +%% @copyright 2008 Mochi Media, Inc. + +%% @doc String Formatting for Erlang, inspired by Python 2.6 +%% (PEP 3101). +%% +-module(mochifmt). +-author('bob@mochimedia.com'). +-export([format/2, format_field/2, convert_field/2, get_value/2, get_field/2]). +-export([tokenize/1, format/3, get_field/3, format_field/3]). +-export([bformat/2, bformat/3]). +-export([f/2, f/3]). +-export([test/0]). + +-record(conversion, {length, precision, ctype, align, fill_char, sign}). + +%% @spec tokenize(S::string()) -> tokens() +%% @doc Tokenize a format string into mochifmt's internal format. +tokenize(S) -> + {?MODULE, tokenize(S, "", [])}. + +%% @spec convert_field(Arg, Conversion::conversion()) -> term() +%% @doc Process Arg according to the given explicit conversion specifier. +convert_field(Arg, "") -> + Arg; +convert_field(Arg, "r") -> + repr(Arg); +convert_field(Arg, "s") -> + str(Arg). + +%% @spec get_value(Key::string(), Args::args()) -> term() +%% @doc Get the Key from Args. If Args is a tuple then convert Key to +%% an integer and get element(1 + Key, Args). If Args is a list and Key +%% can be parsed as an integer then use lists:nth(1 + Key, Args), +%% otherwise try and look for Key in Args as a proplist, converting +%% Key to an atom or binary if necessary. +get_value(Key, Args) when is_tuple(Args) -> + element(1 + list_to_integer(Key), Args); +get_value(Key, Args) when is_list(Args) -> + try lists:nth(1 + list_to_integer(Key), Args) + catch error:_ -> + {_K, V} = proplist_lookup(Key, Args), + V + end. + +%% @spec get_field(Key::string(), Args) -> term() +%% @doc Consecutively call get_value/2 on parts of Key delimited by ".", +%% replacing Args with the result of the previous get_value. This +%% is used to implement formats such as {0.0}. +get_field(Key, Args) -> + get_field(Key, Args, ?MODULE). + +%% @spec get_field(Key::string(), Args, Module) -> term() +%% @doc Consecutively call Module:get_value/2 on parts of Key delimited by ".", +%% replacing Args with the result of the previous get_value. This +%% is used to implement formats such as {0.0}. +get_field(Key, Args, Module) -> + {Name, Next} = lists:splitwith(fun (C) -> C =/= $. end, Key), + Res = try Module:get_value(Name, Args) + catch error:undef -> get_value(Name, Args) end, + case Next of + "" -> + Res; + "." ++ S1 -> + get_field(S1, Res, Module) + end. + +%% @spec format(Format::string(), Args) -> iolist() +%% @doc Format Args with Format. +format(Format, Args) -> + format(Format, Args, ?MODULE). + +%% @spec format(Format::string(), Args, Module) -> iolist() +%% @doc Format Args with Format using Module. +format({?MODULE, Parts}, Args, Module) -> + format2(Parts, Args, Module, []); +format(S, Args, Module) -> + format(tokenize(S), Args, Module). + +%% @spec format_field(Arg, Format) -> iolist() +%% @doc Format Arg with Format. +format_field(Arg, Format) -> + format_field(Arg, Format, ?MODULE). + +%% @spec format_field(Arg, Format, _Module) -> iolist() +%% @doc Format Arg with Format. +format_field(Arg, Format, _Module) -> + F = default_ctype(Arg, parse_std_conversion(Format)), + fix_padding(fix_sign(convert2(Arg, F), F), F). + +%% @spec f(Format::string(), Args) -> string() +%% @doc Format Args with Format and return a string(). +f(Format, Args) -> + f(Format, Args, ?MODULE). + +%% @spec f(Format::string(), Args, Module) -> string() +%% @doc Format Args with Format using Module and return a string(). +f(Format, Args, Module) -> + case lists:member(${, Format) of + true -> + binary_to_list(bformat(Format, Args, Module)); + false -> + Format + end. + +%% @spec bformat(Format::string(), Args) -> binary() +%% @doc Format Args with Format and return a binary(). +bformat(Format, Args) -> + iolist_to_binary(format(Format, Args)). + +%% @spec bformat(Format::string(), Args, Module) -> binary() +%% @doc Format Args with Format using Module and return a binary(). +bformat(Format, Args, Module) -> + iolist_to_binary(format(Format, Args, Module)). + +%% @spec test() -> ok +%% @doc Run tests. +test() -> + ok = test_tokenize(), + ok = test_format(), + ok = test_std(), + ok = test_records(), + ok. + +%% Internal API + +add_raw("", Acc) -> + Acc; +add_raw(S, Acc) -> + [{raw, lists:reverse(S)} | Acc]. + +tokenize([], S, Acc) -> + lists:reverse(add_raw(S, Acc)); +tokenize("{{" ++ Rest, S, Acc) -> + tokenize(Rest, "{" ++ S, Acc); +tokenize("{" ++ Rest, S, Acc) -> + {Format, Rest1} = tokenize_format(Rest), + tokenize(Rest1, "", [{format, make_format(Format)} | add_raw(S, Acc)]); +tokenize("}}" ++ Rest, S, Acc) -> + tokenize(Rest, "}" ++ S, Acc); +tokenize([C | Rest], S, Acc) -> + tokenize(Rest, [C | S], Acc). + +tokenize_format(S) -> + tokenize_format(S, 1, []). + +tokenize_format("}" ++ Rest, 1, Acc) -> + {lists:reverse(Acc), Rest}; +tokenize_format("}" ++ Rest, N, Acc) -> + tokenize_format(Rest, N - 1, "}" ++ Acc); +tokenize_format("{" ++ Rest, N, Acc) -> + tokenize_format(Rest, 1 + N, "{" ++ Acc); +tokenize_format([C | Rest], N, Acc) -> + tokenize_format(Rest, N, [C | Acc]). + +make_format(S) -> + {Name0, Spec} = case lists:splitwith(fun (C) -> C =/= $: end, S) of + {_, ""} -> + {S, ""}; + {SN, ":" ++ SS} -> + {SN, SS} + end, + {Name, Transform} = case lists:splitwith(fun (C) -> C =/= $! end, Name0) of + {_, ""} -> + {Name0, ""}; + {TN, "!" ++ TT} -> + {TN, TT} + end, + {Name, Transform, Spec}. + +proplist_lookup(S, P) -> + A = try list_to_existing_atom(S) + catch error:_ -> make_ref() end, + B = try list_to_binary(S) + catch error:_ -> make_ref() end, + proplist_lookup2({S, A, B}, P). + +proplist_lookup2({KS, KA, KB}, [{K, V} | _]) + when KS =:= K orelse KA =:= K orelse KB =:= K -> + {K, V}; +proplist_lookup2(Keys, [_ | Rest]) -> + proplist_lookup2(Keys, Rest). + +format2([], _Args, _Module, Acc) -> + lists:reverse(Acc); +format2([{raw, S} | Rest], Args, Module, Acc) -> + format2(Rest, Args, Module, [S | Acc]); +format2([{format, {Key, Convert, Format0}} | Rest], Args, Module, Acc) -> + Format = f(Format0, Args, Module), + V = case Module of + ?MODULE -> + V0 = get_field(Key, Args), + V1 = convert_field(V0, Convert), + format_field(V1, Format); + _ -> + V0 = try Module:get_field(Key, Args) + catch error:undef -> get_field(Key, Args, Module) end, + V1 = try Module:convert_field(V0, Convert) + catch error:undef -> convert_field(V0, Convert) end, + try Module:format_field(V1, Format) + catch error:undef -> format_field(V1, Format, Module) end + end, + format2(Rest, Args, Module, [V | Acc]). + +default_ctype(_Arg, C=#conversion{ctype=N}) when N =/= undefined -> + C; +default_ctype(Arg, C) when is_integer(Arg) -> + C#conversion{ctype=decimal}; +default_ctype(Arg, C) when is_float(Arg) -> + C#conversion{ctype=general}; +default_ctype(_Arg, C) -> + C#conversion{ctype=string}. + +fix_padding(Arg, #conversion{length=undefined}) -> + Arg; +fix_padding(Arg, F=#conversion{length=Length, fill_char=Fill0, align=Align0, + ctype=Type}) -> + Padding = Length - iolist_size(Arg), + Fill = case Fill0 of + undefined -> + $\s; + _ -> + Fill0 + end, + Align = case Align0 of + undefined -> + case Type of + string -> + left; + _ -> + right + end; + _ -> + Align0 + end, + case Padding > 0 of + true -> + do_padding(Arg, Padding, Fill, Align, F); + false -> + Arg + end. + +do_padding(Arg, Padding, Fill, right, _F) -> + [lists:duplicate(Padding, Fill), Arg]; +do_padding(Arg, Padding, Fill, center, _F) -> + LPadding = lists:duplicate(Padding div 2, Fill), + RPadding = case Padding band 1 of + 1 -> + [Fill | LPadding]; + _ -> + LPadding + end, + [LPadding, Arg, RPadding]; +do_padding([$- | Arg], Padding, Fill, sign_right, _F) -> + [[$- | lists:duplicate(Padding, Fill)], Arg]; +do_padding(Arg, Padding, Fill, sign_right, #conversion{sign=$-}) -> + [lists:duplicate(Padding, Fill), Arg]; +do_padding([S | Arg], Padding, Fill, sign_right, #conversion{sign=S}) -> + [[S | lists:duplicate(Padding, Fill)], Arg]; +do_padding(Arg, Padding, Fill, sign_right, #conversion{sign=undefined}) -> + [lists:duplicate(Padding, Fill), Arg]; +do_padding(Arg, Padding, Fill, left, _F) -> + [Arg | lists:duplicate(Padding, Fill)]. + +fix_sign(Arg, #conversion{sign=$+}) when Arg >= 0 -> + [$+, Arg]; +fix_sign(Arg, #conversion{sign=$\s}) when Arg >= 0 -> + [$\s, Arg]; +fix_sign(Arg, _F) -> + Arg. + +ctype($\%) -> percent; +ctype($s) -> string; +ctype($b) -> bin; +ctype($o) -> oct; +ctype($X) -> upper_hex; +ctype($x) -> hex; +ctype($c) -> char; +ctype($d) -> decimal; +ctype($g) -> general; +ctype($f) -> fixed; +ctype($e) -> exp. + +align($<) -> left; +align($>) -> right; +align($^) -> center; +align($=) -> sign_right. + +convert2(Arg, F=#conversion{ctype=percent}) -> + [convert2(100.0 * Arg, F#conversion{ctype=fixed}), $\%]; +convert2(Arg, #conversion{ctype=string}) -> + str(Arg); +convert2(Arg, #conversion{ctype=bin}) -> + erlang:integer_to_list(Arg, 2); +convert2(Arg, #conversion{ctype=oct}) -> + erlang:integer_to_list(Arg, 8); +convert2(Arg, #conversion{ctype=upper_hex}) -> + erlang:integer_to_list(Arg, 16); +convert2(Arg, #conversion{ctype=hex}) -> + string:to_lower(erlang:integer_to_list(Arg, 16)); +convert2(Arg, #conversion{ctype=char}) when Arg < 16#80 -> + [Arg]; +convert2(Arg, #conversion{ctype=char}) -> + xmerl_ucs:to_utf8(Arg); +convert2(Arg, #conversion{ctype=decimal}) -> + integer_to_list(Arg); +convert2(Arg, #conversion{ctype=general, precision=undefined}) -> + try mochinum:digits(Arg) + catch error:undef -> io_lib:format("~g", [Arg]) end; +convert2(Arg, #conversion{ctype=fixed, precision=undefined}) -> + io_lib:format("~f", [Arg]); +convert2(Arg, #conversion{ctype=exp, precision=undefined}) -> + io_lib:format("~e", [Arg]); +convert2(Arg, #conversion{ctype=general, precision=P}) -> + io_lib:format("~." ++ integer_to_list(P) ++ "g", [Arg]); +convert2(Arg, #conversion{ctype=fixed, precision=P}) -> + io_lib:format("~." ++ integer_to_list(P) ++ "f", [Arg]); +convert2(Arg, #conversion{ctype=exp, precision=P}) -> + io_lib:format("~." ++ integer_to_list(P) ++ "e", [Arg]). + +str(A) when is_atom(A) -> + atom_to_list(A); +str(I) when is_integer(I) -> + integer_to_list(I); +str(F) when is_float(F) -> + try mochinum:digits(F) + catch error:undef -> io_lib:format("~g", [F]) end; +str(L) when is_list(L) -> + L; +str(B) when is_binary(B) -> + B; +str(P) -> + repr(P). + +repr(P) when is_float(P) -> + try mochinum:digits(P) + catch error:undef -> float_to_list(P) end; +repr(P) -> + io_lib:format("~p", [P]). + +parse_std_conversion(S) -> + parse_std_conversion(S, #conversion{}). + +parse_std_conversion("", Acc) -> + Acc; +parse_std_conversion([Fill, Align | Spec], Acc) + when Align =:= $< orelse Align =:= $> orelse Align =:= $= orelse Align =:= $^ -> + parse_std_conversion(Spec, Acc#conversion{fill_char=Fill, + align=align(Align)}); +parse_std_conversion([Align | Spec], Acc) + when Align =:= $< orelse Align =:= $> orelse Align =:= $= orelse Align =:= $^ -> + parse_std_conversion(Spec, Acc#conversion{align=align(Align)}); +parse_std_conversion([Sign | Spec], Acc) + when Sign =:= $+ orelse Sign =:= $- orelse Sign =:= $\s -> + parse_std_conversion(Spec, Acc#conversion{sign=Sign}); +parse_std_conversion("0" ++ Spec, Acc) -> + Align = case Acc#conversion.align of + undefined -> + sign_right; + A -> + A + end, + parse_std_conversion(Spec, Acc#conversion{fill_char=$0, align=Align}); +parse_std_conversion(Spec=[D|_], Acc) when D >= $0 andalso D =< $9 -> + {W, Spec1} = lists:splitwith(fun (C) -> C >= $0 andalso C =< $9 end, Spec), + parse_std_conversion(Spec1, Acc#conversion{length=list_to_integer(W)}); +parse_std_conversion([$. | Spec], Acc) -> + case lists:splitwith(fun (C) -> C >= $0 andalso C =< $9 end, Spec) of + {"", Spec1} -> + parse_std_conversion(Spec1, Acc); + {P, Spec1} -> + parse_std_conversion(Spec1, + Acc#conversion{precision=list_to_integer(P)}) + end; +parse_std_conversion([Type], Acc) -> + parse_std_conversion("", Acc#conversion{ctype=ctype(Type)}). + +test_tokenize() -> + {?MODULE, [{raw, "ABC"}]} = tokenize("ABC"), + {?MODULE, [{format, {"0", "", ""}}]} = tokenize("{0}"), + {?MODULE, [{raw, "ABC"}, {format, {"1", "", ""}}, {raw, "DEF"}]} = + tokenize("ABC{1}DEF"), + ok. + +test_format() -> + <<" -4">> = bformat("{0:4}", [-4]), + <<" 4">> = bformat("{0:4}", [4]), + <<" 4">> = bformat("{0:{0}}", [4]), + <<"4 ">> = bformat("{0:4}", ["4"]), + <<"4 ">> = bformat("{0:{0}}", ["4"]), + <<"1.2yoDEF">> = bformat("{2}{0}{1}{3}", {yo, "DE", 1.2, <<"F">>}), + <<"cafebabe">> = bformat("{0:x}", {16#cafebabe}), + <<"CAFEBABE">> = bformat("{0:X}", {16#cafebabe}), + <<"CAFEBABE">> = bformat("{0:X}", {16#cafebabe}), + <<"755">> = bformat("{0:o}", {8#755}), + <<"a">> = bformat("{0:c}", {97}), + %% Horizontal ellipsis + <<226, 128, 166>> = bformat("{0:c}", {16#2026}), + <<"11">> = bformat("{0:b}", {3}), + <<"11">> = bformat("{0:b}", [3]), + <<"11">> = bformat("{three:b}", [{three, 3}]), + <<"11">> = bformat("{three:b}", [{"three", 3}]), + <<"11">> = bformat("{three:b}", [{<<"three">>, 3}]), + <<"\"foo\"">> = bformat("{0!r}", {"foo"}), + <<"2008-5-4">> = bformat("{0.0}-{0.1}-{0.2}", {{2008,5,4}}), + <<"2008-05-04">> = bformat("{0.0:04}-{0.1:02}-{0.2:02}", {{2008,5,4}}), + <<"foo6bar-6">> = bformat("foo{1}{0}-{1}", {bar, 6}), + <<"-'atom test'-">> = bformat("-{arg!r}-", [{arg, 'atom test'}]), + <<"2008-05-04">> = bformat("{0.0:0{1.0}}-{0.1:0{1.1}}-{0.2:0{1.2}}", + {{2008,5,4}, {4, 2, 2}}), + ok. + +test_std() -> + M = mochifmt_std:new(), + <<"01">> = bformat("{0}{1}", [0, 1], M), + ok. + +test_records() -> + M = mochifmt_records:new([{conversion, record_info(fields, conversion)}]), + R = #conversion{length=long, precision=hard, sign=peace}, + long = M:get_value("length", R), + hard = M:get_value("precision", R), + peace = M:get_value("sign", R), + <<"long hard">> = bformat("{length} {precision}", R, M), + <<"long hard">> = bformat("{0.length} {0.precision}", [R], M), + ok. diff --git a/web/api/webmachine/deps/mochiweb/src/mochifmt_records.erl b/web/api/webmachine/deps/mochiweb/src/mochifmt_records.erl new file mode 100644 index 0000000..94c7797 --- /dev/null +++ b/web/api/webmachine/deps/mochiweb/src/mochifmt_records.erl @@ -0,0 +1,30 @@ +%% @author Bob Ippolito +%% @copyright 2008 Mochi Media, Inc. + +%% @doc Formatter that understands records. +%% +%% Usage: +%% +%% 1> M = mochifmt_records:new([{rec, record_info(fields, rec)}]), +%% M:format("{0.bar}", [#rec{bar=foo}]). +%% foo + +-module(mochifmt_records, [Recs]). +-author('bob@mochimedia.com'). +-export([get_value/2]). + +get_value(Key, Rec) when is_tuple(Rec) and is_atom(element(1, Rec)) -> + try begin + Atom = list_to_existing_atom(Key), + {_, Fields} = proplists:lookup(element(1, Rec), Recs), + element(get_rec_index(Atom, Fields, 2), Rec) + end + catch error:_ -> mochifmt:get_value(Key, Rec) + end; +get_value(Key, Args) -> + mochifmt:get_value(Key, Args). + +get_rec_index(Atom, [Atom | _], Index) -> + Index; +get_rec_index(Atom, [_ | Rest], Index) -> + get_rec_index(Atom, Rest, 1 + Index). diff --git a/web/api/webmachine/deps/mochiweb/src/mochifmt_std.erl b/web/api/webmachine/deps/mochiweb/src/mochifmt_std.erl new file mode 100644 index 0000000..9442016 --- /dev/null +++ b/web/api/webmachine/deps/mochiweb/src/mochifmt_std.erl @@ -0,0 +1,23 @@ +%% @author Bob Ippolito +%% @copyright 2008 Mochi Media, Inc. + +%% @doc Template module for a mochifmt formatter. + +-module(mochifmt_std, []). +-author('bob@mochimedia.com'). +-export([format/2, get_value/2, format_field/2, get_field/2, convert_field/2]). + +format(Format, Args) -> + mochifmt:format(Format, Args, THIS). + +get_field(Key, Args) -> + mochifmt:get_field(Key, Args, THIS). + +convert_field(Key, Args) -> + mochifmt:convert_field(Key, Args). + +get_value(Key, Args) -> + mochifmt:get_value(Key, Args). + +format_field(Arg, Format) -> + mochifmt:format_field(Arg, Format, THIS). diff --git a/web/api/webmachine/deps/mochiweb/src/mochihex.erl b/web/api/webmachine/deps/mochiweb/src/mochihex.erl new file mode 100644 index 0000000..7fe6899 --- /dev/null +++ b/web/api/webmachine/deps/mochiweb/src/mochihex.erl @@ -0,0 +1,75 @@ +%% @author Bob Ippolito +%% @copyright 2006 Mochi Media, Inc. + +%% @doc Utilities for working with hexadecimal strings. + +-module(mochihex). +-author('bob@mochimedia.com'). + +-export([test/0, to_hex/1, to_bin/1, to_int/1, dehex/1, hexdigit/1]). + +%% @type iolist() = [char() | binary() | iolist()] +%% @type iodata() = iolist() | binary() + +%% @spec to_hex(integer | iolist()) -> string() +%% @doc Convert an iolist to a hexadecimal string. +to_hex(0) -> + "0"; +to_hex(I) when is_integer(I), I > 0 -> + to_hex_int(I, []); +to_hex(B) -> + to_hex(iolist_to_binary(B), []). + +%% @spec to_bin(string()) -> binary() +%% @doc Convert a hexadecimal string to a binary. +to_bin(L) -> + to_bin(L, []). + +%% @spec to_int(string()) -> integer() +%% @doc Convert a hexadecimal string to an integer. +to_int(L) -> + erlang:list_to_integer(L, 16). + +%% @spec dehex(char()) -> integer() +%% @doc Convert a hex digit to its integer value. +dehex(C) when C >= $0, C =< $9 -> + C - $0; +dehex(C) when C >= $a, C =< $f -> + C - $a + 10; +dehex(C) when C >= $A, C =< $F -> + C - $A + 10. + +%% @spec hexdigit(integer()) -> char() +%% @doc Convert an integer less than 16 to a hex digit. +hexdigit(C) when C >= 0, C =< 9 -> + C + $0; +hexdigit(C) when C =< 15 -> + C + $a - 10. + +%% @spec test() -> ok +%% @doc Test this module. +test() -> + "ff000ff1" = to_hex([255, 0, 15, 241]), + <<255, 0, 15, 241>> = to_bin("ff000ff1"), + 16#ff000ff1 = to_int("ff000ff1"), + "ff000ff1" = to_hex(16#ff000ff1), + ok. + + +%% Internal API + +to_hex(<<>>, Acc) -> + lists:reverse(Acc); +to_hex(<>, Acc) -> + to_hex(Rest, [hexdigit(C2), hexdigit(C1) | Acc]). + +to_hex_int(0, Acc) -> + Acc; +to_hex_int(I, Acc) -> + to_hex_int(I bsr 4, [hexdigit(I band 15) | Acc]). + +to_bin([], Acc) -> + iolist_to_binary(lists:reverse(Acc)); +to_bin([C1, C2 | Rest], Acc) -> + to_bin(Rest, [(dehex(C1) bsl 4) bor dehex(C2) | Acc]). + diff --git a/web/api/webmachine/deps/mochiweb/src/mochijson.erl b/web/api/webmachine/deps/mochiweb/src/mochijson.erl new file mode 100644 index 0000000..d1ad3fa --- /dev/null +++ b/web/api/webmachine/deps/mochiweb/src/mochijson.erl @@ -0,0 +1,533 @@ +%% @author Bob Ippolito +%% @copyright 2006 Mochi Media, Inc. + +%% @doc Yet another JSON (RFC 4627) library for Erlang. +-module(mochijson). +-author('bob@mochimedia.com'). +-export([encoder/1, encode/1]). +-export([decoder/1, decode/1]). +-export([binary_encoder/1, binary_encode/1]). +-export([binary_decoder/1, binary_decode/1]). +-export([test/0]). + +% This is a macro to placate syntax highlighters.. +-define(Q, $\"). +-define(ADV_COL(S, N), S#decoder{column=N+S#decoder.column}). +-define(INC_COL(S), S#decoder{column=1+S#decoder.column}). +-define(INC_LINE(S), S#decoder{column=1, line=1+S#decoder.line}). + +%% @type iolist() = [char() | binary() | iolist()] +%% @type iodata() = iolist() | binary() +%% @type json_string() = atom | string() | binary() +%% @type json_number() = integer() | float() +%% @type json_array() = {array, [json_term()]} +%% @type json_object() = {struct, [{json_string(), json_term()}]} +%% @type json_term() = json_string() | json_number() | json_array() | +%% json_object() +%% @type encoding() = utf8 | unicode +%% @type encoder_option() = {input_encoding, encoding()} | +%% {handler, function()} +%% @type decoder_option() = {input_encoding, encoding()} | +%% {object_hook, function()} +%% @type bjson_string() = binary() +%% @type bjson_number() = integer() | float() +%% @type bjson_array() = [bjson_term()] +%% @type bjson_object() = {struct, [{bjson_string(), bjson_term()}]} +%% @type bjson_term() = bjson_string() | bjson_number() | bjson_array() | +%% bjson_object() +%% @type binary_encoder_option() = {handler, function()} +%% @type binary_decoder_option() = {object_hook, function()} + +-record(encoder, {input_encoding=unicode, + handler=null}). + +-record(decoder, {input_encoding=utf8, + object_hook=null, + line=1, + column=1, + state=null}). + +%% @spec encoder([encoder_option()]) -> function() +%% @doc Create an encoder/1 with the given options. +encoder(Options) -> + State = parse_encoder_options(Options, #encoder{}), + fun (O) -> json_encode(O, State) end. + +%% @spec encode(json_term()) -> iolist() +%% @doc Encode the given as JSON to an iolist. +encode(Any) -> + json_encode(Any, #encoder{}). + +%% @spec decoder([decoder_option()]) -> function() +%% @doc Create a decoder/1 with the given options. +decoder(Options) -> + State = parse_decoder_options(Options, #decoder{}), + fun (O) -> json_decode(O, State) end. + +%% @spec decode(iolist()) -> json_term() +%% @doc Decode the given iolist to Erlang terms. +decode(S) -> + json_decode(S, #decoder{}). + +%% @spec binary_decoder([binary_decoder_option()]) -> function() +%% @doc Create a binary_decoder/1 with the given options. +binary_decoder(Options) -> + mochijson2:decoder(Options). + +%% @spec binary_encoder([binary_encoder_option()]) -> function() +%% @doc Create a binary_encoder/1 with the given options. +binary_encoder(Options) -> + mochijson2:encoder(Options). + +%% @spec binary_encode(bjson_term()) -> iolist() +%% @doc Encode the given as JSON to an iolist, using lists for arrays and +%% binaries for strings. +binary_encode(Any) -> + mochijson2:encode(Any). + +%% @spec binary_decode(iolist()) -> bjson_term() +%% @doc Decode the given iolist to Erlang terms, using lists for arrays and +%% binaries for strings. +binary_decode(S) -> + mochijson2:decode(S). + +test() -> + test_all(), + mochijson2:test(). + +%% Internal API + +parse_encoder_options([], State) -> + State; +parse_encoder_options([{input_encoding, Encoding} | Rest], State) -> + parse_encoder_options(Rest, State#encoder{input_encoding=Encoding}); +parse_encoder_options([{handler, Handler} | Rest], State) -> + parse_encoder_options(Rest, State#encoder{handler=Handler}). + +parse_decoder_options([], State) -> + State; +parse_decoder_options([{input_encoding, Encoding} | Rest], State) -> + parse_decoder_options(Rest, State#decoder{input_encoding=Encoding}); +parse_decoder_options([{object_hook, Hook} | Rest], State) -> + parse_decoder_options(Rest, State#decoder{object_hook=Hook}). + +json_encode(true, _State) -> + "true"; +json_encode(false, _State) -> + "false"; +json_encode(null, _State) -> + "null"; +json_encode(I, _State) when is_integer(I) -> + integer_to_list(I); +json_encode(F, _State) when is_float(F) -> + mochinum:digits(F); +json_encode(L, State) when is_list(L); is_binary(L); is_atom(L) -> + json_encode_string(L, State); +json_encode({array, Props}, State) when is_list(Props) -> + json_encode_array(Props, State); +json_encode({struct, Props}, State) when is_list(Props) -> + json_encode_proplist(Props, State); +json_encode(Bad, #encoder{handler=null}) -> + exit({json_encode, {bad_term, Bad}}); +json_encode(Bad, State=#encoder{handler=Handler}) -> + json_encode(Handler(Bad), State). + +json_encode_array([], _State) -> + "[]"; +json_encode_array(L, State) -> + F = fun (O, Acc) -> + [$,, json_encode(O, State) | Acc] + end, + [$, | Acc1] = lists:foldl(F, "[", L), + lists:reverse([$\] | Acc1]). + +json_encode_proplist([], _State) -> + "{}"; +json_encode_proplist(Props, State) -> + F = fun ({K, V}, Acc) -> + KS = case K of + K when is_atom(K) -> + json_encode_string_utf8(atom_to_list(K)); + K when is_integer(K) -> + json_encode_string(integer_to_list(K), State); + K when is_list(K); is_binary(K) -> + json_encode_string(K, State) + end, + VS = json_encode(V, State), + [$,, VS, $:, KS | Acc] + end, + [$, | Acc1] = lists:foldl(F, "{", Props), + lists:reverse([$\} | Acc1]). + +json_encode_string(A, _State) when is_atom(A) -> + json_encode_string_unicode(xmerl_ucs:from_utf8(atom_to_list(A))); +json_encode_string(B, _State) when is_binary(B) -> + json_encode_string_unicode(xmerl_ucs:from_utf8(B)); +json_encode_string(S, #encoder{input_encoding=utf8}) -> + json_encode_string_utf8(S); +json_encode_string(S, #encoder{input_encoding=unicode}) -> + json_encode_string_unicode(S). + +json_encode_string_utf8(S) -> + [?Q | json_encode_string_utf8_1(S)]. + +json_encode_string_utf8_1([C | Cs]) when C >= 0, C =< 16#7f -> + NewC = case C of + $\\ -> "\\\\"; + ?Q -> "\\\""; + _ when C >= $\s, C < 16#7f -> C; + $\t -> "\\t"; + $\n -> "\\n"; + $\r -> "\\r"; + $\f -> "\\f"; + $\b -> "\\b"; + _ when C >= 0, C =< 16#7f -> unihex(C); + _ -> exit({json_encode, {bad_char, C}}) + end, + [NewC | json_encode_string_utf8_1(Cs)]; +json_encode_string_utf8_1(All=[C | _]) when C >= 16#80, C =< 16#10FFFF -> + [?Q | Rest] = json_encode_string_unicode(xmerl_ucs:from_utf8(All)), + Rest; +json_encode_string_utf8_1([]) -> + "\"". + +json_encode_string_unicode(S) -> + [?Q | json_encode_string_unicode_1(S)]. + +json_encode_string_unicode_1([C | Cs]) -> + NewC = case C of + $\\ -> "\\\\"; + ?Q -> "\\\""; + _ when C >= $\s, C < 16#7f -> C; + $\t -> "\\t"; + $\n -> "\\n"; + $\r -> "\\r"; + $\f -> "\\f"; + $\b -> "\\b"; + _ when C >= 0, C =< 16#10FFFF -> unihex(C); + _ -> exit({json_encode, {bad_char, C}}) + end, + [NewC | json_encode_string_unicode_1(Cs)]; +json_encode_string_unicode_1([]) -> + "\"". + +dehex(C) when C >= $0, C =< $9 -> + C - $0; +dehex(C) when C >= $a, C =< $f -> + C - $a + 10; +dehex(C) when C >= $A, C =< $F -> + C - $A + 10. + +hexdigit(C) when C >= 0, C =< 9 -> + C + $0; +hexdigit(C) when C =< 15 -> + C + $a - 10. + +unihex(C) when C < 16#10000 -> + <> = <>, + Digits = [hexdigit(D) || D <- [D3, D2, D1, D0]], + [$\\, $u | Digits]; +unihex(C) when C =< 16#10FFFF -> + N = C - 16#10000, + S1 = 16#d800 bor ((N bsr 10) band 16#3ff), + S2 = 16#dc00 bor (N band 16#3ff), + [unihex(S1), unihex(S2)]. + +json_decode(B, S) when is_binary(B) -> + json_decode(binary_to_list(B), S); +json_decode(L, S) -> + {Res, L1, S1} = decode1(L, S), + {eof, [], _} = tokenize(L1, S1#decoder{state=trim}), + Res. + +decode1(L, S=#decoder{state=null}) -> + case tokenize(L, S#decoder{state=any}) of + {{const, C}, L1, S1} -> + {C, L1, S1}; + {start_array, L1, S1} -> + decode_array(L1, S1#decoder{state=any}, []); + {start_object, L1, S1} -> + decode_object(L1, S1#decoder{state=key}, []) + end. + +make_object(V, #decoder{object_hook=null}) -> + V; +make_object(V, #decoder{object_hook=Hook}) -> + Hook(V). + +decode_object(L, S=#decoder{state=key}, Acc) -> + case tokenize(L, S) of + {end_object, Rest, S1} -> + V = make_object({struct, lists:reverse(Acc)}, S1), + {V, Rest, S1#decoder{state=null}}; + {{const, K}, Rest, S1} when is_list(K) -> + {colon, L2, S2} = tokenize(Rest, S1), + {V, L3, S3} = decode1(L2, S2#decoder{state=null}), + decode_object(L3, S3#decoder{state=comma}, [{K, V} | Acc]) + end; +decode_object(L, S=#decoder{state=comma}, Acc) -> + case tokenize(L, S) of + {end_object, Rest, S1} -> + V = make_object({struct, lists:reverse(Acc)}, S1), + {V, Rest, S1#decoder{state=null}}; + {comma, Rest, S1} -> + decode_object(Rest, S1#decoder{state=key}, Acc) + end. + +decode_array(L, S=#decoder{state=any}, Acc) -> + case tokenize(L, S) of + {end_array, Rest, S1} -> + {{array, lists:reverse(Acc)}, Rest, S1#decoder{state=null}}; + {start_array, Rest, S1} -> + {Array, Rest1, S2} = decode_array(Rest, S1#decoder{state=any}, []), + decode_array(Rest1, S2#decoder{state=comma}, [Array | Acc]); + {start_object, Rest, S1} -> + {Array, Rest1, S2} = decode_object(Rest, S1#decoder{state=key}, []), + decode_array(Rest1, S2#decoder{state=comma}, [Array | Acc]); + {{const, Const}, Rest, S1} -> + decode_array(Rest, S1#decoder{state=comma}, [Const | Acc]) + end; +decode_array(L, S=#decoder{state=comma}, Acc) -> + case tokenize(L, S) of + {end_array, Rest, S1} -> + {{array, lists:reverse(Acc)}, Rest, S1#decoder{state=null}}; + {comma, Rest, S1} -> + decode_array(Rest, S1#decoder{state=any}, Acc) + end. + +tokenize_string(IoList=[C | _], S=#decoder{input_encoding=utf8}, Acc) + when is_list(C); is_binary(C); C >= 16#7f -> + List = xmerl_ucs:from_utf8(iolist_to_binary(IoList)), + tokenize_string(List, S#decoder{input_encoding=unicode}, Acc); +tokenize_string("\"" ++ Rest, S, Acc) -> + {lists:reverse(Acc), Rest, ?INC_COL(S)}; +tokenize_string("\\\"" ++ Rest, S, Acc) -> + tokenize_string(Rest, ?ADV_COL(S, 2), [$\" | Acc]); +tokenize_string("\\\\" ++ Rest, S, Acc) -> + tokenize_string(Rest, ?ADV_COL(S, 2), [$\\ | Acc]); +tokenize_string("\\/" ++ Rest, S, Acc) -> + tokenize_string(Rest, ?ADV_COL(S, 2), [$/ | Acc]); +tokenize_string("\\b" ++ Rest, S, Acc) -> + tokenize_string(Rest, ?ADV_COL(S, 2), [$\b | Acc]); +tokenize_string("\\f" ++ Rest, S, Acc) -> + tokenize_string(Rest, ?ADV_COL(S, 2), [$\f | Acc]); +tokenize_string("\\n" ++ Rest, S, Acc) -> + tokenize_string(Rest, ?ADV_COL(S, 2), [$\n | Acc]); +tokenize_string("\\r" ++ Rest, S, Acc) -> + tokenize_string(Rest, ?ADV_COL(S, 2), [$\r | Acc]); +tokenize_string("\\t" ++ Rest, S, Acc) -> + tokenize_string(Rest, ?ADV_COL(S, 2), [$\t | Acc]); +tokenize_string([$\\, $u, C3, C2, C1, C0 | Rest], S, Acc) -> + % coalesce UTF-16 surrogate pair? + C = dehex(C0) bor + (dehex(C1) bsl 4) bor + (dehex(C2) bsl 8) bor + (dehex(C3) bsl 12), + tokenize_string(Rest, ?ADV_COL(S, 6), [C | Acc]); +tokenize_string([C | Rest], S, Acc) when C >= $\s; C < 16#10FFFF -> + tokenize_string(Rest, ?ADV_COL(S, 1), [C | Acc]). + +tokenize_number(IoList=[C | _], Mode, S=#decoder{input_encoding=utf8}, Acc) + when is_list(C); is_binary(C); C >= 16#7f -> + List = xmerl_ucs:from_utf8(iolist_to_binary(IoList)), + tokenize_number(List, Mode, S#decoder{input_encoding=unicode}, Acc); +tokenize_number([$- | Rest], sign, S, []) -> + tokenize_number(Rest, int, ?INC_COL(S), [$-]); +tokenize_number(Rest, sign, S, []) -> + tokenize_number(Rest, int, S, []); +tokenize_number([$0 | Rest], int, S, Acc) -> + tokenize_number(Rest, frac, ?INC_COL(S), [$0 | Acc]); +tokenize_number([C | Rest], int, S, Acc) when C >= $1, C =< $9 -> + tokenize_number(Rest, int1, ?INC_COL(S), [C | Acc]); +tokenize_number([C | Rest], int1, S, Acc) when C >= $0, C =< $9 -> + tokenize_number(Rest, int1, ?INC_COL(S), [C | Acc]); +tokenize_number(Rest, int1, S, Acc) -> + tokenize_number(Rest, frac, S, Acc); +tokenize_number([$., C | Rest], frac, S, Acc) when C >= $0, C =< $9 -> + tokenize_number(Rest, frac1, ?ADV_COL(S, 2), [C, $. | Acc]); +tokenize_number([E | Rest], frac, S, Acc) when E == $e; E == $E -> + tokenize_number(Rest, esign, ?INC_COL(S), [$e, $0, $. | Acc]); +tokenize_number(Rest, frac, S, Acc) -> + {{int, lists:reverse(Acc)}, Rest, S}; +tokenize_number([C | Rest], frac1, S, Acc) when C >= $0, C =< $9 -> + tokenize_number(Rest, frac1, ?INC_COL(S), [C | Acc]); +tokenize_number([E | Rest], frac1, S, Acc) when E == $e; E == $E -> + tokenize_number(Rest, esign, ?INC_COL(S), [$e | Acc]); +tokenize_number(Rest, frac1, S, Acc) -> + {{float, lists:reverse(Acc)}, Rest, S}; +tokenize_number([C | Rest], esign, S, Acc) when C == $-; C == $+ -> + tokenize_number(Rest, eint, ?INC_COL(S), [C | Acc]); +tokenize_number(Rest, esign, S, Acc) -> + tokenize_number(Rest, eint, S, Acc); +tokenize_number([C | Rest], eint, S, Acc) when C >= $0, C =< $9 -> + tokenize_number(Rest, eint1, ?INC_COL(S), [C | Acc]); +tokenize_number([C | Rest], eint1, S, Acc) when C >= $0, C =< $9 -> + tokenize_number(Rest, eint1, ?INC_COL(S), [C | Acc]); +tokenize_number(Rest, eint1, S, Acc) -> + {{float, lists:reverse(Acc)}, Rest, S}. + +tokenize([], S=#decoder{state=trim}) -> + {eof, [], S}; +tokenize([L | Rest], S) when is_list(L) -> + tokenize(L ++ Rest, S); +tokenize([B | Rest], S) when is_binary(B) -> + tokenize(xmerl_ucs:from_utf8(B) ++ Rest, S); +tokenize("\r\n" ++ Rest, S) -> + tokenize(Rest, ?INC_LINE(S)); +tokenize("\n" ++ Rest, S) -> + tokenize(Rest, ?INC_LINE(S)); +tokenize([C | Rest], S) when C == $\s; C == $\t -> + tokenize(Rest, ?INC_COL(S)); +tokenize("{" ++ Rest, S) -> + {start_object, Rest, ?INC_COL(S)}; +tokenize("}" ++ Rest, S) -> + {end_object, Rest, ?INC_COL(S)}; +tokenize("[" ++ Rest, S) -> + {start_array, Rest, ?INC_COL(S)}; +tokenize("]" ++ Rest, S) -> + {end_array, Rest, ?INC_COL(S)}; +tokenize("," ++ Rest, S) -> + {comma, Rest, ?INC_COL(S)}; +tokenize(":" ++ Rest, S) -> + {colon, Rest, ?INC_COL(S)}; +tokenize("null" ++ Rest, S) -> + {{const, null}, Rest, ?ADV_COL(S, 4)}; +tokenize("true" ++ Rest, S) -> + {{const, true}, Rest, ?ADV_COL(S, 4)}; +tokenize("false" ++ Rest, S) -> + {{const, false}, Rest, ?ADV_COL(S, 5)}; +tokenize("\"" ++ Rest, S) -> + {String, Rest1, S1} = tokenize_string(Rest, ?INC_COL(S), []), + {{const, String}, Rest1, S1}; +tokenize(L=[C | _], S) when C >= $0, C =< $9; C == $- -> + case tokenize_number(L, sign, S, []) of + {{int, Int}, Rest, S1} -> + {{const, list_to_integer(Int)}, Rest, S1}; + {{float, Float}, Rest, S1} -> + {{const, list_to_float(Float)}, Rest, S1} + end. + +%% testing constructs borrowed from the Yaws JSON implementation. + +%% Create an object from a list of Key/Value pairs. + +obj_new() -> + {struct, []}. + +is_obj({struct, Props}) -> + F = fun ({K, _}) when is_list(K) -> + true; + (_) -> + false + end, + lists:all(F, Props). + +obj_from_list(Props) -> + Obj = {struct, Props}, + case is_obj(Obj) of + true -> Obj; + false -> exit(json_bad_object) + end. + +%% Test for equivalence of Erlang terms. +%% Due to arbitrary order of construction, equivalent objects might +%% compare unequal as erlang terms, so we need to carefully recurse +%% through aggregates (tuples and objects). + +equiv({struct, Props1}, {struct, Props2}) -> + equiv_object(Props1, Props2); +equiv({array, L1}, {array, L2}) -> + equiv_list(L1, L2); +equiv(N1, N2) when is_number(N1), is_number(N2) -> N1 == N2; +equiv(S1, S2) when is_list(S1), is_list(S2) -> S1 == S2; +equiv(true, true) -> true; +equiv(false, false) -> true; +equiv(null, null) -> true. + +%% Object representation and traversal order is unknown. +%% Use the sledgehammer and sort property lists. + +equiv_object(Props1, Props2) -> + L1 = lists:keysort(1, Props1), + L2 = lists:keysort(1, Props2), + Pairs = lists:zip(L1, L2), + true = lists:all(fun({{K1, V1}, {K2, V2}}) -> + equiv(K1, K2) and equiv(V1, V2) + end, Pairs). + +%% Recursively compare tuple elements for equivalence. + +equiv_list([], []) -> + true; +equiv_list([V1 | L1], [V2 | L2]) -> + case equiv(V1, V2) of + true -> + equiv_list(L1, L2); + false -> + false + end. + +test_all() -> + test_issue33(), + test_one(e2j_test_vec(utf8), 1). + +test_issue33() -> + %% http://code.google.com/p/mochiweb/issues/detail?id=33 + Js = {struct, [{"key", [194, 163]}]}, + Encoder = encoder([{input_encoding, utf8}]), + "{\"key\":\"\\u00a3\"}" = lists:flatten(Encoder(Js)). + +test_one([], _N) -> + %% io:format("~p tests passed~n", [N-1]), + ok; +test_one([{E, J} | Rest], N) -> + %% io:format("[~p] ~p ~p~n", [N, E, J]), + true = equiv(E, decode(J)), + true = equiv(E, decode(encode(E))), + test_one(Rest, 1+N). + +e2j_test_vec(utf8) -> + [ + {1, "1"}, + {3.1416, "3.14160"}, % text representation may truncate, trail zeroes + {-1, "-1"}, + {-3.1416, "-3.14160"}, + {12.0e10, "1.20000e+11"}, + {1.234E+10, "1.23400e+10"}, + {-1.234E-10, "-1.23400e-10"}, + {10.0, "1.0e+01"}, + {123.456, "1.23456E+2"}, + {10.0, "1e1"}, + {"foo", "\"foo\""}, + {"foo" ++ [5] ++ "bar", "\"foo\\u0005bar\""}, + {"", "\"\""}, + {"\"", "\"\\\"\""}, + {"\n\n\n", "\"\\n\\n\\n\""}, + {"\\", "\"\\\\\""}, + {"\" \b\f\r\n\t\"", "\"\\\" \\b\\f\\r\\n\\t\\\"\""}, + {obj_new(), "{}"}, + {obj_from_list([{"foo", "bar"}]), "{\"foo\":\"bar\"}"}, + {obj_from_list([{"foo", "bar"}, {"baz", 123}]), + "{\"foo\":\"bar\",\"baz\":123}"}, + {{array, []}, "[]"}, + {{array, [{array, []}]}, "[[]]"}, + {{array, [1, "foo"]}, "[1,\"foo\"]"}, + + % json array in a json object + {obj_from_list([{"foo", {array, [123]}}]), + "{\"foo\":[123]}"}, + + % json object in a json object + {obj_from_list([{"foo", obj_from_list([{"bar", true}])}]), + "{\"foo\":{\"bar\":true}}"}, + + % fold evaluation order + {obj_from_list([{"foo", {array, []}}, + {"bar", obj_from_list([{"baz", true}])}, + {"alice", "bob"}]), + "{\"foo\":[],\"bar\":{\"baz\":true},\"alice\":\"bob\"}"}, + + % json object in a json array + {{array, [-123, "foo", obj_from_list([{"bar", {array, []}}]), null]}, + "[-123,\"foo\",{\"bar\":[]},null]"} + ]. diff --git a/web/api/webmachine/deps/mochiweb/src/mochijson2.erl b/web/api/webmachine/deps/mochiweb/src/mochijson2.erl new file mode 100644 index 0000000..246d39e --- /dev/null +++ b/web/api/webmachine/deps/mochiweb/src/mochijson2.erl @@ -0,0 +1,618 @@ +%% @author Bob Ippolito +%% @copyright 2007 Mochi Media, Inc. + +%% @doc Yet another JSON (RFC 4627) library for Erlang. mochijson2 works +%% with binaries as strings, arrays as lists (without an {array, _}) +%% wrapper and it only knows how to decode UTF-8 (and ASCII). + +-module(mochijson2). +-author('bob@mochimedia.com'). +-export([encoder/1, encode/1]). +-export([decoder/1, decode/1]). +-export([test/0]). + +% This is a macro to placate syntax highlighters.. +-define(Q, $\"). +-define(ADV_COL(S, N), S#decoder{offset=N+S#decoder.offset, + column=N+S#decoder.column}). +-define(INC_COL(S), S#decoder{offset=1+S#decoder.offset, + column=1+S#decoder.column}). +-define(INC_LINE(S), S#decoder{offset=1+S#decoder.offset, + column=1, + line=1+S#decoder.line}). +-define(INC_CHAR(S, C), + case C of + $\n -> + S#decoder{column=1, + line=1+S#decoder.line, + offset=1+S#decoder.offset}; + _ -> + S#decoder{column=1+S#decoder.column, + offset=1+S#decoder.offset} + end). +-define(IS_WHITESPACE(C), + (C =:= $\s orelse C =:= $\t orelse C =:= $\r orelse C =:= $\n)). + +%% @type iolist() = [char() | binary() | iolist()] +%% @type iodata() = iolist() | binary() +%% @type json_string() = atom | binary() +%% @type json_number() = integer() | float() +%% @type json_array() = [json_term()] +%% @type json_object() = {struct, [{json_string(), json_term()}]} +%% @type json_term() = json_string() | json_number() | json_array() | +%% json_object() + +-record(encoder, {handler=null, + utf8=false}). + +-record(decoder, {object_hook=null, + offset=0, + line=1, + column=1, + state=null}). + +%% @spec encoder([encoder_option()]) -> function() +%% @doc Create an encoder/1 with the given options. +%% @type encoder_option() = handler_option() | utf8_option() +%% @type utf8_option() = boolean(). Emit unicode as utf8 (default - false) +encoder(Options) -> + State = parse_encoder_options(Options, #encoder{}), + fun (O) -> json_encode(O, State) end. + +%% @spec encode(json_term()) -> iolist() +%% @doc Encode the given as JSON to an iolist. +encode(Any) -> + json_encode(Any, #encoder{}). + +%% @spec decoder([decoder_option()]) -> function() +%% @doc Create a decoder/1 with the given options. +decoder(Options) -> + State = parse_decoder_options(Options, #decoder{}), + fun (O) -> json_decode(O, State) end. + +%% @spec decode(iolist()) -> json_term() +%% @doc Decode the given iolist to Erlang terms. +decode(S) -> + json_decode(S, #decoder{}). + +test() -> + test_all(). + +%% Internal API + +parse_encoder_options([], State) -> + State; +parse_encoder_options([{handler, Handler} | Rest], State) -> + parse_encoder_options(Rest, State#encoder{handler=Handler}); +parse_encoder_options([{utf8, Switch} | Rest], State) -> + parse_encoder_options(Rest, State#encoder{utf8=Switch}). + +parse_decoder_options([], State) -> + State; +parse_decoder_options([{object_hook, Hook} | Rest], State) -> + parse_decoder_options(Rest, State#decoder{object_hook=Hook}). + +json_encode(true, _State) -> + <<"true">>; +json_encode(false, _State) -> + <<"false">>; +json_encode(null, _State) -> + <<"null">>; +json_encode(I, _State) when is_integer(I) andalso I >= -2147483648 andalso I =< 2147483647 -> + %% Anything outside of 32-bit integers should be encoded as a float + integer_to_list(I); +json_encode(I, _State) when is_integer(I) -> + mochinum:digits(float(I)); +json_encode(F, _State) when is_float(F) -> + mochinum:digits(F); +json_encode(S, State) when is_binary(S); is_atom(S) -> + json_encode_string(S, State); +json_encode(Array, State) when is_list(Array) -> + json_encode_array(Array, State); +json_encode({struct, Props}, State) when is_list(Props) -> + json_encode_proplist(Props, State); +json_encode(Bad, #encoder{handler=null}) -> + exit({json_encode, {bad_term, Bad}}); +json_encode(Bad, State=#encoder{handler=Handler}) -> + json_encode(Handler(Bad), State). + +json_encode_array([], _State) -> + <<"[]">>; +json_encode_array(L, State) -> + F = fun (O, Acc) -> + [$,, json_encode(O, State) | Acc] + end, + [$, | Acc1] = lists:foldl(F, "[", L), + lists:reverse([$\] | Acc1]). + +json_encode_proplist([], _State) -> + <<"{}">>; +json_encode_proplist(Props, State) -> + F = fun ({K, V}, Acc) -> + KS = json_encode_string(K, State), + VS = json_encode(V, State), + [$,, VS, $:, KS | Acc] + end, + [$, | Acc1] = lists:foldl(F, "{", Props), + lists:reverse([$\} | Acc1]). + +json_encode_string(A, State) when is_atom(A) -> + L = atom_to_list(A), + case json_string_is_safe(L) of + true -> + [?Q, L, ?Q]; + false -> + json_encode_string_unicode(xmerl_ucs:from_utf8(L), State, [?Q]) + end; +json_encode_string(B, State) when is_binary(B) -> + case json_bin_is_safe(B) of + true -> + [?Q, B, ?Q]; + false -> + json_encode_string_unicode(xmerl_ucs:from_utf8(B), State, [?Q]) + end; +json_encode_string(I, _State) when is_integer(I) -> + [?Q, integer_to_list(I), ?Q]; +json_encode_string(L, State) when is_list(L) -> + case json_string_is_safe(L) of + true -> + [?Q, L, ?Q]; + false -> + json_encode_string_unicode(L, State, [?Q]) + end. + +json_string_is_safe([]) -> + true; +json_string_is_safe([C | Rest]) -> + case C of + ?Q -> + false; + $\\ -> + false; + $\b -> + false; + $\f -> + false; + $\n -> + false; + $\r -> + false; + $\t -> + false; + C when C >= 0, C < $\s; C >= 16#7f, C =< 16#10FFFF -> + false; + C when C < 16#7f -> + json_string_is_safe(Rest); + _ -> + false + end. + +json_bin_is_safe(<<>>) -> + true; +json_bin_is_safe(<>) -> + case C of + ?Q -> + false; + $\\ -> + false; + $\b -> + false; + $\f -> + false; + $\n -> + false; + $\r -> + false; + $\t -> + false; + C when C >= 0, C < $\s; C >= 16#7f, C =< 16#10FFFF -> + false; + C when C < 16#7f -> + json_bin_is_safe(Rest); + _ -> + false + end. + +json_encode_string_unicode([], _State, Acc) -> + lists:reverse([$\" | Acc]); +json_encode_string_unicode([C | Cs], State, Acc) -> + Acc1 = case C of + ?Q -> + [?Q, $\\ | Acc]; + %% Escaping solidus is only useful when trying to protect + %% against "" injection attacks which are only + %% possible when JSON is inserted into a HTML document + %% in-line. mochijson2 does not protect you from this, so + %% if you do insert directly into HTML then you need to + %% uncomment the following case or escape the output of encode. + %% + %% $/ -> + %% [$/, $\\ | Acc]; + %% + $\\ -> + [$\\, $\\ | Acc]; + $\b -> + [$b, $\\ | Acc]; + $\f -> + [$f, $\\ | Acc]; + $\n -> + [$n, $\\ | Acc]; + $\r -> + [$r, $\\ | Acc]; + $\t -> + [$t, $\\ | Acc]; + C when C >= 0, C < $\s -> + [unihex(C) | Acc]; + C when C >= 16#7f, C =< 16#10FFFF, State#encoder.utf8 -> + [xmerl_ucs:to_utf8(C) | Acc]; + C when C >= 16#7f, C =< 16#10FFFF, not State#encoder.utf8 -> + [unihex(C) | Acc]; + C when C < 16#7f -> + [C | Acc]; + _ -> + exit({json_encode, {bad_char, C}}) + end, + json_encode_string_unicode(Cs, State, Acc1). + +hexdigit(C) when C >= 0, C =< 9 -> + C + $0; +hexdigit(C) when C =< 15 -> + C + $a - 10. + +unihex(C) when C < 16#10000 -> + <> = <>, + Digits = [hexdigit(D) || D <- [D3, D2, D1, D0]], + [$\\, $u | Digits]; +unihex(C) when C =< 16#10FFFF -> + N = C - 16#10000, + S1 = 16#d800 bor ((N bsr 10) band 16#3ff), + S2 = 16#dc00 bor (N band 16#3ff), + [unihex(S1), unihex(S2)]. + +json_decode(L, S) when is_list(L) -> + json_decode(iolist_to_binary(L), S); +json_decode(B, S) -> + {Res, S1} = decode1(B, S), + {eof, _} = tokenize(B, S1#decoder{state=trim}), + Res. + +decode1(B, S=#decoder{state=null}) -> + case tokenize(B, S#decoder{state=any}) of + {{const, C}, S1} -> + {C, S1}; + {start_array, S1} -> + decode_array(B, S1); + {start_object, S1} -> + decode_object(B, S1) + end. + +make_object(V, #decoder{object_hook=null}) -> + V; +make_object(V, #decoder{object_hook=Hook}) -> + Hook(V). + +decode_object(B, S) -> + decode_object(B, S#decoder{state=key}, []). + +decode_object(B, S=#decoder{state=key}, Acc) -> + case tokenize(B, S) of + {end_object, S1} -> + V = make_object({struct, lists:reverse(Acc)}, S1), + {V, S1#decoder{state=null}}; + {{const, K}, S1} -> + {colon, S2} = tokenize(B, S1), + {V, S3} = decode1(B, S2#decoder{state=null}), + decode_object(B, S3#decoder{state=comma}, [{K, V} | Acc]) + end; +decode_object(B, S=#decoder{state=comma}, Acc) -> + case tokenize(B, S) of + {end_object, S1} -> + V = make_object({struct, lists:reverse(Acc)}, S1), + {V, S1#decoder{state=null}}; + {comma, S1} -> + decode_object(B, S1#decoder{state=key}, Acc) + end. + +decode_array(B, S) -> + decode_array(B, S#decoder{state=any}, []). + +decode_array(B, S=#decoder{state=any}, Acc) -> + case tokenize(B, S) of + {end_array, S1} -> + {lists:reverse(Acc), S1#decoder{state=null}}; + {start_array, S1} -> + {Array, S2} = decode_array(B, S1), + decode_array(B, S2#decoder{state=comma}, [Array | Acc]); + {start_object, S1} -> + {Array, S2} = decode_object(B, S1), + decode_array(B, S2#decoder{state=comma}, [Array | Acc]); + {{const, Const}, S1} -> + decode_array(B, S1#decoder{state=comma}, [Const | Acc]) + end; +decode_array(B, S=#decoder{state=comma}, Acc) -> + case tokenize(B, S) of + {end_array, S1} -> + {lists:reverse(Acc), S1#decoder{state=null}}; + {comma, S1} -> + decode_array(B, S1#decoder{state=any}, Acc) + end. + +tokenize_string(B, S=#decoder{offset=O}) -> + case tokenize_string_fast(B, O) of + {escape, O1} -> + Length = O1 - O, + S1 = ?ADV_COL(S, Length), + <<_:O/binary, Head:Length/binary, _/binary>> = B, + tokenize_string(B, S1, lists:reverse(binary_to_list(Head))); + O1 -> + Length = O1 - O, + <<_:O/binary, String:Length/binary, ?Q, _/binary>> = B, + {{const, String}, ?ADV_COL(S, Length + 1)} + end. + +tokenize_string_fast(B, O) -> + case B of + <<_:O/binary, ?Q, _/binary>> -> + O; + <<_:O/binary, C, _/binary>> when C =/= $\\ -> + tokenize_string_fast(B, 1 + O); + _ -> + {escape, O} + end. + +tokenize_string(B, S=#decoder{offset=O}, Acc) -> + case B of + <<_:O/binary, ?Q, _/binary>> -> + {{const, iolist_to_binary(lists:reverse(Acc))}, ?INC_COL(S)}; + <<_:O/binary, "\\\"", _/binary>> -> + tokenize_string(B, ?ADV_COL(S, 2), [$\" | Acc]); + <<_:O/binary, "\\\\", _/binary>> -> + tokenize_string(B, ?ADV_COL(S, 2), [$\\ | Acc]); + <<_:O/binary, "\\/", _/binary>> -> + tokenize_string(B, ?ADV_COL(S, 2), [$/ | Acc]); + <<_:O/binary, "\\b", _/binary>> -> + tokenize_string(B, ?ADV_COL(S, 2), [$\b | Acc]); + <<_:O/binary, "\\f", _/binary>> -> + tokenize_string(B, ?ADV_COL(S, 2), [$\f | Acc]); + <<_:O/binary, "\\n", _/binary>> -> + tokenize_string(B, ?ADV_COL(S, 2), [$\n | Acc]); + <<_:O/binary, "\\r", _/binary>> -> + tokenize_string(B, ?ADV_COL(S, 2), [$\r | Acc]); + <<_:O/binary, "\\t", _/binary>> -> + tokenize_string(B, ?ADV_COL(S, 2), [$\t | Acc]); + <<_:O/binary, "\\u", C3, C2, C1, C0, _/binary>> -> + %% coalesce UTF-16 surrogate pair? + C = erlang:list_to_integer([C3, C2, C1, C0], 16), + Acc1 = lists:reverse(xmerl_ucs:to_utf8(C), Acc), + tokenize_string(B, ?ADV_COL(S, 6), Acc1); + <<_:O/binary, C, _/binary>> -> + tokenize_string(B, ?INC_CHAR(S, C), [C | Acc]) + end. + +tokenize_number(B, S) -> + case tokenize_number(B, sign, S, []) of + {{int, Int}, S1} -> + {{const, list_to_integer(Int)}, S1}; + {{float, Float}, S1} -> + {{const, list_to_float(Float)}, S1} + end. + +tokenize_number(B, sign, S=#decoder{offset=O}, []) -> + case B of + <<_:O/binary, $-, _/binary>> -> + tokenize_number(B, int, ?INC_COL(S), [$-]); + _ -> + tokenize_number(B, int, S, []) + end; +tokenize_number(B, int, S=#decoder{offset=O}, Acc) -> + case B of + <<_:O/binary, $0, _/binary>> -> + tokenize_number(B, frac, ?INC_COL(S), [$0 | Acc]); + <<_:O/binary, C, _/binary>> when C >= $1 andalso C =< $9 -> + tokenize_number(B, int1, ?INC_COL(S), [C | Acc]) + end; +tokenize_number(B, int1, S=#decoder{offset=O}, Acc) -> + case B of + <<_:O/binary, C, _/binary>> when C >= $0 andalso C =< $9 -> + tokenize_number(B, int1, ?INC_COL(S), [C | Acc]); + _ -> + tokenize_number(B, frac, S, Acc) + end; +tokenize_number(B, frac, S=#decoder{offset=O}, Acc) -> + case B of + <<_:O/binary, $., C, _/binary>> when C >= $0, C =< $9 -> + tokenize_number(B, frac1, ?ADV_COL(S, 2), [C, $. | Acc]); + <<_:O/binary, E, _/binary>> when E =:= $e orelse E =:= $E -> + tokenize_number(B, esign, ?INC_COL(S), [$e, $0, $. | Acc]); + _ -> + {{int, lists:reverse(Acc)}, S} + end; +tokenize_number(B, frac1, S=#decoder{offset=O}, Acc) -> + case B of + <<_:O/binary, C, _/binary>> when C >= $0 andalso C =< $9 -> + tokenize_number(B, frac1, ?INC_COL(S), [C | Acc]); + <<_:O/binary, E, _/binary>> when E =:= $e orelse E =:= $E -> + tokenize_number(B, esign, ?INC_COL(S), [$e | Acc]); + _ -> + {{float, lists:reverse(Acc)}, S} + end; +tokenize_number(B, esign, S=#decoder{offset=O}, Acc) -> + case B of + <<_:O/binary, C, _/binary>> when C =:= $- orelse C=:= $+ -> + tokenize_number(B, eint, ?INC_COL(S), [C | Acc]); + _ -> + tokenize_number(B, eint, S, Acc) + end; +tokenize_number(B, eint, S=#decoder{offset=O}, Acc) -> + case B of + <<_:O/binary, C, _/binary>> when C >= $0 andalso C =< $9 -> + tokenize_number(B, eint1, ?INC_COL(S), [C | Acc]) + end; +tokenize_number(B, eint1, S=#decoder{offset=O}, Acc) -> + case B of + <<_:O/binary, C, _/binary>> when C >= $0 andalso C =< $9 -> + tokenize_number(B, eint1, ?INC_COL(S), [C | Acc]); + _ -> + {{float, lists:reverse(Acc)}, S} + end. + +tokenize(B, S=#decoder{offset=O}) -> + case B of + <<_:O/binary, C, _/binary>> when ?IS_WHITESPACE(C) -> + tokenize(B, ?INC_CHAR(S, C)); + <<_:O/binary, "{", _/binary>> -> + {start_object, ?INC_COL(S)}; + <<_:O/binary, "}", _/binary>> -> + {end_object, ?INC_COL(S)}; + <<_:O/binary, "[", _/binary>> -> + {start_array, ?INC_COL(S)}; + <<_:O/binary, "]", _/binary>> -> + {end_array, ?INC_COL(S)}; + <<_:O/binary, ",", _/binary>> -> + {comma, ?INC_COL(S)}; + <<_:O/binary, ":", _/binary>> -> + {colon, ?INC_COL(S)}; + <<_:O/binary, "null", _/binary>> -> + {{const, null}, ?ADV_COL(S, 4)}; + <<_:O/binary, "true", _/binary>> -> + {{const, true}, ?ADV_COL(S, 4)}; + <<_:O/binary, "false", _/binary>> -> + {{const, false}, ?ADV_COL(S, 5)}; + <<_:O/binary, "\"", _/binary>> -> + tokenize_string(B, ?INC_COL(S)); + <<_:O/binary, C, _/binary>> when (C >= $0 andalso C =< $9) + orelse C =:= $- -> + tokenize_number(B, S); + <<_:O/binary>> -> + trim = S#decoder.state, + {eof, S} + end. + +%% testing constructs borrowed from the Yaws JSON implementation. + +%% Create an object from a list of Key/Value pairs. + +obj_new() -> + {struct, []}. + +is_obj({struct, Props}) -> + F = fun ({K, _}) when is_binary(K) -> + true; + (_) -> + false + end, + lists:all(F, Props). + +obj_from_list(Props) -> + Obj = {struct, Props}, + case is_obj(Obj) of + true -> Obj; + false -> exit({json_bad_object, Obj}) + end. + +%% Test for equivalence of Erlang terms. +%% Due to arbitrary order of construction, equivalent objects might +%% compare unequal as erlang terms, so we need to carefully recurse +%% through aggregates (tuples and objects). + +equiv({struct, Props1}, {struct, Props2}) -> + equiv_object(Props1, Props2); +equiv(L1, L2) when is_list(L1), is_list(L2) -> + equiv_list(L1, L2); +equiv(N1, N2) when is_number(N1), is_number(N2) -> N1 == N2; +equiv(B1, B2) when is_binary(B1), is_binary(B2) -> B1 == B2; +equiv(true, true) -> true; +equiv(false, false) -> true; +equiv(null, null) -> true. + +%% Object representation and traversal order is unknown. +%% Use the sledgehammer and sort property lists. + +equiv_object(Props1, Props2) -> + L1 = lists:keysort(1, Props1), + L2 = lists:keysort(1, Props2), + Pairs = lists:zip(L1, L2), + true = lists:all(fun({{K1, V1}, {K2, V2}}) -> + equiv(K1, K2) and equiv(V1, V2) + end, Pairs). + +%% Recursively compare tuple elements for equivalence. + +equiv_list([], []) -> + true; +equiv_list([V1 | L1], [V2 | L2]) -> + case equiv(V1, V2) of + true -> + equiv_list(L1, L2); + false -> + false + end. + +test_all() -> + [1199344435545.0, 1] = decode(<<"[1199344435545.0,1]">>), + test_encoder_utf8(), + test_one(e2j_test_vec(utf8), 1). + +test_one([], _N) -> + %% io:format("~p tests passed~n", [N-1]), + ok; +test_one([{E, J} | Rest], N) -> + %% io:format("[~p] ~p ~p~n", [N, E, J]), + true = equiv(E, decode(J)), + true = equiv(E, decode(encode(E))), + test_one(Rest, 1+N). + +e2j_test_vec(utf8) -> + [ + {1, "1"}, + {3.1416, "3.14160"}, %% text representation may truncate, trail zeroes + {-1, "-1"}, + {-3.1416, "-3.14160"}, + {12.0e10, "1.20000e+11"}, + {1.234E+10, "1.23400e+10"}, + {-1.234E-10, "-1.23400e-10"}, + {10.0, "1.0e+01"}, + {123.456, "1.23456E+2"}, + {10.0, "1e1"}, + {<<"foo">>, "\"foo\""}, + {<<"foo", 5, "bar">>, "\"foo\\u0005bar\""}, + {<<"">>, "\"\""}, + {<<"\n\n\n">>, "\"\\n\\n\\n\""}, + {<<"\" \b\f\r\n\t\"">>, "\"\\\" \\b\\f\\r\\n\\t\\\"\""}, + {obj_new(), "{}"}, + {obj_from_list([{<<"foo">>, <<"bar">>}]), "{\"foo\":\"bar\"}"}, + {obj_from_list([{<<"foo">>, <<"bar">>}, {<<"baz">>, 123}]), + "{\"foo\":\"bar\",\"baz\":123}"}, + {[], "[]"}, + {[[]], "[[]]"}, + {[1, <<"foo">>], "[1,\"foo\"]"}, + + %% json array in a json object + {obj_from_list([{<<"foo">>, [123]}]), + "{\"foo\":[123]}"}, + + %% json object in a json object + {obj_from_list([{<<"foo">>, obj_from_list([{<<"bar">>, true}])}]), + "{\"foo\":{\"bar\":true}}"}, + + %% fold evaluation order + {obj_from_list([{<<"foo">>, []}, + {<<"bar">>, obj_from_list([{<<"baz">>, true}])}, + {<<"alice">>, <<"bob">>}]), + "{\"foo\":[],\"bar\":{\"baz\":true},\"alice\":\"bob\"}"}, + + %% json object in a json array + {[-123, <<"foo">>, obj_from_list([{<<"bar">>, []}]), null], + "[-123,\"foo\",{\"bar\":[]},null]"} + ]. + +%% test utf8 encoding +test_encoder_utf8() -> + %% safe conversion case (default) + [34,"\\u0001","\\u0442","\\u0435","\\u0441","\\u0442",34] = + encode(<<1,"\321\202\320\265\321\201\321\202">>), + + %% raw utf8 output (optional) + Enc = mochijson2:encoder([{utf8, true}]), + [34,"\\u0001",[209,130],[208,181],[209,129],[209,130],34] = + Enc(<<1,"\321\202\320\265\321\201\321\202">>). diff --git a/web/api/webmachine/deps/mochiweb/src/mochinum.erl b/web/api/webmachine/deps/mochiweb/src/mochinum.erl new file mode 100644 index 0000000..4f88f9a --- /dev/null +++ b/web/api/webmachine/deps/mochiweb/src/mochinum.erl @@ -0,0 +1,289 @@ +%% @copyright 2007 Mochi Media, Inc. +%% @author Bob Ippolito + +%% @doc Useful numeric algorithms for floats that cover some deficiencies +%% in the math module. More interesting is digits/1, which implements +%% the algorithm from: +%% http://www.cs.indiana.edu/~burger/fp/index.html +%% See also "Printing Floating-Point Numbers Quickly and Accurately" +%% in Proceedings of the SIGPLAN '96 Conference on Programming Language +%% Design and Implementation. + +-module(mochinum). +-author("Bob Ippolito "). +-export([digits/1, frexp/1, int_pow/2, int_ceil/1, test/0]). + +%% IEEE 754 Float exponent bias +-define(FLOAT_BIAS, 1022). +-define(MIN_EXP, -1074). +-define(BIG_POW, 4503599627370496). + +%% External API + +%% @spec digits(number()) -> string() +%% @doc Returns a string that accurately represents the given integer or float +%% using a conservative amount of digits. Great for generating +%% human-readable output, or compact ASCII serializations for floats. +digits(N) when is_integer(N) -> + integer_to_list(N); +digits(0.0) -> + "0.0"; +digits(Float) -> + {Frac, Exp} = frexp(Float), + Exp1 = Exp - 53, + Frac1 = trunc(abs(Frac) * (1 bsl 53)), + [Place | Digits] = digits1(Float, Exp1, Frac1), + R = insert_decimal(Place, [$0 + D || D <- Digits]), + case Float < 0 of + true -> + [$- | R]; + _ -> + R + end. + +%% @spec frexp(F::float()) -> {Frac::float(), Exp::float()} +%% @doc Return the fractional and exponent part of an IEEE 754 double, +%% equivalent to the libc function of the same name. +%% F = Frac * pow(2, Exp). +frexp(F) -> + frexp1(unpack(F)). + +%% @spec int_pow(X::integer(), N::integer()) -> Y::integer() +%% @doc Moderately efficient way to exponentiate integers. +%% int_pow(10, 2) = 100. +int_pow(_X, 0) -> + 1; +int_pow(X, N) when N > 0 -> + int_pow(X, N, 1). + +%% @spec int_ceil(F::float()) -> integer() +%% @doc Return the ceiling of F as an integer. The ceiling is defined as +%% F when F == trunc(F); +%% trunc(F) when F < 0; +%% trunc(F) + 1 when F > 0. +int_ceil(X) -> + T = trunc(X), + case (X - T) of + Neg when Neg < 0 -> T; + Pos when Pos > 0 -> T + 1; + _ -> T + end. + + +%% Internal API + +int_pow(X, N, R) when N < 2 -> + R * X; +int_pow(X, N, R) -> + int_pow(X * X, N bsr 1, case N band 1 of 1 -> R * X; 0 -> R end). + +insert_decimal(0, S) -> + "0." ++ S; +insert_decimal(Place, S) when Place > 0 -> + L = length(S), + case Place - L of + 0 -> + S ++ ".0"; + N when N < 0 -> + {S0, S1} = lists:split(L + N, S), + S0 ++ "." ++ S1; + N when N < 6 -> + %% More places than digits + S ++ lists:duplicate(N, $0) ++ ".0"; + _ -> + insert_decimal_exp(Place, S) + end; +insert_decimal(Place, S) when Place > -6 -> + "0." ++ lists:duplicate(abs(Place), $0) ++ S; +insert_decimal(Place, S) -> + insert_decimal_exp(Place, S). + +insert_decimal_exp(Place, S) -> + [C | S0] = S, + S1 = case S0 of + [] -> + "0"; + _ -> + S0 + end, + Exp = case Place < 0 of + true -> + "e-"; + false -> + "e+" + end, + [C] ++ "." ++ S1 ++ Exp ++ integer_to_list(abs(Place - 1)). + + +digits1(Float, Exp, Frac) -> + Round = ((Frac band 1) =:= 0), + case Exp >= 0 of + true -> + BExp = 1 bsl Exp, + case (Frac /= ?BIG_POW) of + true -> + scale((Frac * BExp * 2), 2, BExp, BExp, + Round, Round, Float); + false -> + scale((Frac * BExp * 4), 4, (BExp * 2), BExp, + Round, Round, Float) + end; + false -> + case (Exp == ?MIN_EXP) orelse (Frac /= ?BIG_POW) of + true -> + scale((Frac * 2), 1 bsl (1 - Exp), 1, 1, + Round, Round, Float); + false -> + scale((Frac * 4), 1 bsl (2 - Exp), 2, 1, + Round, Round, Float) + end + end. + +scale(R, S, MPlus, MMinus, LowOk, HighOk, Float) -> + Est = int_ceil(math:log10(abs(Float)) - 1.0e-10), + %% Note that the scheme implementation uses a 326 element look-up table + %% for int_pow(10, N) where we do not. + case Est >= 0 of + true -> + fixup(R, S * int_pow(10, Est), MPlus, MMinus, Est, + LowOk, HighOk); + false -> + Scale = int_pow(10, -Est), + fixup(R * Scale, S, MPlus * Scale, MMinus * Scale, Est, + LowOk, HighOk) + end. + +fixup(R, S, MPlus, MMinus, K, LowOk, HighOk) -> + TooLow = case HighOk of + true -> + (R + MPlus) >= S; + false -> + (R + MPlus) > S + end, + case TooLow of + true -> + [(K + 1) | generate(R, S, MPlus, MMinus, LowOk, HighOk)]; + false -> + [K | generate(R * 10, S, MPlus * 10, MMinus * 10, LowOk, HighOk)] + end. + +generate(R0, S, MPlus, MMinus, LowOk, HighOk) -> + D = R0 div S, + R = R0 rem S, + TC1 = case LowOk of + true -> + R =< MMinus; + false -> + R < MMinus + end, + TC2 = case HighOk of + true -> + (R + MPlus) >= S; + false -> + (R + MPlus) > S + end, + case TC1 of + false -> + case TC2 of + false -> + [D | generate(R * 10, S, MPlus * 10, MMinus * 10, + LowOk, HighOk)]; + true -> + [D + 1] + end; + true -> + case TC2 of + false -> + [D]; + true -> + case R * 2 < S of + true -> + [D]; + false -> + [D + 1] + end + end + end. + +unpack(Float) -> + <> = <>, + {Sign, Exp, Frac}. + +frexp1({_Sign, 0, 0}) -> + {0.0, 0}; +frexp1({Sign, 0, Frac}) -> + Exp = log2floor(Frac), + <> = <>, + {Frac1, -(?FLOAT_BIAS) - 52 + Exp}; +frexp1({Sign, Exp, Frac}) -> + <> = <>, + {Frac1, Exp - ?FLOAT_BIAS}. + +log2floor(Int) -> + log2floor(Int, 0). + +log2floor(0, N) -> + N; +log2floor(Int, N) -> + log2floor(Int bsr 1, 1 + N). + + +test() -> + ok = test_frexp(), + ok = test_int_ceil(), + ok = test_int_pow(), + ok = test_digits(), + ok. + +test_int_ceil() -> + 1 = int_ceil(0.0001), + 0 = int_ceil(0.0), + 1 = int_ceil(0.99), + 1 = int_ceil(1.0), + -1 = int_ceil(-1.5), + -2 = int_ceil(-2.0), + ok. + +test_int_pow() -> + 1 = int_pow(1, 1), + 1 = int_pow(1, 0), + 1 = int_pow(10, 0), + 10 = int_pow(10, 1), + 100 = int_pow(10, 2), + 1000 = int_pow(10, 3), + ok. + +test_digits() -> + "0" = digits(0), + "0.0" = digits(0.0), + "1.0" = digits(1.0), + "-1.0" = digits(-1.0), + "0.1" = digits(0.1), + "0.01" = digits(0.01), + "0.001" = digits(0.001), + ok. + +test_frexp() -> + %% zero + {0.0, 0} = frexp(0.0), + %% one + {0.5, 1} = frexp(1.0), + %% negative one + {-0.5, 1} = frexp(-1.0), + %% small denormalized number + %% 4.94065645841246544177e-324 + <> = <<0,0,0,0,0,0,0,1>>, + {0.5, -1073} = frexp(SmallDenorm), + %% large denormalized number + %% 2.22507385850720088902e-308 + <> = <<0,15,255,255,255,255,255,255>>, + {0.99999999999999978, -1022} = frexp(BigDenorm), + %% small normalized number + %% 2.22507385850720138309e-308 + <> = <<0,16,0,0,0,0,0,0>>, + {0.5, -1021} = frexp(SmallNorm), + %% large normalized number + %% 1.79769313486231570815e+308 + <> = <<127,239,255,255,255,255,255,255>>, + {0.99999999999999989, 1024} = frexp(LargeNorm), + ok. diff --git a/web/api/webmachine/deps/mochiweb/src/mochiweb.app b/web/api/webmachine/deps/mochiweb/src/mochiweb.app new file mode 100644 index 0000000..cd8dbb2 --- /dev/null +++ b/web/api/webmachine/deps/mochiweb/src/mochiweb.app @@ -0,0 +1,32 @@ +{application, mochiweb, + [{description, "MochiMedia Web Server"}, + {vsn, "0.01"}, + {modules, [ + mochihex, + mochijson, + mochijson2, + mochinum, + mochiweb, + mochiweb_app, + mochiweb_charref, + mochiweb_cookies, + mochiweb_echo, + mochiweb_headers, + mochiweb_html, + mochiweb_http, + mochiweb_multipart, + mochiweb_request, + mochiweb_response, + mochiweb_skel, + mochiweb_socket_server, + mochiweb_sup, + mochiweb_util, + reloader, + mochifmt, + mochifmt_std, + mochifmt_records + ]}, + {registered, []}, + {mod, {mochiweb_app, []}}, + {env, []}, + {applications, [kernel, stdlib]}]}. diff --git a/web/api/webmachine/deps/mochiweb/src/mochiweb.erl b/web/api/webmachine/deps/mochiweb/src/mochiweb.erl new file mode 100644 index 0000000..0f4d52a --- /dev/null +++ b/web/api/webmachine/deps/mochiweb/src/mochiweb.erl @@ -0,0 +1,110 @@ +%% @author Bob Ippolito +%% @copyright 2007 Mochi Media, Inc. + +%% @doc Start and stop the MochiWeb server. + +-module(mochiweb). +-author('bob@mochimedia.com'). + +-export([start/0, stop/0]). +-export([new_request/1, new_response/1]). +-export([all_loaded/0, all_loaded/1, reload/0]). +-export([test/0]). + +%% @spec start() -> ok +%% @doc Start the MochiWeb server. +start() -> + ensure_started(crypto), + application:start(mochiweb). + +%% @spec stop() -> ok +%% @doc Stop the MochiWeb server. +stop() -> + Res = application:stop(mochiweb), + application:stop(crypto), + Res. + +%% @spec test() -> ok +%% @doc Run all of the tests for MochiWeb. +test() -> + mochiweb_util:test(), + mochiweb_headers:test(), + mochiweb_cookies:test(), + mochihex:test(), + mochinum:test(), + mochijson:test(), + mochiweb_charref:test(), + mochiweb_html:test(), + mochifmt:test(), + test_request(), + ok. + +reload() -> + [c:l(Module) || Module <- all_loaded()]. + +all_loaded() -> + all_loaded(filename:dirname(code:which(?MODULE))). + +all_loaded(Base) when is_atom(Base) -> + []; +all_loaded(Base) -> + FullBase = Base ++ "/", + F = fun ({_Module, Loaded}, Acc) when is_atom(Loaded) -> + Acc; + ({Module, Loaded}, Acc) -> + case lists:prefix(FullBase, Loaded) of + true -> + [Module | Acc]; + false -> + Acc + end + end, + lists:foldl(F, [], code:all_loaded()). + + +%% @spec new_request({Socket, Request, Headers}) -> MochiWebRequest +%% @doc Return a mochiweb_request data structure. +new_request({Socket, {Method, {abs_path, Uri}, Version}, Headers}) -> + mochiweb_request:new(Socket, + Method, + Uri, + Version, + mochiweb_headers:make(Headers)); +% this case probably doesn't "exist". +new_request({Socket, {Method, {absoluteURI, _Protocol, _Host, _Port, Uri}, + Version}, Headers}) -> + mochiweb_request:new(Socket, + Method, + Uri, + Version, + mochiweb_headers:make(Headers)); +%% Request-URI is "*" +%% From http://www.w3.org/Protocols/rfc2616/rfc2616-sec5.html#sec5.1.2 +new_request({Socket, {Method, '*'=Uri, Version}, Headers}) -> + mochiweb_request:new(Socket, + Method, + Uri, + Version, + mochiweb_headers:make(Headers)). + +%% @spec new_response({Request, integer(), Headers}) -> MochiWebResponse +%% @doc Return a mochiweb_response data structure. +new_response({Request, Code, Headers}) -> + mochiweb_response:new(Request, + Code, + mochiweb_headers:make(Headers)). + +%% Internal API + +test_request() -> + R = mochiweb_request:new(z, z, "/foo/bar/baz%20wibble+quux?qs=2", z, []), + "/foo/bar/baz wibble quux" = R:get(path), + ok. + +ensure_started(App) -> + case application:start(App) of + ok -> + ok; + {error, {already_started, App}} -> + ok + end. diff --git a/web/api/webmachine/deps/mochiweb/src/mochiweb_app.erl b/web/api/webmachine/deps/mochiweb/src/mochiweb_app.erl new file mode 100644 index 0000000..2b437f6 --- /dev/null +++ b/web/api/webmachine/deps/mochiweb/src/mochiweb_app.erl @@ -0,0 +1,20 @@ +%% @author Bob Ippolito +%% @copyright 2007 Mochi Media, Inc. + +%% @doc Callbacks for the mochiweb application. + +-module(mochiweb_app). +-author('bob@mochimedia.com'). + +-behaviour(application). +-export([start/2,stop/1]). + +%% @spec start(_Type, _StartArgs) -> ServerRet +%% @doc application start callback for mochiweb. +start(_Type, _StartArgs) -> + mochiweb_sup:start_link(). + +%% @spec stop(_State) -> ServerRet +%% @doc application stop callback for mochiweb. +stop(_State) -> + ok. diff --git a/web/api/webmachine/deps/mochiweb/src/mochiweb_charref.erl b/web/api/webmachine/deps/mochiweb/src/mochiweb_charref.erl new file mode 100644 index 0000000..59fd4a4 --- /dev/null +++ b/web/api/webmachine/deps/mochiweb/src/mochiweb_charref.erl @@ -0,0 +1,295 @@ +%% @author Bob Ippolito +%% @copyright 2007 Mochi Media, Inc. + +%% @doc Converts HTML 4 charrefs and entities to codepoints. +-module(mochiweb_charref). +-export([charref/1, test/0]). + +%% External API. + +%% @spec charref(S) -> integer() | undefined +%% @doc Convert a decimal charref, hex charref, or html entity to a unicode +%% codepoint, or return undefined on failure. +%% The input should not include an ampersand or semicolon. +%% charref("#38") = 38, charref("#x26") = 38, charref("amp") = 38. +charref(B) when is_binary(B) -> + charref(binary_to_list(B)); +charref([$#, C | L]) when C =:= $x orelse C =:= $X -> + try erlang:list_to_integer(L, 16) + catch + error:badarg -> undefined + end; +charref([$# | L]) -> + try list_to_integer(L) + catch + error:badarg -> undefined + end; +charref(L) -> + entity(L). + +%% @spec test() -> ok +%% @doc Run tests for mochiweb_charref. +test() -> + 1234 = charref("#1234"), + 255 = charref("#xfF"), + 255 = charref("#XFf"), + 38 = charref("amp"), + undefined = charref("not_an_entity"), + ok. + +%% Internal API. + +entity("nbsp") -> 160; +entity("iexcl") -> 161; +entity("cent") -> 162; +entity("pound") -> 163; +entity("curren") -> 164; +entity("yen") -> 165; +entity("brvbar") -> 166; +entity("sect") -> 167; +entity("uml") -> 168; +entity("copy") -> 169; +entity("ordf") -> 170; +entity("laquo") -> 171; +entity("not") -> 172; +entity("shy") -> 173; +entity("reg") -> 174; +entity("macr") -> 175; +entity("deg") -> 176; +entity("plusmn") -> 177; +entity("sup2") -> 178; +entity("sup3") -> 179; +entity("acute") -> 180; +entity("micro") -> 181; +entity("para") -> 182; +entity("middot") -> 183; +entity("cedil") -> 184; +entity("sup1") -> 185; +entity("ordm") -> 186; +entity("raquo") -> 187; +entity("frac14") -> 188; +entity("frac12") -> 189; +entity("frac34") -> 190; +entity("iquest") -> 191; +entity("Agrave") -> 192; +entity("Aacute") -> 193; +entity("Acirc") -> 194; +entity("Atilde") -> 195; +entity("Auml") -> 196; +entity("Aring") -> 197; +entity("AElig") -> 198; +entity("Ccedil") -> 199; +entity("Egrave") -> 200; +entity("Eacute") -> 201; +entity("Ecirc") -> 202; +entity("Euml") -> 203; +entity("Igrave") -> 204; +entity("Iacute") -> 205; +entity("Icirc") -> 206; +entity("Iuml") -> 207; +entity("ETH") -> 208; +entity("Ntilde") -> 209; +entity("Ograve") -> 210; +entity("Oacute") -> 211; +entity("Ocirc") -> 212; +entity("Otilde") -> 213; +entity("Ouml") -> 214; +entity("times") -> 215; +entity("Oslash") -> 216; +entity("Ugrave") -> 217; +entity("Uacute") -> 218; +entity("Ucirc") -> 219; +entity("Uuml") -> 220; +entity("Yacute") -> 221; +entity("THORN") -> 222; +entity("szlig") -> 223; +entity("agrave") -> 224; +entity("aacute") -> 225; +entity("acirc") -> 226; +entity("atilde") -> 227; +entity("auml") -> 228; +entity("aring") -> 229; +entity("aelig") -> 230; +entity("ccedil") -> 231; +entity("egrave") -> 232; +entity("eacute") -> 233; +entity("ecirc") -> 234; +entity("euml") -> 235; +entity("igrave") -> 236; +entity("iacute") -> 237; +entity("icirc") -> 238; +entity("iuml") -> 239; +entity("eth") -> 240; +entity("ntilde") -> 241; +entity("ograve") -> 242; +entity("oacute") -> 243; +entity("ocirc") -> 244; +entity("otilde") -> 245; +entity("ouml") -> 246; +entity("divide") -> 247; +entity("oslash") -> 248; +entity("ugrave") -> 249; +entity("uacute") -> 250; +entity("ucirc") -> 251; +entity("uuml") -> 252; +entity("yacute") -> 253; +entity("thorn") -> 254; +entity("yuml") -> 255; +entity("fnof") -> 402; +entity("Alpha") -> 913; +entity("Beta") -> 914; +entity("Gamma") -> 915; +entity("Delta") -> 916; +entity("Epsilon") -> 917; +entity("Zeta") -> 918; +entity("Eta") -> 919; +entity("Theta") -> 920; +entity("Iota") -> 921; +entity("Kappa") -> 922; +entity("Lambda") -> 923; +entity("Mu") -> 924; +entity("Nu") -> 925; +entity("Xi") -> 926; +entity("Omicron") -> 927; +entity("Pi") -> 928; +entity("Rho") -> 929; +entity("Sigma") -> 931; +entity("Tau") -> 932; +entity("Upsilon") -> 933; +entity("Phi") -> 934; +entity("Chi") -> 935; +entity("Psi") -> 936; +entity("Omega") -> 937; +entity("alpha") -> 945; +entity("beta") -> 946; +entity("gamma") -> 947; +entity("delta") -> 948; +entity("epsilon") -> 949; +entity("zeta") -> 950; +entity("eta") -> 951; +entity("theta") -> 952; +entity("iota") -> 953; +entity("kappa") -> 954; +entity("lambda") -> 955; +entity("mu") -> 956; +entity("nu") -> 957; +entity("xi") -> 958; +entity("omicron") -> 959; +entity("pi") -> 960; +entity("rho") -> 961; +entity("sigmaf") -> 962; +entity("sigma") -> 963; +entity("tau") -> 964; +entity("upsilon") -> 965; +entity("phi") -> 966; +entity("chi") -> 967; +entity("psi") -> 968; +entity("omega") -> 969; +entity("thetasym") -> 977; +entity("upsih") -> 978; +entity("piv") -> 982; +entity("bull") -> 8226; +entity("hellip") -> 8230; +entity("prime") -> 8242; +entity("Prime") -> 8243; +entity("oline") -> 8254; +entity("frasl") -> 8260; +entity("weierp") -> 8472; +entity("image") -> 8465; +entity("real") -> 8476; +entity("trade") -> 8482; +entity("alefsym") -> 8501; +entity("larr") -> 8592; +entity("uarr") -> 8593; +entity("rarr") -> 8594; +entity("darr") -> 8595; +entity("harr") -> 8596; +entity("crarr") -> 8629; +entity("lArr") -> 8656; +entity("uArr") -> 8657; +entity("rArr") -> 8658; +entity("dArr") -> 8659; +entity("hArr") -> 8660; +entity("forall") -> 8704; +entity("part") -> 8706; +entity("exist") -> 8707; +entity("empty") -> 8709; +entity("nabla") -> 8711; +entity("isin") -> 8712; +entity("notin") -> 8713; +entity("ni") -> 8715; +entity("prod") -> 8719; +entity("sum") -> 8721; +entity("minus") -> 8722; +entity("lowast") -> 8727; +entity("radic") -> 8730; +entity("prop") -> 8733; +entity("infin") -> 8734; +entity("ang") -> 8736; +entity("and") -> 8743; +entity("or") -> 8744; +entity("cap") -> 8745; +entity("cup") -> 8746; +entity("int") -> 8747; +entity("there4") -> 8756; +entity("sim") -> 8764; +entity("cong") -> 8773; +entity("asymp") -> 8776; +entity("ne") -> 8800; +entity("equiv") -> 8801; +entity("le") -> 8804; +entity("ge") -> 8805; +entity("sub") -> 8834; +entity("sup") -> 8835; +entity("nsub") -> 8836; +entity("sube") -> 8838; +entity("supe") -> 8839; +entity("oplus") -> 8853; +entity("otimes") -> 8855; +entity("perp") -> 8869; +entity("sdot") -> 8901; +entity("lceil") -> 8968; +entity("rceil") -> 8969; +entity("lfloor") -> 8970; +entity("rfloor") -> 8971; +entity("lang") -> 9001; +entity("rang") -> 9002; +entity("loz") -> 9674; +entity("spades") -> 9824; +entity("clubs") -> 9827; +entity("hearts") -> 9829; +entity("diams") -> 9830; +entity("quot") -> 34; +entity("amp") -> 38; +entity("lt") -> 60; +entity("gt") -> 62; +entity("OElig") -> 338; +entity("oelig") -> 339; +entity("Scaron") -> 352; +entity("scaron") -> 353; +entity("Yuml") -> 376; +entity("circ") -> 710; +entity("tilde") -> 732; +entity("ensp") -> 8194; +entity("emsp") -> 8195; +entity("thinsp") -> 8201; +entity("zwnj") -> 8204; +entity("zwj") -> 8205; +entity("lrm") -> 8206; +entity("rlm") -> 8207; +entity("ndash") -> 8211; +entity("mdash") -> 8212; +entity("lsquo") -> 8216; +entity("rsquo") -> 8217; +entity("sbquo") -> 8218; +entity("ldquo") -> 8220; +entity("rdquo") -> 8221; +entity("bdquo") -> 8222; +entity("dagger") -> 8224; +entity("Dagger") -> 8225; +entity("permil") -> 8240; +entity("lsaquo") -> 8249; +entity("rsaquo") -> 8250; +entity("euro") -> 8364; +entity(_) -> undefined. + diff --git a/web/api/webmachine/deps/mochiweb/src/mochiweb_cookies.erl b/web/api/webmachine/deps/mochiweb/src/mochiweb_cookies.erl new file mode 100644 index 0000000..a5a7f37 --- /dev/null +++ b/web/api/webmachine/deps/mochiweb/src/mochiweb_cookies.erl @@ -0,0 +1,257 @@ +%% @author Emad El-Haraty +%% @copyright 2007 Mochi Media, Inc. + +%% @doc HTTP Cookie parsing and generating (RFC 2109, RFC 2965). + +-module(mochiweb_cookies). +-export([parse_cookie/1, cookie/3, cookie/2, test/0]). + +-define(QUOTE, $\"). + +-define(IS_WHITESPACE(C), + (C =:= $\s orelse C =:= $\t orelse C =:= $\r orelse C =:= $\n)). + +%% RFC 2616 separators (called tspecials in RFC 2068) +-define(IS_SEPARATOR(C), + (C < 32 orelse + C =:= $\s orelse C =:= $\t orelse + C =:= $( orelse C =:= $) orelse C =:= $< orelse C =:= $> orelse + C =:= $@ orelse C =:= $, orelse C =:= $; orelse C =:= $: orelse + C =:= $\\ orelse C =:= $\" orelse C =:= $/ orelse + C =:= $[ orelse C =:= $] orelse C =:= $? orelse C =:= $= orelse + C =:= ${ orelse C =:= $})). + +%% @type proplist() = [{Key::string(), Value::string()}]. +%% @type header() = {Name::string(), Value::string()}. + +%% @spec cookie(Key::string(), Value::string()) -> header() +%% @doc Short-hand for cookie(Key, Value, []). +cookie(Key, Value) -> + cookie(Key, Value, []). + +%% @spec cookie(Key::string(), Value::string(), Options::[Option]) -> header() +%% where Option = {max_age, integer()} | {local_time, {date(), time()}} +%% | {domain, string()} | {path, string()} +%% | {secure, true | false} | {http_only, true | false} +%% +%% @doc Generate a Set-Cookie header field tuple. +cookie(Key, Value, Options) -> + Cookie = [any_to_list(Key), "=", quote(Value), "; Version=1"], + %% Set-Cookie: + %% Comment, Domain, Max-Age, Path, Secure, Version + %% Set-Cookie2: + %% Comment, CommentURL, Discard, Domain, Max-Age, Path, Port, Secure, + %% Version + ExpiresPart = + case proplists:get_value(max_age, Options) of + undefined -> + ""; + RawAge -> + When = case proplists:get_value(local_time, Options) of + undefined -> + calendar:local_time(); + LocalTime -> + LocalTime + end, + Age = case RawAge < 0 of + true -> + 0; + false -> + RawAge + end, + ["; Expires=", age_to_cookie_date(Age, When), + "; Max-Age=", quote(Age)] + end, + SecurePart = + case proplists:get_value(secure, Options) of + true -> + "; Secure"; + _ -> + "" + end, + DomainPart = + case proplists:get_value(domain, Options) of + undefined -> + ""; + Domain -> + ["; Domain=", quote(Domain)] + end, + PathPart = + case proplists:get_value(path, Options) of + undefined -> + ""; + Path -> + ["; Path=", quote(Path)] + end, + HttpOnlyPart = + case proplists:get_value(http_only, Options) of + true -> + "; HttpOnly"; + _ -> + "" + end, + CookieParts = [Cookie, ExpiresPart, SecurePart, DomainPart, PathPart, HttpOnlyPart], + {"Set-Cookie", lists:flatten(CookieParts)}. + + +%% Every major browser incorrectly handles quoted strings in a +%% different and (worse) incompatible manner. Instead of wasting time +%% writing redundant code for each browser, we restrict cookies to +%% only contain characters that browsers handle compatibly. +%% +%% By replacing the definition of quote with this, we generate +%% RFC-compliant cookies: +%% +%% quote(V) -> +%% Fun = fun(?QUOTE, Acc) -> [$\\, ?QUOTE | Acc]; +%% (Ch, Acc) -> [Ch | Acc] +%% end, +%% [?QUOTE | lists:foldr(Fun, [?QUOTE], V)]. + +%% Convert to a string and raise an error if quoting is required. +quote(V0) -> + V = any_to_list(V0), + lists:all(fun(Ch) -> Ch =:= $/ orelse not ?IS_SEPARATOR(Ch) end, V) + orelse erlang:error({cookie_quoting_required, V}), + V. + +add_seconds(Secs, LocalTime) -> + Greg = calendar:datetime_to_gregorian_seconds(LocalTime), + calendar:gregorian_seconds_to_datetime(Greg + Secs). + +age_to_cookie_date(Age, LocalTime) -> + httpd_util:rfc1123_date(add_seconds(Age, LocalTime)). + +%% @spec parse_cookie(string()) -> [{K::string(), V::string()}] +%% @doc Parse the contents of a Cookie header field, ignoring cookie +%% attributes, and return a simple property list. +parse_cookie("") -> + []; +parse_cookie(Cookie) -> + parse_cookie(Cookie, []). + +%% @spec test() -> ok +%% @doc Run tests for mochiweb_cookies. +test() -> + parse_cookie_test(), + cookie_test(), + ok. + +%% Internal API + +parse_cookie([], Acc) -> + lists:reverse(Acc); +parse_cookie(String, Acc) -> + {{Token, Value}, Rest} = read_pair(String), + Acc1 = case Token of + "" -> + Acc; + "$" ++ _ -> + Acc; + _ -> + [{Token, Value} | Acc] + end, + parse_cookie(Rest, Acc1). + +read_pair(String) -> + {Token, Rest} = read_token(skip_whitespace(String)), + {Value, Rest1} = read_value(skip_whitespace(Rest)), + {{Token, Value}, skip_past_separator(Rest1)}. + +read_value([$= | Value]) -> + Value1 = skip_whitespace(Value), + case Value1 of + [?QUOTE | _] -> + read_quoted(Value1); + _ -> + read_token(Value1) + end; +read_value(String) -> + {"", String}. + +read_quoted([?QUOTE | String]) -> + read_quoted(String, []). + +read_quoted([], Acc) -> + {lists:reverse(Acc), []}; +read_quoted([?QUOTE | Rest], Acc) -> + {lists:reverse(Acc), Rest}; +read_quoted([$\\, Any | Rest], Acc) -> + read_quoted(Rest, [Any | Acc]); +read_quoted([C | Rest], Acc) -> + read_quoted(Rest, [C | Acc]). + +skip_whitespace(String) -> + F = fun (C) -> ?IS_WHITESPACE(C) end, + lists:dropwhile(F, String). + +read_token(String) -> + F = fun (C) -> not ?IS_SEPARATOR(C) end, + lists:splitwith(F, String). + +skip_past_separator([]) -> + []; +skip_past_separator([$; | Rest]) -> + Rest; +skip_past_separator([$, | Rest]) -> + Rest; +skip_past_separator([_ | Rest]) -> + skip_past_separator(Rest). + +parse_cookie_test() -> + %% RFC example + C1 = "$Version=\"1\"; Customer=\"WILE_E_COYOTE\"; $Path=\"/acme\"; + Part_Number=\"Rocket_Launcher_0001\"; $Path=\"/acme\"; + Shipping=\"FedEx\"; $Path=\"/acme\"", + [ + {"Customer","WILE_E_COYOTE"}, + {"Part_Number","Rocket_Launcher_0001"}, + {"Shipping","FedEx"} + ] = parse_cookie(C1), + %% Potential edge cases + [{"foo", "x"}] = parse_cookie("foo=\"\\x\""), + [] = parse_cookie("="), + [{"foo", ""}, {"bar", ""}] = parse_cookie(" foo ; bar "), + [{"foo", ""}, {"bar", ""}] = parse_cookie("foo=;bar="), + [{"foo", "\";"}, {"bar", ""}] = parse_cookie("foo = \"\\\";\";bar "), + [{"foo", "\";bar"}] = parse_cookie("foo=\"\\\";bar"). + +any_to_list(V) when is_list(V) -> + V; +any_to_list(V) when is_atom(V) -> + atom_to_list(V); +any_to_list(V) when is_binary(V) -> + binary_to_list(V); +any_to_list(V) when is_integer(V) -> + integer_to_list(V). + + +cookie_test() -> + C1 = {"Set-Cookie", + "Customer=WILE_E_COYOTE; " + "Version=1; " + "Path=/acme"}, + C1 = cookie("Customer", "WILE_E_COYOTE", [{path, "/acme"}]), + C1 = cookie("Customer", "WILE_E_COYOTE", + [{path, "/acme"}, {badoption, "negatory"}]), + C1 = cookie('Customer', 'WILE_E_COYOTE', [{path, '/acme'}]), + C1 = cookie(<<"Customer">>, <<"WILE_E_COYOTE">>, [{path, <<"/acme">>}]), + + {"Set-Cookie","=NoKey; Version=1"} = cookie("", "NoKey", []), + + LocalTime = calendar:universal_time_to_local_time({{2007, 5, 15}, {13, 45, 33}}), + C2 = {"Set-Cookie", + "Customer=WILE_E_COYOTE; " + "Version=1; " + "Expires=Tue, 15 May 2007 13:45:33 GMT; " + "Max-Age=0"}, + C2 = cookie("Customer", "WILE_E_COYOTE", + [{max_age, -111}, {local_time, LocalTime}]), + C3 = {"Set-Cookie", + "Customer=WILE_E_COYOTE; " + "Version=1; " + "Expires=Wed, 16 May 2007 13:45:50 GMT; " + "Max-Age=86417"}, + C3 = cookie("Customer", "WILE_E_COYOTE", + [{max_age, 86417}, {local_time, LocalTime}]), + ok. diff --git a/web/api/webmachine/deps/mochiweb/src/mochiweb_echo.erl b/web/api/webmachine/deps/mochiweb/src/mochiweb_echo.erl new file mode 100644 index 0000000..f0c455a --- /dev/null +++ b/web/api/webmachine/deps/mochiweb/src/mochiweb_echo.erl @@ -0,0 +1,31 @@ +%% @author Bob Ippolito +%% @copyright 2007 Mochi Media, Inc. + +%% @doc Simple and stupid echo server to demo mochiweb_socket_server. + +-module(mochiweb_echo). +-author('bob@mochimedia.com'). +-export([start/0, stop/0, loop/1]). + +stop() -> + mochiweb_socket_server:stop(?MODULE). + +start() -> + mochiweb_socket_server:start([{name, ?MODULE}, + {port, 6789}, + {ip, "127.0.0.1"}, + {max, 1}, + {loop, {?MODULE, loop}}]). + +loop(Socket) -> + case gen_tcp:recv(Socket, 0, 30000) of + {ok, Data} -> + case gen_tcp:send(Socket, Data) of + ok -> + loop(Socket); + _ -> + exit(normal) + end; + _Other -> + exit(normal) + end. diff --git a/web/api/webmachine/deps/mochiweb/src/mochiweb_headers.erl b/web/api/webmachine/deps/mochiweb/src/mochiweb_headers.erl new file mode 100644 index 0000000..576b643 --- /dev/null +++ b/web/api/webmachine/deps/mochiweb/src/mochiweb_headers.erl @@ -0,0 +1,186 @@ +%% @author Bob Ippolito +%% @copyright 2007 Mochi Media, Inc. + +%% @doc Case preserving (but case insensitive) HTTP Header dictionary. + +-module(mochiweb_headers). +-author('bob@mochimedia.com'). +-export([empty/0, from_list/1, insert/3, enter/3, get_value/2, lookup/2]). +-export([delete_any/2, get_primary_value/2]). +-export([default/3, enter_from_list/2, default_from_list/2]). +-export([to_list/1, make/1]). +-export([test/0]). + +%% @type headers(). +%% @type key() = atom() | binary() | string(). +%% @type value() = atom() | binary() | string() | integer(). + +%% @spec test() -> ok +%% @doc Run tests for this module. +test() -> + H = ?MODULE:make([{hdr, foo}, {"Hdr", "bar"}, {'Hdr', 2}]), + [{hdr, "foo, bar, 2"}] = ?MODULE:to_list(H), + H1 = ?MODULE:insert(taco, grande, H), + [{hdr, "foo, bar, 2"}, {taco, "grande"}] = ?MODULE:to_list(H1), + H2 = ?MODULE:make([{"Set-Cookie", "foo"}]), + [{"Set-Cookie", "foo"}] = ?MODULE:to_list(H2), + H3 = ?MODULE:insert("Set-Cookie", "bar", H2), + [{"Set-Cookie", "foo"}, {"Set-Cookie", "bar"}] = ?MODULE:to_list(H3), + "foo, bar" = ?MODULE:get_value("set-cookie", H3), + {value, {"Set-Cookie", "foo, bar"}} = ?MODULE:lookup("set-cookie", H3), + undefined = ?MODULE:get_value("shibby", H3), + none = ?MODULE:lookup("shibby", H3), + H4 = ?MODULE:insert("content-type", + "application/x-www-form-urlencoded; charset=utf8", + H3), + "application/x-www-form-urlencoded" = ?MODULE:get_primary_value( + "content-type", H4), + H4 = ?MODULE:delete_any("nonexistent-header", H4), + H3 = ?MODULE:delete_any("content-type", H4), + ok. + +%% @spec empty() -> headers() +%% @doc Create an empty headers structure. +empty() -> + gb_trees:empty(). + +%% @spec make(headers() | [{key(), value()}]) -> headers() +%% @doc Construct a headers() from the given list. +make(L) when is_list(L) -> + from_list(L); +%% assume a tuple is already mochiweb_headers. +make(T) when is_tuple(T) -> + T. + +%% @spec from_list([{key(), value()}]) -> headers() +%% @doc Construct a headers() from the given list. +from_list(List) -> + lists:foldl(fun ({K, V}, T) -> insert(K, V, T) end, empty(), List). + +%% @spec enter_from_list([{key(), value()}], headers()) -> headers() +%% @doc Insert pairs into the headers, replace any values for existing keys. +enter_from_list(List, T) -> + lists:foldl(fun ({K, V}, T1) -> enter(K, V, T1) end, T, List). + +%% @spec default_from_list([{key(), value()}], headers()) -> headers() +%% @doc Insert pairs into the headers for keys that do not already exist. +default_from_list(List, T) -> + lists:foldl(fun ({K, V}, T1) -> default(K, V, T1) end, T, List). + +%% @spec to_list(headers()) -> [{key(), string()}] +%% @doc Return the contents of the headers. The keys will be the exact key +%% that was first inserted (e.g. may be an atom or binary, case is +%% preserved). +to_list(T) -> + F = fun ({K, {array, L}}, Acc) -> + L1 = lists:reverse(L), + lists:foldl(fun (V, Acc1) -> [{K, V} | Acc1] end, Acc, L1); + (Pair, Acc) -> + [Pair | Acc] + end, + lists:reverse(lists:foldl(F, [], gb_trees:values(T))). + +%% @spec get_value(key(), headers()) -> string() | undefined +%% @doc Return the value of the given header using a case insensitive search. +%% undefined will be returned for keys that are not present. +get_value(K, T) -> + case lookup(K, T) of + {value, {_, V}} -> + expand(V); + none -> + undefined + end. + +%% @spec get_primary_value(key(), headers()) -> string() | undefined +%% @doc Return the value of the given header up to the first semicolon using +%% a case insensitive search. undefined will be returned for keys +%% that are not present. +get_primary_value(K, T) -> + case get_value(K, T) of + undefined -> + undefined; + V -> + lists:takewhile(fun (C) -> C =/= $; end, V) + end. + +%% @spec lookup(key(), headers()) -> {value, {key(), string()}} | none +%% @doc Return the case preserved key and value for the given header using +%% a case insensitive search. none will be returned for keys that are +%% not present. +lookup(K, T) -> + case gb_trees:lookup(normalize(K), T) of + {value, {K0, V}} -> + {value, {K0, expand(V)}}; + none -> + none + end. + +%% @spec default(key(), value(), headers()) -> headers() +%% @doc Insert the pair into the headers if it does not already exist. +default(K, V, T) -> + K1 = normalize(K), + V1 = any_to_list(V), + try gb_trees:insert(K1, {K, V1}, T) + catch + error:{key_exists, _} -> + T + end. + +%% @spec enter(key(), value(), headers()) -> headers() +%% @doc Insert the pair into the headers, replacing any pre-existing key. +enter(K, V, T) -> + K1 = normalize(K), + V1 = any_to_list(V), + gb_trees:enter(K1, {K, V1}, T). + +%% @spec insert(key(), value(), headers()) -> headers() +%% @doc Insert the pair into the headers, merging with any pre-existing key. +%% A merge is done with Value = V0 ++ ", " ++ V1. +insert(K, V, T) -> + K1 = normalize(K), + V1 = any_to_list(V), + try gb_trees:insert(K1, {K, V1}, T) + catch + error:{key_exists, _} -> + {K0, V0} = gb_trees:get(K1, T), + V2 = merge(K1, V1, V0), + gb_trees:update(K1, {K0, V2}, T) + end. + +%% @spec delete_any(key(), headers()) -> headers() +%% @doc Delete the header corresponding to key if it is present. +delete_any(K, T) -> + K1 = normalize(K), + gb_trees:delete_any(K1, T). + +%% Internal API + +expand({array, L}) -> + mochiweb_util:join(lists:reverse(L), ", "); +expand(V) -> + V. + +merge("set-cookie", V1, {array, L}) -> + {array, [V1 | L]}; +merge("set-cookie", V1, V0) -> + {array, [V1, V0]}; +merge(_, V1, V0) -> + V0 ++ ", " ++ V1. + +normalize(K) when is_list(K) -> + string:to_lower(K); +normalize(K) when is_atom(K) -> + normalize(atom_to_list(K)); +normalize(K) when is_binary(K) -> + normalize(binary_to_list(K)). + +any_to_list(V) when is_list(V) -> + V; +any_to_list(V) when is_atom(V) -> + atom_to_list(V); +any_to_list(V) when is_binary(V) -> + binary_to_list(V); +any_to_list(V) when is_integer(V) -> + integer_to_list(V). + + diff --git a/web/api/webmachine/deps/mochiweb/src/mochiweb_html.erl b/web/api/webmachine/deps/mochiweb/src/mochiweb_html.erl new file mode 100644 index 0000000..b4c8138 --- /dev/null +++ b/web/api/webmachine/deps/mochiweb/src/mochiweb_html.erl @@ -0,0 +1,893 @@ +%% @author Bob Ippolito +%% @copyright 2007 Mochi Media, Inc. + +%% @doc Loosely tokenizes and generates parse trees for HTML 4. +-module(mochiweb_html). +-export([tokens/1, parse/1, parse_tokens/1, to_tokens/1, escape/1, + escape_attr/1, to_html/1, test/0]). + +% This is a macro to placate syntax highlighters.. +-define(QUOTE, $\"). +-define(SQUOTE, $\'). +-define(ADV_COL(S, N), + S#decoder{column=N+S#decoder.column, + offset=N+S#decoder.offset}). +-define(INC_COL(S), + S#decoder{column=1+S#decoder.column, + offset=1+S#decoder.offset}). +-define(INC_LINE(S), + S#decoder{column=1, + line=1+S#decoder.line, + offset=1+S#decoder.offset}). +-define(INC_CHAR(S, C), + case C of + $\n -> + S#decoder{column=1, + line=1+S#decoder.line, + offset=1+S#decoder.offset}; + _ -> + S#decoder{column=1+S#decoder.column, + offset=1+S#decoder.offset} + end). + +-define(IS_WHITESPACE(C), + (C =:= $\s orelse C =:= $\t orelse C =:= $\r orelse C =:= $\n)). +-define(IS_LITERAL_SAFE(C), + ((C >= $A andalso C =< $Z) orelse (C >= $a andalso C =< $z) + orelse (C >= $0 andalso C =< $9))). + +-record(decoder, {line=1, + column=1, + offset=0}). + +%% @type html_node() = {string(), [html_attr()], [html_node() | string()]} +%% @type html_attr() = {string(), string()} +%% @type html_token() = html_data() | start_tag() | end_tag() | inline_html() | html_comment() | html_doctype() +%% @type html_data() = {data, string(), Whitespace::boolean()} +%% @type start_tag() = {start_tag, Name, [html_attr()], Singleton::boolean()} +%% @type end_tag() = {end_tag, Name} +%% @type html_comment() = {comment, Comment} +%% @type html_doctype() = {doctype, [Doctype]} +%% @type inline_html() = {'=', iolist()} + +%% External API. + +%% @spec parse(string() | binary()) -> html_node() +%% @doc tokenize and then transform the token stream into a HTML tree. +parse(Input) -> + parse_tokens(tokens(Input)). + +%% @spec parse_tokens([html_token()]) -> html_node() +%% @doc Transform the output of tokens(Doc) into a HTML tree. +parse_tokens(Tokens) when is_list(Tokens) -> + %% Skip over doctype, processing instructions + F = fun (X) -> + case X of + {start_tag, _, _, false} -> + false; + _ -> + true + end + end, + [{start_tag, Tag, Attrs, false} | Rest] = lists:dropwhile(F, Tokens), + {Tree, _} = tree(Rest, [norm({Tag, Attrs})]), + Tree. + +%% @spec tokens(StringOrBinary) -> [html_token()] +%% @doc Transform the input UTF-8 HTML into a token stream. +tokens(Input) -> + tokens(iolist_to_binary(Input), #decoder{}, []). + +%% @spec to_tokens(html_node()) -> [html_token()] +%% @doc Convert a html_node() tree to a list of tokens. +to_tokens({Tag0}) -> + to_tokens({Tag0, [], []}); +to_tokens(T={'=', _}) -> + [T]; +to_tokens(T={doctype, _}) -> + [T]; +to_tokens(T={comment, _}) -> + [T]; +to_tokens({Tag0, Acc}) -> + to_tokens({Tag0, [], Acc}); +to_tokens({Tag0, Attrs, Acc}) -> + Tag = to_tag(Tag0), + to_tokens([{Tag, Acc}], [{start_tag, Tag, Attrs, is_singleton(Tag)}]). + +%% @spec to_html([html_token()] | html_node()) -> iolist() +%% @doc Convert a list of html_token() to a HTML document. +to_html(Node) when is_tuple(Node) -> + to_html(to_tokens(Node)); +to_html(Tokens) when is_list(Tokens) -> + to_html(Tokens, []). + +%% @spec escape(string() | atom() | binary()) -> binary() +%% @doc Escape a string such that it's safe for HTML (amp; lt; gt;). +escape(B) when is_binary(B) -> + escape(binary_to_list(B), []); +escape(A) when is_atom(A) -> + escape(atom_to_list(A), []); +escape(S) when is_list(S) -> + escape(S, []). + +%% @spec escape_attr(string() | binary() | atom() | integer() | float()) -> binary() +%% @doc Escape a string such that it's safe for HTML attrs +%% (amp; lt; gt; quot;). +escape_attr(B) when is_binary(B) -> + escape_attr(binary_to_list(B), []); +escape_attr(A) when is_atom(A) -> + escape_attr(atom_to_list(A), []); +escape_attr(S) when is_list(S) -> + escape_attr(S, []); +escape_attr(I) when is_integer(I) -> + escape_attr(integer_to_list(I), []); +escape_attr(F) when is_float(F) -> + escape_attr(mochinum:digits(F), []). + +%% @spec test() -> ok +%% @doc Run tests for mochiweb_html. +test() -> + test_destack(), + test_tokens(), + test_tokens2(), + test_parse(), + test_parse2(), + test_parse_tokens(), + test_escape(), + test_escape_attr(), + test_to_html(), + ok. + + +%% Internal API + +test_to_html() -> + Expect = <<"hey!

what's up

sucka
">>, + Expect = iolist_to_binary( + to_html({html, [], + [{<<"head">>, [], + [{title, <<"hey!">>}]}, + {body, [], + [{p, [{class, foo}], [<<"what's">>, <<" up">>, {br}]}, + {'div', <<"sucka">>}, + {comment, <<" comment! ">>}]}]})), + Expect1 = <<"">>, + Expect1 = iolist_to_binary( + to_html({doctype, + [<<"html">>, <<"PUBLIC">>, + <<"-//W3C//DTD XHTML 1.0 Transitional//EN">>, + <<"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">>]})), + ok. +to_html([], Acc) -> + lists:reverse(Acc); +to_html([{'=', Content} | Rest], Acc) -> + to_html(Rest, [Content | Acc]); +to_html([{pi, Tag, Attrs} | Rest], Acc) -> + Open = [<<">, + Tag, + attrs_to_html(Attrs, []), + <<"?>">>], + to_html(Rest, [Open | Acc]); +to_html([{comment, Comment} | Rest], Acc) -> + to_html(Rest, [[<<"">>] | Acc]); +to_html([{doctype, Parts} | Rest], Acc) -> + Inside = doctype_to_html(Parts, Acc), + to_html(Rest, [[<<">, Inside, <<">">>] | Acc]); +to_html([{data, Data, _Whitespace} | Rest], Acc) -> + to_html(Rest, [escape(Data) | Acc]); +to_html([{start_tag, Tag, Attrs, Singleton} | Rest], Acc) -> + Open = [<<"<">>, + Tag, + attrs_to_html(Attrs, []), + case Singleton of + true -> <<" />">>; + false -> <<">">> + end], + to_html(Rest, [Open | Acc]); +to_html([{end_tag, Tag} | Rest], Acc) -> + to_html(Rest, [[<<">, Tag, <<">">>] | Acc]). + +doctype_to_html([], Acc) -> + lists:reverse(Acc); +doctype_to_html([Word | Rest], Acc) -> + case lists:all(fun (C) -> ?IS_LITERAL_SAFE(C) end, + binary_to_list(iolist_to_binary(Word))) of + true -> + doctype_to_html(Rest, [[<<" ">>, Word] | Acc]); + false -> + doctype_to_html(Rest, [[<<" \"">>, escape_attr(Word), ?QUOTE] | Acc]) + end. + +attrs_to_html([], Acc) -> + lists:reverse(Acc); +attrs_to_html([{K, V} | Rest], Acc) -> + attrs_to_html(Rest, + [[<<" ">>, escape(K), <<"=\"">>, + escape_attr(V), <<"\"">>] | Acc]). + +test_escape() -> + <<"&quot;\"word <<up!&quot;">> = + escape(<<""\"word <>), + ok. + +test_escape_attr() -> + <<"&quot;"word <<up!&quot;">> = + escape_attr(<<""\"word <>), + ok. + +escape([], Acc) -> + list_to_binary(lists:reverse(Acc)); +escape("<" ++ Rest, Acc) -> + escape(Rest, lists:reverse("<", Acc)); +escape(">" ++ Rest, Acc) -> + escape(Rest, lists:reverse(">", Acc)); +escape("&" ++ Rest, Acc) -> + escape(Rest, lists:reverse("&", Acc)); +escape([C | Rest], Acc) -> + escape(Rest, [C | Acc]). + +escape_attr([], Acc) -> + list_to_binary(lists:reverse(Acc)); +escape_attr("<" ++ Rest, Acc) -> + escape_attr(Rest, lists:reverse("<", Acc)); +escape_attr(">" ++ Rest, Acc) -> + escape_attr(Rest, lists:reverse(">", Acc)); +escape_attr("&" ++ Rest, Acc) -> + escape_attr(Rest, lists:reverse("&", Acc)); +escape_attr([?QUOTE | Rest], Acc) -> + escape_attr(Rest, lists:reverse(""", Acc)); +escape_attr([C | Rest], Acc) -> + escape_attr(Rest, [C | Acc]). + +to_tag(A) when is_atom(A) -> + norm(atom_to_list(A)); +to_tag(L) -> + norm(L). + +to_tokens([], Acc) -> + lists:reverse(Acc); +to_tokens([{Tag, []} | Rest], Acc) -> + to_tokens(Rest, [{end_tag, to_tag(Tag)} | Acc]); +to_tokens([{Tag0, [{T0} | R1]} | Rest], Acc) -> + %% Allow {br} + to_tokens([{Tag0, [{T0, [], []} | R1]} | Rest], Acc); +to_tokens([{Tag0, [T0={'=', _C0} | R1]} | Rest], Acc) -> + %% Allow {'=', iolist()} + to_tokens([{Tag0, R1} | Rest], [T0 | Acc]); +to_tokens([{Tag0, [T0={comment, _C0} | R1]} | Rest], Acc) -> + %% Allow {comment, iolist()} + to_tokens([{Tag0, R1} | Rest], [T0 | Acc]); +to_tokens([{Tag0, [{T0, A0=[{_, _} | _]} | R1]} | Rest], Acc) -> + %% Allow {p, [{"class", "foo"}]} + to_tokens([{Tag0, [{T0, A0, []} | R1]} | Rest], Acc); +to_tokens([{Tag0, [{T0, C0} | R1]} | Rest], Acc) -> + %% Allow {p, "content"} and {p, <<"content">>} + to_tokens([{Tag0, [{T0, [], C0} | R1]} | Rest], Acc); +to_tokens([{Tag0, [{T0, A1, C0} | R1]} | Rest], Acc) when is_binary(C0) -> + %% Allow {"p", [{"class", "foo"}], <<"content">>} + to_tokens([{Tag0, [{T0, A1, binary_to_list(C0)} | R1]} | Rest], Acc); +to_tokens([{Tag0, [{T0, A1, C0=[C | _]} | R1]} | Rest], Acc) + when is_integer(C) -> + %% Allow {"p", [{"class", "foo"}], "content"} + to_tokens([{Tag0, [{T0, A1, [C0]} | R1]} | Rest], Acc); +to_tokens([{Tag0, [{T0, A1, C1} | R1]} | Rest], Acc) -> + %% Native {"p", [{"class", "foo"}], ["content"]} + Tag = to_tag(Tag0), + T1 = to_tag(T0), + case is_singleton(norm(T1)) of + true -> + to_tokens([{Tag, R1} | Rest], [{start_tag, T1, A1, true} | Acc]); + false -> + to_tokens([{T1, C1}, {Tag, R1} | Rest], + [{start_tag, T1, A1, false} | Acc]) + end; +to_tokens([{Tag0, [L | R1]} | Rest], Acc) when is_list(L) -> + %% List text + Tag = to_tag(Tag0), + to_tokens([{Tag, R1} | Rest], [{data, iolist_to_binary(L), false} | Acc]); +to_tokens([{Tag0, [B | R1]} | Rest], Acc) when is_binary(B) -> + %% Binary text + Tag = to_tag(Tag0), + to_tokens([{Tag, R1} | Rest], [{data, B, false} | Acc]). + +test_tokens() -> + [{start_tag, <<"foo">>, [{<<"bar">>, <<"baz">>}, + {<<"wibble">>, <<"wibble">>}, + {<<"alice">>, <<"bob">>}], true}] = + tokens(<<"">>), + [{start_tag, <<"foo">>, [{<<"bar">>, <<"baz">>}, + {<<"wibble">>, <<"wibble">>}, + {<<"alice">>, <<"bob">>}], true}] = + tokens(<<"">>), + [{comment, <<"[if lt IE 7]>\n\n>}] = + tokens(<<"">>), + [{start_tag, <<"script">>, [{<<"type">>, <<"text/javascript">>}], false}, + {data, <<" A= B <= C ">>, false}, + {end_tag, <<"script">>}] = + tokens(<<"">>), + [{start_tag, <<"script">>, [{<<"type">>, <<"text/javascript">>}], false}, + {data, <<" A= B <= C ">>, false}, + {end_tag, <<"script">>}] = + tokens(<<"">>), + [{start_tag, <<"script">>, [{<<"type">>, <<"text/javascript">>}], false}, + {data, <<" A= B <= C ">>, false}, + {end_tag, <<"script">>}] = + tokens(<<"">>), + [{start_tag, <<"script">>, [{<<"type">>, <<"text/javascript">>}], false}, + {data, <<" A= B <= C ">>, false}, + {end_tag, <<"script">>}] = + tokens(<<"">>), + [{start_tag, <<"textarea">>, [], false}, + {data, <<"">>, false}, + {end_tag, <<"textarea">>}] = + tokens(<<"">>), + ok. + +tokens(B, S=#decoder{offset=O}, Acc) -> + case B of + <<_:O/binary>> -> + lists:reverse(Acc); + _ -> + {Tag, S1} = tokenize(B, S), + case parse_flag(Tag) of + script -> + {Tag2, S2} = tokenize_script(B, S1), + tokens(B, S2, [Tag2, Tag | Acc]); + textarea -> + {Tag2, S2} = tokenize_textarea(B, S1), + tokens(B, S2, [Tag2, Tag | Acc]); + none -> + tokens(B, S1, [Tag | Acc]) + end + end. + +parse_flag({start_tag, B, _, false}) -> + case string:to_lower(binary_to_list(B)) of + "script" -> + script; + "textarea" -> + textarea; + _ -> + none + end; +parse_flag(_) -> + none. + +tokenize(B, S=#decoder{offset=O}) -> + case B of + <<_:O/binary, " + + + + CDATA>>]]> +">>, + Expect = {<<"html">>, [], + [{<<"head">>, [], + [{<<"meta">>, + [{<<"http-equiv">>,<<"Content-Type">>}, + {<<"content">>,<<"text/html; charset=UTF-8">>}], + []}, + {<<"title">>,[],[<<"Foo">>]}, + {<<"link">>, + [{<<"rel">>,<<"stylesheet">>}, + {<<"type">>,<<"text/css">>}, + {<<"href">>,<<"/static/rel/dojo/resources/dojo.css">>}, + {<<"media">>,<<"screen">>}], + []}, + {<<"link">>, + [{<<"rel">>,<<"stylesheet">>}, + {<<"type">>,<<"text/css">>}, + {<<"href">>,<<"/static/foo.css">>}, + {<<"media">>,<<"screen">>}], + []}, + {comment,<<"[if lt IE 7]>\n \n >}, + {<<"link">>, + [{<<"rel">>,<<"icon">>}, + {<<"href">>,<<"/static/images/favicon.ico">>}, + {<<"type">>,<<"image/x-icon">>}], + []}, + {<<"link">>, + [{<<"rel">>,<<"shortcut icon">>}, + {<<"href">>,<<"/static/images/favicon.ico">>}, + {<<"type">>,<<"image/x-icon">>}], + []}]}, + {<<"body">>, + [{<<"id">>,<<"home">>}, + {<<"class">>,<<"tundra">>}], + [<<"<CDATA>>">>]}]}, + Expect = parse(D0), + ok. + +test_tokens2() -> + D0 = <<"from __future__ import *http://bob.pythonmac.orgBob's Rants">>, + Expect = [{start_tag,<<"channel">>,[],false}, + {start_tag,<<"title">>,[],false}, + {data,<<"from __future__ import *">>,false}, + {end_tag,<<"title">>}, + {start_tag,<<"link">>,[],true}, + {data,<<"http://bob.pythonmac.org">>,false}, + {end_tag,<<"link">>}, + {start_tag,<<"description">>,[],false}, + {data,<<"Bob's Rants">>,false}, + {end_tag,<<"description">>}, + {end_tag,<<"channel">>}], + Expect = tokens(D0), + ok. + +test_parse2() -> + D0 = <<"from __future__ import *http://bob.pythonmac.org
fooBob's Rants
">>, + Expect = {<<"channel">>,[], + [{<<"title">>,[],[<<"from __future__ import *">>]}, + {<<"link">>,[],[ + <<"http://bob.pythonmac.org">>, + {<<"br">>,[],[]}, + <<"foo">>]}, + {<<"description">>,[],[<<"Bob's Rants">>]}]}, + Expect = parse(D0), + ok. + +test_parse_tokens() -> + D0 = [{doctype,[<<"HTML">>,<<"PUBLIC">>,<<"-//W3C//DTD HTML 4.01 Transitional//EN">>]}, + {data,<<"\n">>,true}, + {start_tag,<<"html">>,[],false}], + {<<"html">>, [], []} = parse_tokens(D0), + D1 = D0 ++ [{end_tag, <<"html">>}], + {<<"html">>, [], []} = parse_tokens(D1), + D2 = D0 ++ [{start_tag, <<"body">>, [], false}], + {<<"html">>, [], [{<<"body">>, [], []}]} = parse_tokens(D2), + D3 = D0 ++ [{start_tag, <<"head">>, [], false}, + {end_tag, <<"head">>}, + {start_tag, <<"body">>, [], false}], + {<<"html">>, [], [{<<"head">>, [], []}, {<<"body">>, [], []}]} = parse_tokens(D3), + D4 = D3 ++ [{data,<<"\n">>,true}, + {start_tag,<<"div">>,[{<<"class">>,<<"a">>}],false}, + {start_tag,<<"a">>,[{<<"name">>,<<"#anchor">>}],false}, + {end_tag,<<"a">>}, + {end_tag,<<"div">>}, + {start_tag,<<"div">>,[{<<"class">>,<<"b">>}],false}, + {start_tag,<<"div">>,[{<<"class">>,<<"c">>}],false}, + {end_tag,<<"div">>}, + {end_tag,<<"div">>}], + {<<"html">>, [], + [{<<"head">>, [], []}, + {<<"body">>, [], + [{<<"div">>, [{<<"class">>, <<"a">>}], [{<<"a">>, [{<<"name">>, <<"#anchor">>}], []}]}, + {<<"div">>, [{<<"class">>, <<"b">>}], [{<<"div">>, [{<<"class">>, <<"c">>}], []}]} + ]}]} = parse_tokens(D4), + D5 = [{start_tag,<<"html">>,[],false}, + {data,<<"\n">>,true}, + {data,<<"boo">>,false}, + {data,<<"hoo">>,false}, + {data,<<"\n">>,true}, + {end_tag,<<"html">>}], + {<<"html">>, [], [<<"\nboohoo\n">>]} = parse_tokens(D5), + D6 = [{start_tag,<<"html">>,[],false}, + {data,<<"\n">>,true}, + {data,<<"\n">>,true}, + {end_tag,<<"html">>}], + {<<"html">>, [], []} = parse_tokens(D6), + D7 = [{start_tag,<<"html">>,[],false}, + {start_tag,<<"ul">>,[],false}, + {start_tag,<<"li">>,[],false}, + {data,<<"word">>,false}, + {start_tag,<<"li">>,[],false}, + {data,<<"up">>,false}, + {end_tag,<<"li">>}, + {start_tag,<<"li">>,[],false}, + {data,<<"fdsa">>,false}, + {start_tag,<<"br">>,[],true}, + {data,<<"asdf">>,false}, + {end_tag,<<"ul">>}, + {end_tag,<<"html">>}], + {<<"html">>, [], + [{<<"ul">>, [], + [{<<"li">>, [], [<<"word">>]}, + {<<"li">>, [], [<<"up">>]}, + {<<"li">>, [], [<<"fdsa">>,{<<"br">>, [], []}, <<"asdf">>]}]}]} = parse_tokens(D7), + ok. + +tree_data([{data, Data, Whitespace} | Rest], AllWhitespace, Acc) -> + tree_data(Rest, (Whitespace andalso AllWhitespace), [Data | Acc]); +tree_data(Rest, AllWhitespace, Acc) -> + {iolist_to_binary(lists:reverse(Acc)), AllWhitespace, Rest}. + +tree([], Stack) -> + {destack(Stack), []}; +tree([{end_tag, Tag} | Rest], Stack) -> + case destack(norm(Tag), Stack) of + S when is_list(S) -> + tree(Rest, S); + Result -> + {Result, []} + end; +tree([{start_tag, Tag, Attrs, true} | Rest], S) -> + tree(Rest, append_stack_child(norm({Tag, Attrs}), S)); +tree([{start_tag, Tag, Attrs, false} | Rest], S) -> + tree(Rest, stack(norm({Tag, Attrs}), S)); +tree([T={pi, _Tag, _Attrs} | Rest], S) -> + tree(Rest, append_stack_child(T, S)); +tree([T={comment, _Comment} | Rest], S) -> + tree(Rest, append_stack_child(T, S)); +tree(L=[{data, _Data, _Whitespace} | _], S) -> + case tree_data(L, true, []) of + {_, true, Rest} -> + tree(Rest, S); + {Data, false, Rest} -> + tree(Rest, append_stack_child(Data, S)) + end. + +norm({Tag, Attrs}) -> + {norm(Tag), [{norm(K), iolist_to_binary(V)} || {K, V} <- Attrs], []}; +norm(Tag) when is_binary(Tag) -> + Tag; +norm(Tag) -> + list_to_binary(string:to_lower(Tag)). + +test_destack() -> + {<<"a">>, [], []} = + destack([{<<"a">>, [], []}]), + {<<"a">>, [], [{<<"b">>, [], []}]} = + destack([{<<"b">>, [], []}, {<<"a">>, [], []}]), + {<<"a">>, [], [{<<"b">>, [], [{<<"c">>, [], []}]}]} = + destack([{<<"c">>, [], []}, {<<"b">>, [], []}, {<<"a">>, [], []}]), + [{<<"a">>, [], [{<<"b">>, [], [{<<"c">>, [], []}]}]}] = + destack(<<"b">>, + [{<<"c">>, [], []}, {<<"b">>, [], []}, {<<"a">>, [], []}]), + [{<<"b">>, [], [{<<"c">>, [], []}]}, {<<"a">>, [], []}] = + destack(<<"c">>, + [{<<"c">>, [], []}, {<<"b">>, [], []},{<<"a">>, [], []}]), + ok. + +stack(T1={TN, _, _}, Stack=[{TN, _, _} | _Rest]) + when TN =:= <<"li">> orelse TN =:= <<"option">> -> + [T1 | destack(TN, Stack)]; +stack(T1={TN0, _, _}, Stack=[{TN1, _, _} | _Rest]) + when (TN0 =:= <<"dd">> orelse TN0 =:= <<"dt">>) andalso + (TN1 =:= <<"dd">> orelse TN1 =:= <<"dt">>) -> + [T1 | destack(TN1, Stack)]; +stack(T1, Stack) -> + [T1 | Stack]. + +append_stack_child(StartTag, [{Name, Attrs, Acc} | Stack]) -> + [{Name, Attrs, [StartTag | Acc]} | Stack]. + +destack(TagName, Stack) when is_list(Stack) -> + F = fun (X) -> + case X of + {TagName, _, _} -> + false; + _ -> + true + end + end, + case lists:splitwith(F, Stack) of + {_, []} -> + %% If we're parsing something like XML we might find + %% a tag that is normally a singleton + %% in HTML but isn't here + case {is_singleton(TagName), Stack} of + {true, [{T0, A0, Acc0} | Post0]} -> + case lists:splitwith(F, Acc0) of + {_, []} -> + %% Actually was a singleton + Stack; + {Pre, [{T1, A1, []} | Post1]} -> + [{T0, A0, [{T1, A1, lists:reverse(Pre)} | Post1]} + | Post0] + end; + _ -> + %% No match, no state change + Stack + end; + {_Pre, [_T]} -> + %% Unfurl the whole stack, we're done + destack(Stack); + {Pre, [T, {T0, A0, Acc0} | Post]} -> + %% Unfurl up to the tag, then accumulate it + [{T0, A0, [destack(Pre ++ [T]) | Acc0]} | Post] + end. + +destack([{Tag, Attrs, Acc}]) -> + {Tag, Attrs, lists:reverse(Acc)}; +destack([{T1, A1, Acc1}, {T0, A0, Acc0} | Rest]) -> + destack([{T0, A0, [{T1, A1, lists:reverse(Acc1)} | Acc0]} | Rest]). + +is_singleton(<<"br">>) -> true; +is_singleton(<<"hr">>) -> true; +is_singleton(<<"img">>) -> true; +is_singleton(<<"input">>) -> true; +is_singleton(<<"base">>) -> true; +is_singleton(<<"meta">>) -> true; +is_singleton(<<"link">>) -> true; +is_singleton(<<"area">>) -> true; +is_singleton(<<"param">>) -> true; +is_singleton(<<"col">>) -> true; +is_singleton(_) -> false. + +tokenize_data(B, S=#decoder{offset=O}) -> + tokenize_data(B, S, O, true). + +tokenize_data(B, S=#decoder{offset=O}, Start, Whitespace) -> + case B of + <<_:O/binary, C, _/binary>> when (C =/= $< andalso C =/= $&) -> + tokenize_data(B, ?INC_CHAR(S, C), Start, + (Whitespace andalso ?IS_WHITESPACE(C))); + _ -> + Len = O - Start, + <<_:Start/binary, Data:Len/binary, _/binary>> = B, + {{data, Data, Whitespace}, S} + end. + +tokenize_attributes(B, S) -> + tokenize_attributes(B, S, []). + +tokenize_attributes(B, S=#decoder{offset=O}, Acc) -> + case B of + <<_:O/binary>> -> + {lists:reverse(Acc), S}; + <<_:O/binary, C, _/binary>> when (C =:= $> orelse C =:= $/) -> + {lists:reverse(Acc), S}; + <<_:O/binary, "?>", _/binary>> -> + {lists:reverse(Acc), S}; + <<_:O/binary, C, _/binary>> when ?IS_WHITESPACE(C) -> + tokenize_attributes(B, ?INC_CHAR(S, C), Acc); + _ -> + {Attr, S1} = tokenize_literal(B, S), + {Value, S2} = tokenize_attr_value(Attr, B, S1), + tokenize_attributes(B, S2, [{Attr, Value} | Acc]) + end. + +tokenize_attr_value(Attr, B, S) -> + S1 = skip_whitespace(B, S), + O = S1#decoder.offset, + case B of + <<_:O/binary, "=", _/binary>> -> + S2 = skip_whitespace(B, ?INC_COL(S1)), + tokenize_word_or_literal(B, S2); + _ -> + {Attr, S1} + end. + +skip_whitespace(B, S=#decoder{offset=O}) -> + case B of + <<_:O/binary, C, _/binary>> when ?IS_WHITESPACE(C) -> + skip_whitespace(B, ?INC_CHAR(S, C)); + _ -> + S + end. + +tokenize_literal(Bin, S) -> + tokenize_literal(Bin, S, []). + +tokenize_literal(Bin, S=#decoder{offset=O}, Acc) -> + case Bin of + <<_:O/binary, $&, _/binary>> -> + {{data, Data, false}, S1} = tokenize_charref(Bin, ?INC_COL(S)), + tokenize_literal(Bin, S1, [Data | Acc]); + <<_:O/binary, C, _/binary>> when not (?IS_WHITESPACE(C) + orelse C =:= $> + orelse C =:= $/ + orelse C =:= $=) -> + tokenize_literal(Bin, ?INC_COL(S), [C | Acc]); + _ -> + {iolist_to_binary(lists:reverse(Acc)), S} + end. + +find_qgt(Bin, S=#decoder{offset=O}) -> + case Bin of + <<_:O/binary, "?>", _/binary>> -> + ?ADV_COL(S, 2); + <<_:O/binary, C, _/binary>> -> + find_qgt(Bin, ?INC_CHAR(S, C)); + _ -> + S + end. + +find_gt(Bin, S) -> + find_gt(Bin, S, false). + +find_gt(Bin, S=#decoder{offset=O}, HasSlash) -> + case Bin of + <<_:O/binary, $/, _/binary>> -> + find_gt(Bin, ?INC_COL(S), true); + <<_:O/binary, $>, _/binary>> -> + {?INC_COL(S), HasSlash}; + <<_:O/binary, C, _/binary>> -> + find_gt(Bin, ?INC_CHAR(S, C), HasSlash); + _ -> + {S, HasSlash} + end. + +tokenize_charref(Bin, S=#decoder{offset=O}) -> + tokenize_charref(Bin, S, O). + +tokenize_charref(Bin, S=#decoder{offset=O}, Start) -> + case Bin of + <<_:O/binary>> -> + <<_:Start/binary, Raw/binary>> = Bin, + {{data, Raw, false}, S}; + <<_:O/binary, C, _/binary>> when ?IS_WHITESPACE(C) + orelse C =:= ?SQUOTE + orelse C =:= ?QUOTE + orelse C =:= $/ + orelse C =:= $> -> + Len = O - Start, + <<_:Start/binary, Raw:Len/binary, _/binary>> = Bin, + {{data, Raw, false}, S}; + <<_:O/binary, $;, _/binary>> -> + Len = O - Start, + <<_:Start/binary, Raw:Len/binary, _/binary>> = Bin, + Data = case mochiweb_charref:charref(Raw) of + undefined -> + Start1 = Start - 1, + Len1 = Len + 2, + <<_:Start1/binary, R:Len1/binary, _/binary>> = Bin, + R; + Unichar -> + list_to_binary(xmerl_ucs:to_utf8(Unichar)) + end, + {{data, Data, false}, ?INC_COL(S)}; + _ -> + tokenize_charref(Bin, ?INC_COL(S), Start) + end. + +tokenize_doctype(Bin, S) -> + tokenize_doctype(Bin, S, []). + +tokenize_doctype(Bin, S=#decoder{offset=O}, Acc) -> + case Bin of + <<_:O/binary>> -> + {{doctype, lists:reverse(Acc)}, S}; + <<_:O/binary, $>, _/binary>> -> + {{doctype, lists:reverse(Acc)}, ?INC_COL(S)}; + <<_:O/binary, C, _/binary>> when ?IS_WHITESPACE(C) -> + tokenize_doctype(Bin, ?INC_CHAR(S, C), Acc); + _ -> + {Word, S1} = tokenize_word_or_literal(Bin, S), + tokenize_doctype(Bin, S1, [Word | Acc]) + end. + +tokenize_word_or_literal(Bin, S=#decoder{offset=O}) -> + case Bin of + <<_:O/binary, C, _/binary>> when ?IS_WHITESPACE(C) -> + {error, {whitespace, [C], S}}; + <<_:O/binary, C, _/binary>> when C =:= ?QUOTE orelse C =:= ?SQUOTE -> + tokenize_word(Bin, ?INC_COL(S), C); + _ -> + tokenize_literal(Bin, S, []) + end. + +tokenize_word(Bin, S, Quote) -> + tokenize_word(Bin, S, Quote, []). + +tokenize_word(Bin, S=#decoder{offset=O}, Quote, Acc) -> + case Bin of + <<_:O/binary>> -> + {iolist_to_binary(lists:reverse(Acc)), S}; + <<_:O/binary, Quote, _/binary>> -> + {iolist_to_binary(lists:reverse(Acc)), ?INC_COL(S)}; + <<_:O/binary, $&, _/binary>> -> + {{data, Data, false}, S1} = tokenize_charref(Bin, ?INC_COL(S)), + tokenize_word(Bin, S1, Quote, [Data | Acc]); + <<_:O/binary, C, _/binary>> -> + tokenize_word(Bin, ?INC_CHAR(S, C), Quote, [C | Acc]) + end. + +tokenize_cdata(Bin, S=#decoder{offset=O}) -> + tokenize_cdata(Bin, S, O). + +tokenize_cdata(Bin, S=#decoder{offset=O}, Start) -> + case Bin of + <<_:O/binary, "]]>", _/binary>> -> + Len = O - Start, + <<_:Start/binary, Raw:Len/binary, _/binary>> = Bin, + {{data, Raw, false}, ?ADV_COL(S, 3)}; + <<_:O/binary, C, _/binary>> -> + tokenize_cdata(Bin, ?INC_CHAR(S, C), Start); + _ -> + <<_:O/binary, Raw/binary>> = Bin, + {{data, Raw, false}, S} + end. + +tokenize_comment(Bin, S=#decoder{offset=O}) -> + tokenize_comment(Bin, S, O). + +tokenize_comment(Bin, S=#decoder{offset=O}, Start) -> + case Bin of + <<_:O/binary, "-->", _/binary>> -> + Len = O - Start, + <<_:Start/binary, Raw:Len/binary, _/binary>> = Bin, + {{comment, Raw}, ?ADV_COL(S, 3)}; + <<_:O/binary, C, _/binary>> -> + tokenize_comment(Bin, ?INC_CHAR(S, C), Start); + <<_:Start/binary, Raw/binary>> -> + {{comment, Raw}, S} + end. + +tokenize_script(Bin, S=#decoder{offset=O}) -> + tokenize_script(Bin, S, O). + +tokenize_script(Bin, S=#decoder{offset=O}, Start) -> + case Bin of + %% Just a look-ahead, we want the end_tag separately + <<_:O/binary, $<, $/, SS, CC, RR, II, PP, TT, _/binary>> + when (SS =:= $s orelse SS =:= $S) andalso + (CC =:= $c orelse CC =:= $C) andalso + (RR =:= $r orelse RR =:= $R) andalso + (II =:= $i orelse II =:= $I) andalso + (PP =:= $p orelse PP =:= $P) andalso + (TT=:= $t orelse TT =:= $T) -> + Len = O - Start, + <<_:Start/binary, Raw:Len/binary, _/binary>> = Bin, + {{data, Raw, false}, S}; + <<_:O/binary, C, _/binary>> -> + tokenize_script(Bin, ?INC_CHAR(S, C), Start); + <<_:Start/binary, Raw/binary>> -> + {{data, Raw, false}, S} + end. + +tokenize_textarea(Bin, S=#decoder{offset=O}) -> + tokenize_textarea(Bin, S, O). + +tokenize_textarea(Bin, S=#decoder{offset=O}, Start) -> + case Bin of + %% Just a look-ahead, we want the end_tag separately + <<_:O/binary, $<, $/, TT, EE, XX, TT2, AA, RR, EE2, AA2, _/binary>> + when (TT =:= $t orelse TT =:= $T) andalso + (EE =:= $e orelse EE =:= $E) andalso + (XX =:= $x orelse XX =:= $X) andalso + (TT2 =:= $t orelse TT2 =:= $T) andalso + (AA =:= $a orelse AA =:= $A) andalso + (RR =:= $r orelse RR =:= $R) andalso + (EE2 =:= $e orelse EE2 =:= $E) andalso + (AA2 =:= $a orelse AA2 =:= $A) -> + Len = O - Start, + <<_:Start/binary, Raw:Len/binary, _/binary>> = Bin, + {{data, Raw, false}, S}; + <<_:O/binary, C, _/binary>> -> + tokenize_textarea(Bin, ?INC_CHAR(S, C), Start); + <<_:Start/binary, Raw/binary>> -> + {{data, Raw, false}, S} + end. diff --git a/web/api/webmachine/deps/mochiweb/src/mochiweb_http.erl b/web/api/webmachine/deps/mochiweb/src/mochiweb_http.erl new file mode 100644 index 0000000..14a3657 --- /dev/null +++ b/web/api/webmachine/deps/mochiweb/src/mochiweb_http.erl @@ -0,0 +1,142 @@ +%% @author Bob Ippolito +%% @copyright 2007 Mochi Media, Inc. + +%% @doc HTTP server. + +-module(mochiweb_http). +-author('bob@mochimedia.com'). +-export([start/0, start/1, stop/0, stop/1]). +-export([loop/2, default_body/1]). + +-define(IDLE_TIMEOUT, 30000). + +-define(MAX_HEADERS, 1000). +-define(DEFAULTS, [{name, ?MODULE}, + {port, 8888}]). + +set_default({Prop, Value}, PropList) -> + case proplists:is_defined(Prop, PropList) of + true -> + PropList; + false -> + [{Prop, Value} | PropList] + end. + +set_defaults(Defaults, PropList) -> + lists:foldl(fun set_default/2, PropList, Defaults). + +parse_options(Options) -> + {loop, HttpLoop} = proplists:lookup(loop, Options), + Loop = fun (S) -> + ?MODULE:loop(S, HttpLoop) + end, + Options1 = [{loop, Loop} | proplists:delete(loop, Options)], + set_defaults(?DEFAULTS, Options1). + +stop() -> + mochiweb_socket_server:stop(?MODULE). + +stop(Name) -> + mochiweb_socket_server:stop(Name). + +start() -> + start([{ip, "127.0.0.1"}, + {loop, {?MODULE, default_body}}]). + +start(Options) -> + mochiweb_socket_server:start(parse_options(Options)). + +frm(Body) -> + ["" + "
" + "" + "" + "
" + "
" + "
" + "" + "" + "" + "
" + "
", Body, "
" + ""]. + +default_body(Req, M, "/chunked") when M =:= 'GET'; M =:= 'HEAD' -> + Res = Req:ok({"text/plain", [], chunked}), + Res:write_chunk("First chunk\r\n"), + timer:sleep(5000), + Res:write_chunk("Last chunk\r\n"), + Res:write_chunk(""); +default_body(Req, M, _Path) when M =:= 'GET'; M =:= 'HEAD' -> + Body = io_lib:format("~p~n", [[{parse_qs, Req:parse_qs()}, + {parse_cookie, Req:parse_cookie()}, + Req:dump()]]), + Req:ok({"text/html", + [mochiweb_cookies:cookie("mochiweb_http", "test_cookie")], + frm(Body)}); +default_body(Req, 'POST', "/multipart") -> + Body = io_lib:format("~p~n", [[{parse_qs, Req:parse_qs()}, + {parse_cookie, Req:parse_cookie()}, + {body, Req:recv_body()}, + Req:dump()]]), + Req:ok({"text/html", [], frm(Body)}); +default_body(Req, 'POST', _Path) -> + Body = io_lib:format("~p~n", [[{parse_qs, Req:parse_qs()}, + {parse_cookie, Req:parse_cookie()}, + {parse_post, Req:parse_post()}, + Req:dump()]]), + Req:ok({"text/html", [], frm(Body)}); +default_body(Req, _Method, _Path) -> + Req:respond({501, [], []}). + +default_body(Req) -> + default_body(Req, Req:get(method), Req:get(path)). + +loop(Socket, Body) -> + inet:setopts(Socket, [{packet, http}]), + request(Socket, Body). + +request(Socket, Body) -> + case gen_tcp:recv(Socket, 0, ?IDLE_TIMEOUT) of + {ok, {http_request, Method, Path, Version}} -> + headers(Socket, {Method, Path, Version}, [], Body, 0); + {error, {http_error, "\r\n"}} -> + request(Socket, Body); + {error, {http_error, "\n"}} -> + request(Socket, Body); + _Other -> + gen_tcp:close(Socket), + exit(normal) + end. + +headers(Socket, Request, Headers, _Body, ?MAX_HEADERS) -> + %% Too many headers sent, bad request. + inet:setopts(Socket, [{packet, raw}]), + Req = mochiweb:new_request({Socket, Request, + lists:reverse(Headers)}), + Req:respond({400, [], []}), + gen_tcp:close(Socket), + exit(normal); +headers(Socket, Request, Headers, Body, HeaderCount) -> + case gen_tcp:recv(Socket, 0, ?IDLE_TIMEOUT) of + {ok, http_eoh} -> + inet:setopts(Socket, [{packet, raw}]), + Req = mochiweb:new_request({Socket, Request, + lists:reverse(Headers)}), + Body(Req), + case Req:should_close() of + true -> + gen_tcp:close(Socket), + exit(normal); + false -> + Req:cleanup(), + ?MODULE:loop(Socket, Body) + end; + {ok, {http_header, _, Name, _, Value}} -> + headers(Socket, Request, [{Name, Value} | Headers], Body, + 1 + HeaderCount); + _Other -> + gen_tcp:close(Socket), + exit(normal) + end. diff --git a/web/api/webmachine/deps/mochiweb/src/mochiweb_multipart.erl b/web/api/webmachine/deps/mochiweb/src/mochiweb_multipart.erl new file mode 100644 index 0000000..9eb4bad --- /dev/null +++ b/web/api/webmachine/deps/mochiweb/src/mochiweb_multipart.erl @@ -0,0 +1,429 @@ +%% @author Bob Ippolito +%% @copyright 2007 Mochi Media, Inc. + +%% @doc Utilities for parsing multipart/form-data. + +-module(mochiweb_multipart). +-author('bob@mochimedia.com'). + +-export([parse_form/1, parse_form/2]). +-export([parse_multipart_request/2]). +-export([test/0]). + +-define(CHUNKSIZE, 4096). + +-record(mp, {state, boundary, length, buffer, callback, req}). + +%% TODO: DOCUMENT THIS MODULE. + +parse_form(Req) -> + parse_form(Req, fun default_file_handler/2). + +parse_form(Req, FileHandler) -> + Callback = fun (Next) -> parse_form_outer(Next, FileHandler, []) end, + {_, _, Res} = parse_multipart_request(Req, Callback), + Res. + +parse_form_outer(eof, _, Acc) -> + lists:reverse(Acc); +parse_form_outer({headers, H}, FileHandler, State) -> + {"form-data", H1} = proplists:get_value("content-disposition", H), + Name = proplists:get_value("name", H1), + Filename = proplists:get_value("filename", H1), + case Filename of + undefined -> + fun (Next) -> + parse_form_value(Next, {Name, []}, FileHandler, State) + end; + _ -> + ContentType = proplists:get_value("content-type", H), + Handler = FileHandler(Filename, ContentType), + fun (Next) -> + parse_form_file(Next, {Name, Handler}, FileHandler, State) + end + end. + +parse_form_value(body_end, {Name, Acc}, FileHandler, State) -> + Value = binary_to_list(iolist_to_binary(lists:reverse(Acc))), + State1 = [{Name, Value} | State], + fun (Next) -> parse_form_outer(Next, FileHandler, State1) end; +parse_form_value({body, Data}, {Name, Acc}, FileHandler, State) -> + Acc1 = [Data | Acc], + fun (Next) -> parse_form_value(Next, {Name, Acc1}, FileHandler, State) end. + +parse_form_file(body_end, {Name, Handler}, FileHandler, State) -> + Value = Handler(eof), + State1 = [{Name, Value} | State], + fun (Next) -> parse_form_outer(Next, FileHandler, State1) end; +parse_form_file({body, Data}, {Name, Handler}, FileHandler, State) -> + H1 = Handler(Data), + fun (Next) -> parse_form_file(Next, {Name, H1}, FileHandler, State) end. + +default_file_handler(Filename, ContentType) -> + default_file_handler_1(Filename, ContentType, []). + +default_file_handler_1(Filename, ContentType, Acc) -> + fun(eof) -> + Value = iolist_to_binary(lists:reverse(Acc)), + {Filename, ContentType, Value}; + (Next) -> + default_file_handler_1(Filename, ContentType, [Next | Acc]) + end. + +parse_multipart_request(Req, Callback) -> + %% TODO: Support chunked? + Length = list_to_integer(Req:get_header_value("content-length")), + Boundary = iolist_to_binary( + get_boundary(Req:get_header_value("content-type"))), + Prefix = <<"\r\n--", Boundary/binary>>, + BS = size(Boundary), + Chunk = read_chunk(Req, Length), + Length1 = Length - size(Chunk), + <<"--", Boundary:BS/binary, "\r\n", Rest/binary>> = Chunk, + feed_mp(headers, #mp{boundary=Prefix, + length=Length1, + buffer=Rest, + callback=Callback, + req=Req}). + +parse_headers(<<>>) -> + []; +parse_headers(Binary) -> + parse_headers(Binary, []). + +parse_headers(Binary, Acc) -> + case find_in_binary(<<"\r\n">>, Binary) of + {exact, N} -> + <> = Binary, + parse_headers(Rest, [split_header(Line) | Acc]); + not_found -> + lists:reverse([split_header(Binary) | Acc]) + end. + +split_header(Line) -> + {Name, [$: | Value]} = lists:splitwith(fun (C) -> C =/= $: end, + binary_to_list(Line)), + {string:to_lower(string:strip(Name)), + mochiweb_util:parse_header(Value)}. + +read_chunk(Req, Length) when Length > 0 -> + case Length of + Length when Length < ?CHUNKSIZE -> + Req:recv(Length); + _ -> + Req:recv(?CHUNKSIZE) + end. + +read_more(State=#mp{length=Length, buffer=Buffer, req=Req}) -> + Data = read_chunk(Req, Length), + Buffer1 = <>, + State#mp{length=Length - size(Data), + buffer=Buffer1}. + +feed_mp(headers, State=#mp{buffer=Buffer, callback=Callback}) -> + {State1, P} = case find_in_binary(<<"\r\n\r\n">>, Buffer) of + {exact, N} -> + {State, N}; + _ -> + S1 = read_more(State), + %% Assume headers must be less than ?CHUNKSIZE + {exact, N} = find_in_binary(<<"\r\n\r\n">>, + S1#mp.buffer), + {S1, N} + end, + <> = State1#mp.buffer, + NextCallback = Callback({headers, parse_headers(Headers)}), + feed_mp(body, State1#mp{buffer=Rest, + callback=NextCallback}); +feed_mp(body, State=#mp{boundary=Prefix, buffer=Buffer, callback=Callback}) -> + case find_boundary(Prefix, Buffer) of + {end_boundary, Start, Skip} -> + <> = Buffer, + C1 = Callback({body, Data}), + C2 = C1(body_end), + {State#mp.length, Rest, C2(eof)}; + {next_boundary, Start, Skip} -> + <> = Buffer, + C1 = Callback({body, Data}), + feed_mp(headers, State#mp{callback=C1(body_end), + buffer=Rest}); + {maybe, Start} -> + <> = Buffer, + feed_mp(body, read_more(State#mp{callback=Callback({body, Data}), + buffer=Rest})); + not_found -> + {Data, Rest} = {Buffer, <<>>}, + feed_mp(body, read_more(State#mp{callback=Callback({body, Data}), + buffer=Rest})) + end. + +get_boundary(ContentType) -> + {"multipart/form-data", Opts} = mochiweb_util:parse_header(ContentType), + case proplists:get_value("boundary", Opts) of + S when is_list(S) -> + S + end. + +find_in_binary(B, Data) when size(B) > 0 -> + case size(Data) - size(B) of + Last when Last < 0 -> + partial_find(B, Data, 0, size(Data)); + Last -> + find_in_binary(B, size(B), Data, 0, Last) + end. + +find_in_binary(B, BS, D, N, Last) when N =< Last-> + case D of + <<_:N/binary, B:BS/binary, _/binary>> -> + {exact, N}; + _ -> + find_in_binary(B, BS, D, 1 + N, Last) + end; +find_in_binary(B, BS, D, N, Last) when N =:= 1 + Last -> + partial_find(B, D, N, BS - 1). + +partial_find(_B, _D, _N, 0) -> + not_found; +partial_find(B, D, N, K) -> + <> = B, + case D of + <<_Skip:N/binary, B1:K/binary>> -> + {partial, N, K}; + _ -> + partial_find(B, D, 1 + N, K - 1) + end. + +find_boundary(Prefix, Data) -> + case find_in_binary(Prefix, Data) of + {exact, Skip} -> + PrefixSkip = Skip + size(Prefix), + case Data of + <<_:PrefixSkip/binary, "\r\n", _/binary>> -> + {next_boundary, Skip, size(Prefix) + 2}; + <<_:PrefixSkip/binary, "--\r\n", _/binary>> -> + {end_boundary, Skip, size(Prefix) + 4}; + _ when size(Data) < PrefixSkip + 4 -> + %% Underflow + {maybe, Skip}; + _ -> + %% False positive + not_found + end; + {partial, Skip, Length} when (Skip + Length) =:= size(Data) -> + %% Underflow + {maybe, Skip}; + _ -> + not_found + end. + +with_socket_server(ServerFun, ClientFun) -> + {ok, Server} = mochiweb_socket_server:start([{ip, "127.0.0.1"}, + {port, 0}, + {loop, ServerFun}]), + Port = mochiweb_socket_server:get(Server, port), + {ok, Client} = gen_tcp:connect("127.0.0.1", Port, + [binary, {active, false}]), + Res = (catch ClientFun(Client)), + mochiweb_socket_server:stop(Server), + Res. + +fake_request(Socket, ContentType, Length) -> + mochiweb_request:new(Socket, + 'POST', + "/multipart", + {1,1}, + mochiweb_headers:make( + [{"content-type", ContentType}, + {"content-length", Length}])). + +test_callback(Expect, [Expect | Rest]) -> + case Rest of + [] -> + ok; + _ -> + fun (Next) -> test_callback(Next, Rest) end + end. + +test_parse3() -> + ContentType = "multipart/form-data; boundary=---------------------------7386909285754635891697677882", + BinContent = <<"-----------------------------7386909285754635891697677882\r\nContent-Disposition: form-data; name=\"hidden\"\r\n\r\nmultipart message\r\n-----------------------------7386909285754635891697677882\r\nContent-Disposition: form-data; name=\"file\"; filename=\"test_file.txt\"\r\nContent-Type: text/plain\r\n\r\nWoo multiline text file\n\nLa la la\r\n-----------------------------7386909285754635891697677882--\r\n">>, + Expect = [{headers, + [{"content-disposition", + {"form-data", [{"name", "hidden"}]}}]}, + {body, <<"multipart message">>}, + body_end, + {headers, + [{"content-disposition", + {"form-data", [{"name", "file"}, {"filename", "test_file.txt"}]}}, + {"content-type", {"text/plain", []}}]}, + {body, <<"Woo multiline text file\n\nLa la la">>}, + body_end, + eof], + TestCallback = fun (Next) -> test_callback(Next, Expect) end, + ServerFun = fun (Socket) -> + case gen_tcp:send(Socket, BinContent) of + ok -> + exit(normal) + end + end, + ClientFun = fun (Socket) -> + Req = fake_request(Socket, ContentType, + size(BinContent)), + Res = parse_multipart_request(Req, TestCallback), + {0, <<>>, ok} = Res, + ok + end, + ok = with_socket_server(ServerFun, ClientFun), + ok. + + +test_parse2() -> + ContentType = "multipart/form-data; boundary=---------------------------6072231407570234361599764024", + BinContent = <<"-----------------------------6072231407570234361599764024\r\nContent-Disposition: form-data; name=\"hidden\"\r\n\r\nmultipart message\r\n-----------------------------6072231407570234361599764024\r\nContent-Disposition: form-data; name=\"file\"; filename=\"\"\r\nContent-Type: application/octet-stream\r\n\r\n\r\n-----------------------------6072231407570234361599764024--\r\n">>, + Expect = [{headers, + [{"content-disposition", + {"form-data", [{"name", "hidden"}]}}]}, + {body, <<"multipart message">>}, + body_end, + {headers, + [{"content-disposition", + {"form-data", [{"name", "file"}, {"filename", ""}]}}, + {"content-type", {"application/octet-stream", []}}]}, + {body, <<>>}, + body_end, + eof], + TestCallback = fun (Next) -> test_callback(Next, Expect) end, + ServerFun = fun (Socket) -> + case gen_tcp:send(Socket, BinContent) of + ok -> + exit(normal) + end + end, + ClientFun = fun (Socket) -> + Req = fake_request(Socket, ContentType, + size(BinContent)), + Res = parse_multipart_request(Req, TestCallback), + {0, <<>>, ok} = Res, + ok + end, + ok = with_socket_server(ServerFun, ClientFun), + ok. + +test_parse_form() -> + ContentType = "multipart/form-data; boundary=AaB03x", + "AaB03x" = get_boundary(ContentType), + Content = mochiweb_util:join( + ["--AaB03x", + "Content-Disposition: form-data; name=\"submit-name\"", + "", + "Larry", + "--AaB03x", + "Content-Disposition: form-data; name=\"files\";" + ++ "filename=\"file1.txt\"", + "Content-Type: text/plain", + "", + "... contents of file1.txt ...", + "--AaB03x--", + ""], "\r\n"), + BinContent = iolist_to_binary(Content), + ServerFun = fun (Socket) -> + case gen_tcp:send(Socket, BinContent) of + ok -> + exit(normal) + end + end, + ClientFun = fun (Socket) -> + Req = fake_request(Socket, ContentType, + size(BinContent)), + Res = parse_form(Req), + [{"submit-name", "Larry"}, + {"files", {"file1.txt", {"text/plain",[]}, + <<"... contents of file1.txt ...">>} + }] = Res, + ok + end, + ok = with_socket_server(ServerFun, ClientFun), + ok. + +test_parse() -> + ContentType = "multipart/form-data; boundary=AaB03x", + "AaB03x" = get_boundary(ContentType), + Content = mochiweb_util:join( + ["--AaB03x", + "Content-Disposition: form-data; name=\"submit-name\"", + "", + "Larry", + "--AaB03x", + "Content-Disposition: form-data; name=\"files\";" + ++ "filename=\"file1.txt\"", + "Content-Type: text/plain", + "", + "... contents of file1.txt ...", + "--AaB03x--", + ""], "\r\n"), + BinContent = iolist_to_binary(Content), + Expect = [{headers, + [{"content-disposition", + {"form-data", [{"name", "submit-name"}]}}]}, + {body, <<"Larry">>}, + body_end, + {headers, + [{"content-disposition", + {"form-data", [{"name", "files"}, {"filename", "file1.txt"}]}}, + {"content-type", {"text/plain", []}}]}, + {body, <<"... contents of file1.txt ...">>}, + body_end, + eof], + TestCallback = fun (Next) -> test_callback(Next, Expect) end, + ServerFun = fun (Socket) -> + case gen_tcp:send(Socket, BinContent) of + ok -> + exit(normal) + end + end, + ClientFun = fun (Socket) -> + Req = fake_request(Socket, ContentType, + size(BinContent)), + Res = parse_multipart_request(Req, TestCallback), + {0, <<>>, ok} = Res, + ok + end, + ok = with_socket_server(ServerFun, ClientFun), + ok. + +test_find_boundary() -> + B = <<"\r\n--X">>, + {next_boundary, 0, 7} = find_boundary(B, <<"\r\n--X\r\nRest">>), + {next_boundary, 1, 7} = find_boundary(B, <<"!\r\n--X\r\nRest">>), + {end_boundary, 0, 9} = find_boundary(B, <<"\r\n--X--\r\nRest">>), + {end_boundary, 1, 9} = find_boundary(B, <<"!\r\n--X--\r\nRest">>), + not_found = find_boundary(B, <<"--X\r\nRest">>), + {maybe, 0} = find_boundary(B, <<"\r\n--X\r">>), + {maybe, 1} = find_boundary(B, <<"!\r\n--X\r">>), + P = <<"\r\n-----------------------------16037454351082272548568224146">>, + B0 = <<55,212,131,77,206,23,216,198,35,87,252,118,252,8,25,211,132,229, + 182,42,29,188,62,175,247,243,4,4,0,59, 13,10,45,45,45,45,45,45,45, + 45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45,45, + 49,54,48,51,55,52,53,52,51,53,49>>, + {maybe, 30} = find_boundary(P, B0), + ok. + +test_find_in_binary() -> + {exact, 0} = find_in_binary(<<"foo">>, <<"foobarbaz">>), + {exact, 1} = find_in_binary(<<"oo">>, <<"foobarbaz">>), + {exact, 8} = find_in_binary(<<"z">>, <<"foobarbaz">>), + not_found = find_in_binary(<<"q">>, <<"foobarbaz">>), + {partial, 7, 2} = find_in_binary(<<"azul">>, <<"foobarbaz">>), + {exact, 0} = find_in_binary(<<"foobarbaz">>, <<"foobarbaz">>), + {partial, 0, 3} = find_in_binary(<<"foobar">>, <<"foo">>), + {partial, 1, 3} = find_in_binary(<<"foobar">>, <<"afoo">>), + ok. + +test() -> + test_find_in_binary(), + test_find_boundary(), + test_parse(), + test_parse2(), + test_parse3(), + test_parse_form(), + ok. diff --git a/web/api/webmachine/deps/mochiweb/src/mochiweb_request.erl b/web/api/webmachine/deps/mochiweb/src/mochiweb_request.erl new file mode 100644 index 0000000..7773235 --- /dev/null +++ b/web/api/webmachine/deps/mochiweb/src/mochiweb_request.erl @@ -0,0 +1,795 @@ +%% @author Bob Ippolito +%% @copyright 2007 Mochi Media, Inc. + +%% @doc MochiWeb HTTP Request abstraction. + +-module(mochiweb_request, [Socket, Method, RawPath, Version, Headers]). +-author('bob@mochimedia.com'). + +-include_lib("kernel/include/file.hrl"). + +-define(QUIP, "Any of you quaids got a smint?"). +-define(READ_SIZE, 8192). + +-export([get_header_value/1, get_primary_header_value/1, get/1, dump/0]). +-export([send/1, recv/1, recv/2, recv_body/0, recv_body/1, stream_body/3]). +-export([start_response/1, start_response_length/1, start_raw_response/1]). +-export([respond/1, ok/1]). +-export([not_found/0, not_found/1]). +-export([parse_post/0, parse_qs/0]). +-export([should_close/0, cleanup/0]). +-export([parse_cookie/0, get_cookie_value/1]). +-export([serve_file/2, serve_file/3]). +-export([test/0]). + +-define(SAVE_QS, mochiweb_request_qs). +-define(SAVE_PATH, mochiweb_request_path). +-define(SAVE_RECV, mochiweb_request_recv). +-define(SAVE_BODY, mochiweb_request_body). +-define(SAVE_BODY_LENGTH, mochiweb_request_body_length). +-define(SAVE_POST, mochiweb_request_post). +-define(SAVE_COOKIE, mochiweb_request_cookie). +-define(SAVE_FORCE_CLOSE, mochiweb_request_force_close). + +%% @type iolist() = [iolist() | binary() | char()]. +%% @type iodata() = binary() | iolist(). +%% @type key() = atom() | string() | binary() +%% @type value() = atom() | string() | binary() | integer() +%% @type headers(). A mochiweb_headers structure. +%% @type response(). A mochiweb_response parameterized module instance. +%% @type ioheaders() = headers() | [{key(), value()}]. + +% 10 second default idle timeout +-define(IDLE_TIMEOUT, 10000). + +% Maximum recv_body() length of 1MB +-define(MAX_RECV_BODY, (1024*1024)). + +%% @spec get_header_value(K) -> undefined | Value +%% @doc Get the value of a given request header. +get_header_value(K) -> + mochiweb_headers:get_value(K, Headers). + +get_primary_header_value(K) -> + mochiweb_headers:get_primary_value(K, Headers). + +%% @type field() = socket | method | raw_path | version | headers | peer | path | body_length | range + +%% @spec get(field()) -> term() +%% @doc Return the internal representation of the given field. +get(socket) -> + Socket; +get(method) -> + Method; +get(raw_path) -> + RawPath; +get(version) -> + Version; +get(headers) -> + Headers; +get(peer) -> + case inet:peername(Socket) of + {ok, {Addr={10, _, _, _}, _Port}} -> + case get_header_value("x-forwarded-for") of + undefined -> + inet_parse:ntoa(Addr); + Hosts -> + string:strip(lists:last(string:tokens(Hosts, ","))) + end; + {ok, {{127, 0, 0, 1}, _Port}} -> + case get_header_value("x-forwarded-for") of + undefined -> + "127.0.0.1"; + Hosts -> + string:strip(lists:last(string:tokens(Hosts, ","))) + end; + {ok, {Addr, _Port}} -> + inet_parse:ntoa(Addr) + end; +get(path) -> + case erlang:get(?SAVE_PATH) of + undefined -> + {Path0, _, _} = mochiweb_util:urlsplit_path(RawPath), + Path = mochiweb_util:unquote(Path0), + put(?SAVE_PATH, Path), + Path; + Cached -> + Cached + end; +get(body_length) -> + erlang:get(?SAVE_BODY_LENGTH); +get(range) -> + case get_header_value(range) of + undefined -> + undefined; + RawRange -> + parse_range_request(RawRange) + end. + +%% @spec dump() -> {mochiweb_request, [{atom(), term()}]} +%% @doc Dump the internal representation to a "human readable" set of terms +%% for debugging/inspection purposes. +dump() -> + {?MODULE, [{method, Method}, + {version, Version}, + {raw_path, RawPath}, + {headers, mochiweb_headers:to_list(Headers)}]}. + +%% @spec send(iodata()) -> ok +%% @doc Send data over the socket. +send(Data) -> + case gen_tcp:send(Socket, Data) of + ok -> + ok; + _ -> + exit(normal) + end. + +%% @spec recv(integer()) -> binary() +%% @doc Receive Length bytes from the client as a binary, with the default +%% idle timeout. +recv(Length) -> + recv(Length, ?IDLE_TIMEOUT). + +%% @spec recv(integer(), integer()) -> binary() +%% @doc Receive Length bytes from the client as a binary, with the given +%% Timeout in msec. +recv(Length, Timeout) -> + case gen_tcp:recv(Socket, Length, Timeout) of + {ok, Data} -> + put(?SAVE_RECV, true), + Data; + _ -> + exit(normal) + end. + +%% @spec body_length() -> undefined | chunked | unknown_transfer_encoding | integer() +%% @doc Infer body length from transfer-encoding and content-length headers. +body_length() -> + case get_header_value("transfer-encoding") of + undefined -> + case get_header_value("content-length") of + undefined -> + undefined; + Length -> + list_to_integer(Length) + end; + "chunked" -> + chunked; + Unknown -> + {unknown_transfer_encoding, Unknown} + end. + + +%% @spec recv_body() -> binary() +%% @doc Receive the body of the HTTP request (defined by Content-Length). +%% Will only receive up to the default max-body length of 1MB. +recv_body() -> + recv_body(?MAX_RECV_BODY). + +%% @spec recv_body(integer()) -> binary() +%% @doc Receive the body of the HTTP request (defined by Content-Length). +%% Will receive up to MaxBody bytes. +recv_body(MaxBody) -> + % we could use a sane constant for max chunk size + Body = stream_body(?MAX_RECV_BODY, fun + ({0, _ChunkedFooter}, {_LengthAcc, BinAcc}) -> + iolist_to_binary(lists:reverse(BinAcc)); + ({Length, Bin}, {LengthAcc, BinAcc}) -> + NewLength = Length + LengthAcc, + if NewLength > MaxBody -> + exit({body_too_large, chunked}); + true -> + {NewLength, [Bin | BinAcc]} + end + end, {0, []}, MaxBody), + put(?SAVE_BODY, Body), + Body. + +stream_body(MaxChunkSize, ChunkFun, FunState) -> + stream_body(MaxChunkSize, ChunkFun, FunState, undefined). + +stream_body(MaxChunkSize, ChunkFun, FunState, MaxBodyLength) -> + Expect = case get_header_value("expect") of + undefined -> + undefined; + Value when is_list(Value) -> + string:to_lower(Value) + end, + case Expect of + "100-continue" -> + start_raw_response({100, gb_trees:empty()}); + _Else -> + ok + end, + case body_length() of + undefined -> + undefined; + {unknown_transfer_encoding, Unknown} -> + exit({unknown_transfer_encoding, Unknown}); + chunked -> + % In this case the MaxBody is actually used to + % determine the maximum allowed size of a single + % chunk. + stream_chunked_body(MaxChunkSize, ChunkFun, FunState); + 0 -> + <<>>; + Length when is_integer(Length) -> + case MaxBodyLength of + MaxBodyLength when is_integer(MaxBodyLength), MaxBodyLength < Length -> + exit({body_too_large, content_length}); + _ -> + stream_unchunked_body(Length, MaxChunkSize, ChunkFun, FunState) + end; + Length -> + exit({length_not_integer, Length}) + end. + + +%% @spec start_response({integer(), ioheaders()}) -> response() +%% @doc Start the HTTP response by sending the Code HTTP response and +%% ResponseHeaders. The server will set header defaults such as Server +%% and Date if not present in ResponseHeaders. +start_response({Code, ResponseHeaders}) -> + HResponse = mochiweb_headers:make(ResponseHeaders), + HResponse1 = mochiweb_headers:default_from_list(server_headers(), + HResponse), + start_raw_response({Code, HResponse1}). + +%% @spec start_raw_response({integer(), headers()}) -> response() +%% @doc Start the HTTP response by sending the Code HTTP response and +%% ResponseHeaders. +start_raw_response({Code, ResponseHeaders}) -> + F = fun ({K, V}, Acc) -> + [make_io(K), <<": ">>, V, <<"\r\n">> | Acc] + end, + End = lists:foldl(F, [<<"\r\n">>], + mochiweb_headers:to_list(ResponseHeaders)), + send([make_version(Version), make_code(Code), <<"\r\n">> | End]), + mochiweb:new_response({THIS, Code, ResponseHeaders}). + + +%% @spec start_response_length({integer(), ioheaders(), integer()}) -> response() +%% @doc Start the HTTP response by sending the Code HTTP response and +%% ResponseHeaders including a Content-Length of Length. The server +%% will set header defaults such as Server +%% and Date if not present in ResponseHeaders. +start_response_length({Code, ResponseHeaders, Length}) -> + HResponse = mochiweb_headers:make(ResponseHeaders), + HResponse1 = mochiweb_headers:enter("Content-Length", Length, HResponse), + start_response({Code, HResponse1}). + +%% @spec respond({integer(), ioheaders(), iodata() | chunked | {file, IoDevice}}) -> response() +%% @doc Start the HTTP response with start_response, and send Body to the +%% client (if the get(method) /= 'HEAD'). The Content-Length header +%% will be set by the Body length, and the server will insert header +%% defaults. +respond({Code, ResponseHeaders, {file, IoDevice}}) -> + Length = iodevice_size(IoDevice), + Response = start_response_length({Code, ResponseHeaders, Length}), + case Method of + 'HEAD' -> + ok; + _ -> + iodevice_stream(IoDevice) + end, + Response; +respond({Code, ResponseHeaders, chunked}) -> + HResponse = mochiweb_headers:make(ResponseHeaders), + HResponse1 = case Method of + 'HEAD' -> + %% This is what Google does, http://www.google.com/ + %% is chunked but HEAD gets Content-Length: 0. + %% The RFC is ambiguous so emulating Google is smart. + mochiweb_headers:enter("Content-Length", "0", + HResponse); + _ when Version >= {1, 1} -> + %% Only use chunked encoding for HTTP/1.1 + mochiweb_headers:enter("Transfer-Encoding", "chunked", + HResponse); + _ -> + %% For pre-1.1 clients we send the data as-is + %% without a Content-Length header and without + %% chunk delimiters. Since the end of the document + %% is now ambiguous we must force a close. + put(?SAVE_FORCE_CLOSE, true), + HResponse + end, + start_response({Code, HResponse1}); +respond({Code, ResponseHeaders, Body}) -> + Response = start_response_length({Code, ResponseHeaders, iolist_size(Body)}), + case Method of + 'HEAD' -> + ok; + _ -> + send(Body) + end, + Response. + +%% @spec not_found() -> response() +%% @doc Alias for not_found([]). +not_found() -> + not_found([]). + +%% @spec not_found(ExtraHeaders) -> response() +%% @doc Alias for respond({404, [{"Content-Type", "text/plain"} +%% | ExtraHeaders], <<"Not found.">>}). +not_found(ExtraHeaders) -> + respond({404, [{"Content-Type", "text/plain"} | ExtraHeaders], + <<"Not found.">>}). + +%% @spec ok({value(), iodata()} | {value(), ioheaders(), iodata() | {file, IoDevice}}) -> +%% response() +%% @doc respond({200, [{"Content-Type", ContentType} | Headers], Body}). +ok({ContentType, Body}) -> + ok({ContentType, [], Body}); +ok({ContentType, ResponseHeaders, Body}) -> + HResponse = mochiweb_headers:make(ResponseHeaders), + case THIS:get(range) of + X when X =:= undefined; X =:= fail -> + HResponse1 = mochiweb_headers:enter("Content-Type", ContentType, HResponse), + respond({200, HResponse1, Body}); + Ranges -> + {PartList, Size} = range_parts(Body, Ranges), + case PartList of + [] -> %% no valid ranges + HResponse1 = mochiweb_headers:enter("Content-Type", + ContentType, + HResponse), + %% could be 416, for now we'll just return 200 + respond({200, HResponse1, Body}); + PartList -> + {RangeHeaders, RangeBody} = + parts_to_body(PartList, ContentType, Size), + HResponse1 = mochiweb_headers:enter_from_list( + [{"Accept-Ranges", "bytes"} | + RangeHeaders], + HResponse), + respond({206, HResponse1, RangeBody}) + end + end. + +%% @spec should_close() -> bool() +%% @doc Return true if the connection must be closed. If false, using +%% Keep-Alive should be safe. +should_close() -> + ForceClose = erlang:get(mochiweb_request_force_close) =/= undefined, + DidNotRecv = erlang:get(mochiweb_request_recv) =:= undefined, + ForceClose orelse Version < {1, 0} + %% Connection: close + orelse get_header_value("connection") =:= "close" + %% HTTP 1.0 requires Connection: Keep-Alive + orelse (Version =:= {1, 0} + andalso get_header_value("connection") =/= "Keep-Alive") + %% unread data left on the socket, can't safely continue + orelse (DidNotRecv + andalso get_header_value("content-length") =/= undefined + andalso list_to_integer(get_header_value("content-length")) > 0) + orelse (DidNotRecv + andalso get_header_value("transfer-encoding") =:= "chunked"). + +%% @spec cleanup() -> ok +%% @doc Clean up any junk in the process dictionary, required before continuing +%% a Keep-Alive request. +cleanup() -> + [erase(K) || K <- [?SAVE_QS, + ?SAVE_PATH, + ?SAVE_RECV, + ?SAVE_BODY, + ?SAVE_POST, + ?SAVE_COOKIE, + ?SAVE_FORCE_CLOSE]], + ok. + +%% @spec parse_qs() -> [{Key::string(), Value::string()}] +%% @doc Parse the query string of the URL. +parse_qs() -> + case erlang:get(?SAVE_QS) of + undefined -> + {_, QueryString, _} = mochiweb_util:urlsplit_path(RawPath), + Parsed = mochiweb_util:parse_qs(QueryString), + put(?SAVE_QS, Parsed), + Parsed; + Cached -> + Cached + end. + +%% @spec get_cookie_value(Key::string) -> string() | undefined +%% @doc Get the value of the given cookie. +get_cookie_value(Key) -> + proplists:get_value(Key, parse_cookie()). + +%% @spec parse_cookie() -> [{Key::string(), Value::string()}] +%% @doc Parse the cookie header. +parse_cookie() -> + case erlang:get(?SAVE_COOKIE) of + undefined -> + Cookies = case get_header_value("cookie") of + undefined -> + []; + Value -> + mochiweb_cookies:parse_cookie(Value) + end, + put(?SAVE_COOKIE, Cookies), + Cookies; + Cached -> + Cached + end. + +%% @spec parse_post() -> [{Key::string(), Value::string()}] +%% @doc Parse an application/x-www-form-urlencoded form POST. This +%% has the side-effect of calling recv_body(). +parse_post() -> + case erlang:get(?SAVE_POST) of + undefined -> + Parsed = case recv_body() of + undefined -> + []; + Binary -> + case get_primary_header_value("content-type") of + "application/x-www-form-urlencoded" ++ _ -> + mochiweb_util:parse_qs(Binary); + _ -> + [] + end + end, + put(?SAVE_POST, Parsed), + Parsed; + Cached -> + Cached + end. + +%% @spec stream_chunked_body(integer(), fun(), term()) -> term() +%% @doc The function is called for each chunk. +%% Used internally by read_chunked_body. +stream_chunked_body(MaxChunkSize, Fun, FunState) -> + case read_chunk_length() of + 0 -> + Fun({0, read_chunk(0)}, FunState); + Length when Length > MaxChunkSize -> + NewState = read_sub_chunks(Length, MaxChunkSize, Fun, FunState), + stream_chunked_body(MaxChunkSize, Fun, NewState); + Length -> + NewState = Fun({Length, read_chunk(Length)}, FunState), + stream_chunked_body(MaxChunkSize, Fun, NewState) + end. + +stream_unchunked_body(0, _MaxChunkSize, Fun, FunState) -> + Fun({0, <<>>}, FunState); +stream_unchunked_body(Length, MaxChunkSize, Fun, FunState) when Length > MaxChunkSize -> + Bin = recv(MaxChunkSize), + NewState = Fun({MaxChunkSize, Bin}, FunState), + stream_unchunked_body(Length - MaxChunkSize, MaxChunkSize, Fun, NewState); +stream_unchunked_body(Length, MaxChunkSize, Fun, FunState) -> + Bin = recv(Length), + NewState = Fun({Length, Bin}, FunState), + stream_unchunked_body(0, MaxChunkSize, Fun, NewState). + + +%% @spec read_chunk_length() -> integer() +%% @doc Read the length of the next HTTP chunk. +read_chunk_length() -> + inet:setopts(Socket, [{packet, line}]), + case gen_tcp:recv(Socket, 0, ?IDLE_TIMEOUT) of + {ok, Header} -> + inet:setopts(Socket, [{packet, raw}]), + Splitter = fun (C) -> + C =/= $\r andalso C =/= $\n andalso C =/= $ + end, + {Hex, _Rest} = lists:splitwith(Splitter, binary_to_list(Header)), + mochihex:to_int(Hex); + _ -> + exit(normal) + end. + +%% @spec read_chunk(integer()) -> Chunk::binary() | [Footer::binary()] +%% @doc Read in a HTTP chunk of the given length. If Length is 0, then read the +%% HTTP footers (as a list of binaries, since they're nominal). +read_chunk(0) -> + inet:setopts(Socket, [{packet, line}]), + F = fun (F1, Acc) -> + case gen_tcp:recv(Socket, 0, ?IDLE_TIMEOUT) of + {ok, <<"\r\n">>} -> + Acc; + {ok, Footer} -> + F1(F1, [Footer | Acc]); + _ -> + exit(normal) + end + end, + Footers = F(F, []), + inet:setopts(Socket, [{packet, raw}]), + Footers; +read_chunk(Length) -> + case gen_tcp:recv(Socket, 2 + Length, ?IDLE_TIMEOUT) of + {ok, <>} -> + Chunk; + _ -> + exit(normal) + end. + +read_sub_chunks(Length, MaxChunkSize, Fun, FunState) when Length > MaxChunkSize -> + Bin = recv(MaxChunkSize), + NewState = Fun({size(Bin), Bin}, FunState), + read_sub_chunks(Length - MaxChunkSize, MaxChunkSize, Fun, NewState); + +read_sub_chunks(Length, _MaxChunkSize, Fun, FunState) -> + Fun({Length, read_chunk(Length)}, FunState). + +%% @spec serve_file(Path, DocRoot) -> Response +%% @doc Serve a file relative to DocRoot. +serve_file(Path, DocRoot) -> + serve_file(Path, DocRoot, []). + +%% @spec serve_file(Path, DocRoot, ExtraHeaders) -> Response +%% @doc Serve a file relative to DocRoot. +serve_file(Path, DocRoot, ExtraHeaders) -> + case mochiweb_util:safe_relative_path(Path) of + undefined -> + not_found(ExtraHeaders); + RelPath -> + FullPath = filename:join([DocRoot, RelPath]), + File = case filelib:is_dir(FullPath) of + true -> + filename:join([FullPath, "index.html"]); + false -> + FullPath + end, + case file:read_file_info(File) of + {ok, FileInfo} -> + LastModified = httpd_util:rfc1123_date(FileInfo#file_info.mtime), + case get_header_value("if-modified-since") of + LastModified -> + respond({304, ExtraHeaders, ""}); + _ -> + case file:open(File, [raw, binary]) of + {ok, IoDevice} -> + ContentType = mochiweb_util:guess_mime(File), + Res = ok({ContentType, + [{"last-modified", LastModified} + | ExtraHeaders], + {file, IoDevice}}), + file:close(IoDevice), + Res; + _ -> + not_found(ExtraHeaders) + end + end; + {error, _} -> + not_found(ExtraHeaders) + end + end. + + +%% Internal API + +server_headers() -> + [{"Server", "MochiWeb/1.0 (" ++ ?QUIP ++ ")"}, + {"Date", httpd_util:rfc1123_date()}]. + +make_io(Atom) when is_atom(Atom) -> + atom_to_list(Atom); +make_io(Integer) when is_integer(Integer) -> + integer_to_list(Integer); +make_io(Io) when is_list(Io); is_binary(Io) -> + Io. + +make_code(X) when is_integer(X) -> + [integer_to_list(X), [" " | httpd_util:reason_phrase(X)]]; +make_code(Io) when is_list(Io); is_binary(Io) -> + Io. + +make_version({1, 0}) -> + <<"HTTP/1.0 ">>; +make_version(_) -> + <<"HTTP/1.1 ">>. + +iodevice_stream(IoDevice) -> + case file:read(IoDevice, ?READ_SIZE) of + eof -> + ok; + {ok, Data} -> + ok = send(Data), + iodevice_stream(IoDevice) + end. + + +parts_to_body([{Start, End, Body}], ContentType, Size) -> + %% return body for a range reponse with a single body + HeaderList = [{"Content-Type", ContentType}, + {"Content-Range", + ["bytes ", + make_io(Start), "-", make_io(End), + "/", make_io(Size)]}], + {HeaderList, Body}; +parts_to_body(BodyList, ContentType, Size) when is_list(BodyList) -> + %% return + %% header Content-Type: multipart/byteranges; boundary=441934886133bdee4 + %% and multipart body + Boundary = mochihex:to_hex(crypto:rand_bytes(8)), + HeaderList = [{"Content-Type", + ["multipart/byteranges; ", + "boundary=", Boundary]}], + MultiPartBody = multipart_body(BodyList, ContentType, Boundary, Size), + + {HeaderList, MultiPartBody}. + +multipart_body([], _ContentType, Boundary, _Size) -> + ["--", Boundary, "--\r\n"]; +multipart_body([{Start, End, Body} | BodyList], ContentType, Boundary, Size) -> + ["--", Boundary, "\r\n", + "Content-Type: ", ContentType, "\r\n", + "Content-Range: ", + "bytes ", make_io(Start), "-", make_io(End), + "/", make_io(Size), "\r\n\r\n", + Body, "\r\n" + | multipart_body(BodyList, ContentType, Boundary, Size)]. + +iodevice_size(IoDevice) -> + {ok, Size} = file:position(IoDevice, eof), + {ok, 0} = file:position(IoDevice, bof), + Size. + +range_parts({file, IoDevice}, Ranges) -> + Size = iodevice_size(IoDevice), + F = fun (Spec, Acc) -> + case range_skip_length(Spec, Size) of + invalid_range -> + Acc; + V -> + [V | Acc] + end + end, + LocNums = lists:foldr(F, [], Ranges), + {ok, Data} = file:pread(IoDevice, LocNums), + Bodies = lists:zipwith(fun ({Skip, Length}, PartialBody) -> + {Skip, Skip + Length - 1, PartialBody} + end, + LocNums, Data), + {Bodies, Size}; + +range_parts(Body0, Ranges) -> + Body = iolist_to_binary(Body0), + Size = size(Body), + F = fun(Spec, Acc) -> + case range_skip_length(Spec, Size) of + invalid_range -> + Acc; + {Skip, Length} -> + <<_:Skip/binary, PartialBody:Length/binary, _/binary>> = Body, + [{Skip, Skip + Length - 1, PartialBody} | Acc] + end + end, + {lists:foldr(F, [], Ranges), Size}. + +range_skip_length(Spec, Size) -> + case Spec of + {none, R} when R =< Size, R >= 0 -> + {Size - R, R}; + {none, _OutOfRange} -> + {0, Size}; + {R, none} when R >= 0, R < Size -> + {R, Size - R}; + {_OutOfRange, none} -> + invalid_range; + {Start, End} when 0 =< Start, Start =< End, End < Size -> + {Start, End - Start + 1}; + {_OutOfRange, _End} -> + invalid_range + end. + +parse_range_request(RawRange) when is_list(RawRange) -> + try + "bytes=" ++ RangeString = RawRange, + Ranges = string:tokens(RangeString, ","), + lists:map(fun ("-" ++ V) -> + {none, list_to_integer(V)}; + (R) -> + case string:tokens(R, "-") of + [S1, S2] -> + {list_to_integer(S1), list_to_integer(S2)}; + [S] -> + {list_to_integer(S), none} + end + end, + Ranges) + catch + _:_ -> + fail + end. + + +test() -> + ok = test_range(), + ok. + +test_range() -> + %% valid, single ranges + io:format("Testing parse_range_request with valid single ranges~n"), + io:format("1"), + [{20, 30}] = parse_range_request("bytes=20-30"), + io:format("2"), + [{20, none}] = parse_range_request("bytes=20-"), + io:format("3"), + [{none, 20}] = parse_range_request("bytes=-20"), + io:format(".. ok ~n"), + + + %% invalid, single ranges + io:format("Testing parse_range_request with invalid ranges~n"), + io:format("1"), + fail = parse_range_request(""), + io:format("2"), + fail = parse_range_request("garbage"), + io:format("3"), + fail = parse_range_request("bytes=-20-30"), + io:format(".. ok ~n"), + + %% valid, multiple range + io:format("Testing parse_range_request with valid multiple ranges~n"), + io:format("1"), + [{20, 30}, {50, 100}, {110, 200}] = + parse_range_request("bytes=20-30,50-100,110-200"), + io:format("2"), + [{20, none}, {50, 100}, {none, 200}] = + parse_range_request("bytes=20-,50-100,-200"), + io:format(".. ok~n"), + + %% no ranges + io:format("Testing out parse_range_request with no ranges~n"), + io:format("1"), + [] = parse_range_request("bytes="), + io:format(".. ok~n"), + + Body = <<"012345678901234567890123456789012345678901234567890123456789">>, + BodySize = size(Body), %% 60 + BodySize = 60, + + %% these values assume BodySize =:= 60 + io:format("Testing out range_skip_length on valid ranges~n"), + io:format("1"), + {1,9} = range_skip_length({1,9}, BodySize), %% 1-9 + io:format("2"), + {10,10} = range_skip_length({10,19}, BodySize), %% 10-19 + io:format("3"), + {40, 20} = range_skip_length({none, 20}, BodySize), %% -20 + io:format("4"), + {30, 30} = range_skip_length({30, none}, BodySize), %% 30- + io:format(".. ok ~n"), + + %% valid edge cases for range_skip_length + io:format("Testing out range_skip_length on valid edge case ranges~n"), + io:format("1"), + {BodySize, 0} = range_skip_length({none, 0}, BodySize), + io:format("2"), + {0, BodySize} = range_skip_length({none, BodySize}, BodySize), + io:format("3"), + {0, BodySize} = range_skip_length({0, none}, BodySize), + BodySizeLess1 = BodySize - 1, + io:format("4"), + {BodySizeLess1, 1} = range_skip_length({BodySize - 1, none}, BodySize), + + %% out of range, return whole thing + io:format("5"), + {0, BodySize} = range_skip_length({none, BodySize + 1}, BodySize), + io:format("6"), + {0, BodySize} = range_skip_length({none, -1}, BodySize), + io:format(".. ok ~n"), + + %% invalid ranges + io:format("Testing out range_skip_length on invalid ranges~n"), + io:format("1"), + invalid_range = range_skip_length({-1, 30}, BodySize), + io:format("2"), + invalid_range = range_skip_length({0, BodySize + 1}, BodySize), + io:format("3"), + invalid_range = range_skip_length({-1, BodySize + 1}, BodySize), + io:format("4"), + invalid_range = range_skip_length({BodySize, 40}, BodySize), + io:format("5"), + invalid_range = range_skip_length({-1, none}, BodySize), + io:format("6"), + invalid_range = range_skip_length({BodySize, none}, BodySize), + io:format(".. ok ~n"), + ok. + diff --git a/web/api/webmachine/deps/mochiweb/src/mochiweb_response.erl b/web/api/webmachine/deps/mochiweb/src/mochiweb_response.erl new file mode 100644 index 0000000..6285c4c --- /dev/null +++ b/web/api/webmachine/deps/mochiweb/src/mochiweb_response.erl @@ -0,0 +1,56 @@ +%% @author Bob Ippolito +%% @copyright 2007 Mochi Media, Inc. + +%% @doc Response abstraction. + +-module(mochiweb_response, [Request, Code, Headers]). +-author('bob@mochimedia.com'). + +-define(QUIP, "Any of you quaids got a smint?"). + +-export([get_header_value/1, get/1, dump/0]). +-export([send/1, write_chunk/1]). + +%% @spec get_header_value(string() | atom() | binary()) -> string() | undefined +%% @doc Get the value of the given response header. +get_header_value(K) -> + mochiweb_headers:get_value(K, Headers). + +%% @spec get(request | code | headers) -> term() +%% @doc Return the internal representation of the given field. +get(request) -> + Request; +get(code) -> + Code; +get(headers) -> + Headers. + +%% @spec dump() -> {mochiweb_request, [{atom(), term()}]} +%% @doc Dump the internal representation to a "human readable" set of terms +%% for debugging/inspection purposes. +dump() -> + [{request, Request:dump()}, + {code, Code}, + {headers, mochiweb_headers:to_list(Headers)}]. + +%% @spec send(iodata()) -> ok +%% @doc Send data over the socket if the method is not HEAD. +send(Data) -> + case Request:get(method) of + 'HEAD' -> + ok; + _ -> + Request:send(Data) + end. + +%% @spec write_chunk(iodata()) -> ok +%% @doc Write a chunk of a HTTP chunked response. If Data is zero length, +%% then the chunked response will be finished. +write_chunk(Data) -> + case Request:get(version) of + Version when Version >= {1, 1} -> + Length = iolist_size(Data), + send([io_lib:format("~.16b\r\n", [Length]), Data, <<"\r\n">>]); + _ -> + send(Data) + end. diff --git a/web/api/webmachine/deps/mochiweb/src/mochiweb_skel.erl b/web/api/webmachine/deps/mochiweb/src/mochiweb_skel.erl new file mode 100644 index 0000000..9191594 --- /dev/null +++ b/web/api/webmachine/deps/mochiweb/src/mochiweb_skel.erl @@ -0,0 +1,73 @@ +-module(mochiweb_skel). +-export([skelcopy/2]). + +-include_lib("kernel/include/file.hrl"). + +%% External API + +skelcopy(DestDir, Name) -> + ok = ensuredir(DestDir), + LDst = case length(filename:dirname(DestDir)) of + 1 -> %% handle case when dirname returns "/" + 0; + N -> + N + 1 + end, + skelcopy(src(), DestDir, Name, LDst), + ok = file:make_symlink( + filename:join(filename:dirname(code:which(?MODULE)), ".."), + filename:join([DestDir, Name, "deps", "mochiweb-src"])). + + +%% Internal API + +src() -> + Dir = filename:dirname(code:which(?MODULE)), + filename:join(Dir, "../priv/skel"). + +skel() -> + "skel". + +skelcopy(Src, DestDir, Name, LDst) -> + Dest = re:replace(filename:basename(Src), skel(), Name, + [global, {return, list}]), + case file:read_file_info(Src) of + {ok, #file_info{type=directory, mode=Mode}} -> + Dir = DestDir ++ "/" ++ Dest, + EDst = lists:nthtail(LDst, Dir), + ok = ensuredir(Dir), + ok = file:write_file_info(Dir, #file_info{mode=Mode}), + {ok, Files} = file:list_dir(Src), + io:format("~s/~n", [EDst]), + lists:foreach(fun ("." ++ _) -> ok; + (F) -> + skelcopy(filename:join(Src, F), + Dir, + Name, + LDst) + end, + Files), + ok; + {ok, #file_info{type=regular, mode=Mode}} -> + OutFile = filename:join(DestDir, Dest), + {ok, B} = file:read_file(Src), + S = re:replace(binary_to_list(B), skel(), Name, + [{return, list}, global]), + ok = file:write_file(OutFile, list_to_binary(S)), + ok = file:write_file_info(OutFile, #file_info{mode=Mode}), + io:format(" ~s~n", [filename:basename(Src)]), + ok; + {ok, _} -> + io:format("ignored source file: ~p~n", [Src]), + ok + end. + +ensuredir(Dir) -> + case file:make_dir(Dir) of + ok -> + ok; + {error, eexist} -> + ok; + E -> + E + end. diff --git a/web/api/webmachine/deps/mochiweb/src/mochiweb_socket_server.erl b/web/api/webmachine/deps/mochiweb/src/mochiweb_socket_server.erl new file mode 100644 index 0000000..d4853da --- /dev/null +++ b/web/api/webmachine/deps/mochiweb/src/mochiweb_socket_server.erl @@ -0,0 +1,248 @@ +%% @author Bob Ippolito +%% @copyright 2007 Mochi Media, Inc. + +%% @doc MochiWeb socket server. + +-module(mochiweb_socket_server). +-author('bob@mochimedia.com'). +-behaviour(gen_server). + +-export([start/1, stop/1]). +-export([init/1, handle_call/3, handle_cast/2, terminate/2, code_change/3, + handle_info/2]). +-export([get/2]). + +-export([acceptor_loop/1]). + +-record(mochiweb_socket_server, + {port, + loop, + name=undefined, + max=2048, + ip=any, + listen=null, + acceptor=null, + backlog=30}). + +start(State=#mochiweb_socket_server{}) -> + start_server(State); +start(Options) -> + start(parse_options(Options)). + +get(Name, Property) -> + gen_server:call(Name, {get, Property}). + +stop(Name) when is_atom(Name) -> + gen_server:cast(Name, stop); +stop(Pid) when is_pid(Pid) -> + gen_server:cast(Pid, stop); +stop({local, Name}) -> + stop(Name); +stop({global, Name}) -> + stop(Name); +stop(Options) -> + State = parse_options(Options), + stop(State#mochiweb_socket_server.name). + +%% Internal API + +parse_options(Options) -> + parse_options(Options, #mochiweb_socket_server{}). + +parse_options([], State) -> + State; +parse_options([{name, L} | Rest], State) when is_list(L) -> + Name = {local, list_to_atom(L)}, + parse_options(Rest, State#mochiweb_socket_server{name=Name}); +parse_options([{name, A} | Rest], State) when is_atom(A) -> + Name = {local, A}, + parse_options(Rest, State#mochiweb_socket_server{name=Name}); +parse_options([{name, Name} | Rest], State) -> + parse_options(Rest, State#mochiweb_socket_server{name=Name}); +parse_options([{port, L} | Rest], State) when is_list(L) -> + Port = list_to_integer(L), + parse_options(Rest, State#mochiweb_socket_server{port=Port}); +parse_options([{port, Port} | Rest], State) -> + parse_options(Rest, State#mochiweb_socket_server{port=Port}); +parse_options([{ip, Ip} | Rest], State) -> + ParsedIp = case Ip of + any -> + any; + Ip when is_tuple(Ip) -> + Ip; + Ip when is_list(Ip) -> + {ok, IpTuple} = inet_parse:address(Ip), + IpTuple + end, + parse_options(Rest, State#mochiweb_socket_server{ip=ParsedIp}); +parse_options([{loop, Loop} | Rest], State) -> + parse_options(Rest, State#mochiweb_socket_server{loop=Loop}); +parse_options([{backlog, Backlog} | Rest], State) -> + parse_options(Rest, State#mochiweb_socket_server{backlog=Backlog}); +parse_options([{max, Max} | Rest], State) -> + MaxInt = case Max of + Max when is_list(Max) -> + list_to_integer(Max); + Max when is_integer(Max) -> + Max + end, + parse_options(Rest, State#mochiweb_socket_server{max=MaxInt}). + +start_server(State=#mochiweb_socket_server{name=Name}) -> + case Name of + undefined -> + gen_server:start_link(?MODULE, State, []); + _ -> + gen_server:start_link(Name, ?MODULE, State, []) + end. + +ipv6_supported() -> + case (catch inet:getaddr("localhost", inet6)) of + {ok, _Addr} -> + true; + {error, _} -> + false + end. + +init(State=#mochiweb_socket_server{ip=Ip, port=Port, backlog=Backlog}) -> + process_flag(trap_exit, true), + BaseOpts = [binary, + {reuseaddr, true}, + {packet, 0}, + {backlog, Backlog}, + {recbuf, 8192}, + {active, false}, + {nodelay, true}], + Opts = case Ip of + any -> + case ipv6_supported() of % IPv4, and IPv6 if supported + true -> [inet, inet6 | BaseOpts]; + _ -> BaseOpts + end; + {_, _, _, _} -> % IPv4 + [inet, {ip, Ip} | BaseOpts]; + {_, _, _, _, _, _, _, _} -> % IPv6 + [inet6, {ip, Ip} | BaseOpts] + end, + case gen_tcp_listen(Port, Opts, State) of + {stop, eacces} -> + case Port < 1024 of + true -> + case fdsrv:start() of + {ok, _} -> + case fdsrv:bind_socket(tcp, Port) of + {ok, Fd} -> + gen_tcp_listen(Port, [{fd, Fd} | Opts], State); + _ -> + {stop, fdsrv_bind_failed} + end; + _ -> + {stop, fdsrv_start_failed} + end; + false -> + {stop, eacces} + end; + Other -> + Other + end. + +gen_tcp_listen(Port, Opts, State) -> + case gen_tcp:listen(Port, Opts) of + {ok, Listen} -> + {ok, ListenPort} = inet:port(Listen), + {ok, new_acceptor(State#mochiweb_socket_server{listen=Listen, + port=ListenPort})}; + {error, Reason} -> + {stop, Reason} + end. + +new_acceptor(State=#mochiweb_socket_server{max=0}) -> + io:format("Not accepting new connections~n"), + State#mochiweb_socket_server{acceptor=null}; +new_acceptor(State=#mochiweb_socket_server{listen=Listen,loop=Loop}) -> + Pid = proc_lib:spawn_link(?MODULE, acceptor_loop, + [{self(), Listen, Loop}]), + State#mochiweb_socket_server{acceptor=Pid}. + +call_loop({M, F}, Socket) -> + M:F(Socket); +call_loop(Loop, Socket) -> + Loop(Socket). + +acceptor_loop({Server, Listen, Loop}) -> + case catch gen_tcp:accept(Listen) of + {ok, Socket} -> + gen_server:cast(Server, {accepted, self()}), + call_loop(Loop, Socket); + {error, closed} -> + exit({error, closed}); + Other -> + error_logger:error_report( + [{application, mochiweb}, + "Accept failed error", + lists:flatten(io_lib:format("~p", [Other]))]), + exit({error, accept_failed}) + end. + + +do_get(port, #mochiweb_socket_server{port=Port}) -> + Port. + +handle_call({get, Property}, _From, State) -> + Res = do_get(Property, State), + {reply, Res, State}; +handle_call(_Message, _From, State) -> + Res = error, + {reply, Res, State}. + +handle_cast({accepted, Pid}, + State=#mochiweb_socket_server{acceptor=Pid, max=Max}) -> + % io:format("accepted ~p~n", [Pid]), + State1 = State#mochiweb_socket_server{max=Max - 1}, + {noreply, new_acceptor(State1)}; +handle_cast(stop, State) -> + {stop, normal, State}. + +terminate(_Reason, #mochiweb_socket_server{listen=Listen, port=Port}) -> + gen_tcp:close(Listen), + case Port < 1024 of + true -> + catch fdsrv:stop(), + ok; + false -> + ok + end. + +code_change(_OldVsn, State, _Extra) -> + State. + +handle_info({'EXIT', Pid, normal}, + State=#mochiweb_socket_server{acceptor=Pid}) -> + % io:format("normal acceptor down~n"), + {noreply, new_acceptor(State)}; +handle_info({'EXIT', Pid, Reason}, + State=#mochiweb_socket_server{acceptor=Pid}) -> + error_logger:error_report({?MODULE, ?LINE, + {acceptor_error, Reason}}), + timer:sleep(100), + {noreply, new_acceptor(State)}; +handle_info({'EXIT', _LoopPid, Reason}, + State=#mochiweb_socket_server{acceptor=Pid, max=Max}) -> + case Reason of + normal -> + ok; + _ -> + error_logger:error_report({?MODULE, ?LINE, + {child_error, Reason}}) + end, + State1 = State#mochiweb_socket_server{max=Max + 1}, + State2 = case Pid of + null -> + new_acceptor(State1); + _ -> + State1 + end, + {noreply, State2}; +handle_info(Info, State) -> + error_logger:info_report([{'INFO', Info}, {'State', State}]), + {noreply, State}. diff --git a/web/api/webmachine/deps/mochiweb/src/mochiweb_sup.erl b/web/api/webmachine/deps/mochiweb/src/mochiweb_sup.erl new file mode 100644 index 0000000..5cb525b --- /dev/null +++ b/web/api/webmachine/deps/mochiweb/src/mochiweb_sup.erl @@ -0,0 +1,34 @@ +%% @author Bob Ippolito +%% @copyright 2007 Mochi Media, Inc. + +%% @doc Supervisor for the mochiweb application. + +-module(mochiweb_sup). +-author('bob@mochimedia.com'). + +-behaviour(supervisor). + +%% External exports +-export([start_link/0, upgrade/0]). + +%% supervisor callbacks +-export([init/1]). + +%% @spec start_link() -> ServerRet +%% @doc API for starting the supervisor. +start_link() -> + supervisor:start_link({local, ?MODULE}, ?MODULE, []). + +%% @spec upgrade() -> ok +%% @doc Add processes if necessary. +upgrade() -> + {ok, {_, Specs}} = init([]), + [supervisor:start_child(?MODULE, Spec) || Spec <- Specs], + ok. + +%% @spec init([]) -> SupervisorTree +%% @doc supervisor callback, ensures yaws is in embedded mode and then +%% returns the supervisor tree. +init([]) -> + Processes = [], + {ok, {{one_for_one, 10, 10}, Processes}}. diff --git a/web/api/webmachine/deps/mochiweb/src/mochiweb_util.erl b/web/api/webmachine/deps/mochiweb/src/mochiweb_util.erl new file mode 100644 index 0000000..1ebb342 --- /dev/null +++ b/web/api/webmachine/deps/mochiweb/src/mochiweb_util.erl @@ -0,0 +1,579 @@ +%% @author Bob Ippolito +%% @copyright 2007 Mochi Media, Inc. + +%% @doc Utilities for parsing and quoting. + +-module(mochiweb_util). +-author('bob@mochimedia.com'). +-export([join/2, quote_plus/1, urlencode/1, parse_qs/1, unquote/1]). +-export([path_split/1]). +-export([urlsplit/1, urlsplit_path/1, urlunsplit/1, urlunsplit_path/1]). +-export([guess_mime/1, parse_header/1]). +-export([shell_quote/1, cmd/1, cmd_string/1, cmd_port/2]). +-export([record_to_proplist/2, record_to_proplist/3]). +-export([safe_relative_path/1, partition/2]). +-export([test/0]). + +-define(PERCENT, 37). % $\% +-define(FULLSTOP, 46). % $\. +-define(IS_HEX(C), ((C >= $0 andalso C =< $9) orelse + (C >= $a andalso C =< $f) orelse + (C >= $A andalso C =< $F))). +-define(QS_SAFE(C), ((C >= $a andalso C =< $z) orelse + (C >= $A andalso C =< $Z) orelse + (C >= $0 andalso C =< $9) orelse + (C =:= ?FULLSTOP orelse C =:= $- orelse C =:= $~ orelse + C =:= $_))). + +hexdigit(C) when C < 10 -> $0 + C; +hexdigit(C) when C < 16 -> $A + (C - 10). + +unhexdigit(C) when C >= $0, C =< $9 -> C - $0; +unhexdigit(C) when C >= $a, C =< $f -> C - $a + 10; +unhexdigit(C) when C >= $A, C =< $F -> C - $A + 10. + +%% @spec partition(String, Sep) -> {String, [], []} | {Prefix, Sep, Postfix} +%% @doc Inspired by Python 2.5's str.partition: +%% partition("foo/bar", "/") = {"foo", "/", "bar"}, +%% partition("foo", "/") = {"foo", "", ""}. +partition(String, Sep) -> + case partition(String, Sep, []) of + undefined -> + {String, "", ""}; + Result -> + Result + end. + +partition("", _Sep, _Acc) -> + undefined; +partition(S, Sep, Acc) -> + case partition2(S, Sep) of + undefined -> + [C | Rest] = S, + partition(Rest, Sep, [C | Acc]); + Rest -> + {lists:reverse(Acc), Sep, Rest} + end. + +partition2(Rest, "") -> + Rest; +partition2([C | R1], [C | R2]) -> + partition2(R1, R2); +partition2(_S, _Sep) -> + undefined. + + + +%% @spec safe_relative_path(string()) -> string() | undefined +%% @doc Return the reduced version of a relative path or undefined if it +%% is not safe. safe relative paths can be joined with an absolute path +%% and will result in a subdirectory of the absolute path. +safe_relative_path("/" ++ _) -> + undefined; +safe_relative_path(P) -> + safe_relative_path(P, []). + +safe_relative_path("", Acc) -> + case Acc of + [] -> + ""; + _ -> + string:join(lists:reverse(Acc), "/") + end; +safe_relative_path(P, Acc) -> + case partition(P, "/") of + {"", "/", _} -> + %% /foo or foo//bar + undefined; + {"..", _, _} when Acc =:= [] -> + undefined; + {"..", _, Rest} -> + safe_relative_path(Rest, tl(Acc)); + {Part, "/", ""} -> + safe_relative_path("", ["", Part | Acc]); + {Part, _, Rest} -> + safe_relative_path(Rest, [Part | Acc]) + end. + +%% @spec shell_quote(string()) -> string() +%% @doc Quote a string according to UNIX shell quoting rules, returns a string +%% surrounded by double quotes. +shell_quote(L) -> + shell_quote(L, [$\"]). + +%% @spec cmd_port([string()], Options) -> port() +%% @doc open_port({spawn, mochiweb_util:cmd_string(Argv)}, Options). +cmd_port(Argv, Options) -> + open_port({spawn, cmd_string(Argv)}, Options). + +%% @spec cmd([string()]) -> string() +%% @doc os:cmd(cmd_string(Argv)). +cmd(Argv) -> + os:cmd(cmd_string(Argv)). + +%% @spec cmd_string([string()]) -> string() +%% @doc Create a shell quoted command string from a list of arguments. +cmd_string(Argv) -> + join([shell_quote(X) || X <- Argv], " "). + +%% @spec join([string()], Separator) -> string() +%% @doc Join a list of strings together with the given separator +%% string or char. +join([], _Separator) -> + []; +join([S], _Separator) -> + lists:flatten(S); +join(Strings, Separator) -> + lists:flatten(revjoin(lists:reverse(Strings), Separator, [])). + +revjoin([], _Separator, Acc) -> + Acc; +revjoin([S | Rest], Separator, []) -> + revjoin(Rest, Separator, [S]); +revjoin([S | Rest], Separator, Acc) -> + revjoin(Rest, Separator, [S, Separator | Acc]). + +%% @spec quote_plus(atom() | integer() | float() | string() | binary()) -> string() +%% @doc URL safe encoding of the given term. +quote_plus(Atom) when is_atom(Atom) -> + quote_plus(atom_to_list(Atom)); +quote_plus(Int) when is_integer(Int) -> + quote_plus(integer_to_list(Int)); +quote_plus(Binary) when is_binary(Binary) -> + quote_plus(binary_to_list(Binary)); +quote_plus(Float) when is_float(Float) -> + quote_plus(mochinum:digits(Float)); +quote_plus(String) -> + quote_plus(String, []). + +quote_plus([], Acc) -> + lists:reverse(Acc); +quote_plus([C | Rest], Acc) when ?QS_SAFE(C) -> + quote_plus(Rest, [C | Acc]); +quote_plus([$\s | Rest], Acc) -> + quote_plus(Rest, [$+ | Acc]); +quote_plus([C | Rest], Acc) -> + <> = <>, + quote_plus(Rest, [hexdigit(Lo), hexdigit(Hi), ?PERCENT | Acc]). + +%% @spec urlencode([{Key, Value}]) -> string() +%% @doc URL encode the property list. +urlencode(Props) -> + RevPairs = lists:foldl(fun ({K, V}, Acc) -> + [[quote_plus(K), $=, quote_plus(V)] | Acc] + end, [], Props), + lists:flatten(revjoin(RevPairs, $&, [])). + +%% @spec parse_qs(string() | binary()) -> [{Key, Value}] +%% @doc Parse a query string or application/x-www-form-urlencoded. +parse_qs(Binary) when is_binary(Binary) -> + parse_qs(binary_to_list(Binary)); +parse_qs(String) -> + parse_qs(String, []). + +parse_qs([], Acc) -> + lists:reverse(Acc); +parse_qs(String, Acc) -> + {Key, Rest} = parse_qs_key(String), + {Value, Rest1} = parse_qs_value(Rest), + parse_qs(Rest1, [{Key, Value} | Acc]). + +parse_qs_key(String) -> + parse_qs_key(String, []). + +parse_qs_key([], Acc) -> + {qs_revdecode(Acc), ""}; +parse_qs_key([$= | Rest], Acc) -> + {qs_revdecode(Acc), Rest}; +parse_qs_key(Rest=[$; | _], Acc) -> + {qs_revdecode(Acc), Rest}; +parse_qs_key(Rest=[$& | _], Acc) -> + {qs_revdecode(Acc), Rest}; +parse_qs_key([C | Rest], Acc) -> + parse_qs_key(Rest, [C | Acc]). + +parse_qs_value(String) -> + parse_qs_value(String, []). + +parse_qs_value([], Acc) -> + {qs_revdecode(Acc), ""}; +parse_qs_value([$; | Rest], Acc) -> + {qs_revdecode(Acc), Rest}; +parse_qs_value([$& | Rest], Acc) -> + {qs_revdecode(Acc), Rest}; +parse_qs_value([C | Rest], Acc) -> + parse_qs_value(Rest, [C | Acc]). + +%% @spec unquote(string() | binary()) -> string() +%% @doc Unquote a URL encoded string. +unquote(Binary) when is_binary(Binary) -> + unquote(binary_to_list(Binary)); +unquote(String) -> + qs_revdecode(lists:reverse(String)). + +qs_revdecode(S) -> + qs_revdecode(S, []). + +qs_revdecode([], Acc) -> + Acc; +qs_revdecode([$+ | Rest], Acc) -> + qs_revdecode(Rest, [$\s | Acc]); +qs_revdecode([Lo, Hi, ?PERCENT | Rest], Acc) when ?IS_HEX(Lo), ?IS_HEX(Hi) -> + qs_revdecode(Rest, [(unhexdigit(Lo) bor (unhexdigit(Hi) bsl 4)) | Acc]); +qs_revdecode([C | Rest], Acc) -> + qs_revdecode(Rest, [C | Acc]). + +%% @spec urlsplit(Url) -> {Scheme, Netloc, Path, Query, Fragment} +%% @doc Return a 5-tuple, does not expand % escapes. Only supports HTTP style +%% URLs. +urlsplit(Url) -> + {Scheme, Url1} = urlsplit_scheme(Url), + {Netloc, Url2} = urlsplit_netloc(Url1), + {Path, Query, Fragment} = urlsplit_path(Url2), + {Scheme, Netloc, Path, Query, Fragment}. + +urlsplit_scheme(Url) -> + urlsplit_scheme(Url, []). + +urlsplit_scheme([], Acc) -> + {"", lists:reverse(Acc)}; +urlsplit_scheme(":" ++ Rest, Acc) -> + {string:to_lower(lists:reverse(Acc)), Rest}; +urlsplit_scheme([C | Rest], Acc) -> + urlsplit_scheme(Rest, [C | Acc]). + +urlsplit_netloc("//" ++ Rest) -> + urlsplit_netloc(Rest, []); +urlsplit_netloc(Path) -> + {"", Path}. + +urlsplit_netloc(Rest=[C | _], Acc) when C =:= $/; C =:= $?; C =:= $# -> + {lists:reverse(Acc), Rest}; +urlsplit_netloc([C | Rest], Acc) -> + urlsplit_netloc(Rest, [C | Acc]). + + +%% @spec path_split(string()) -> {Part, Rest} +%% @doc Split a path starting from the left, as in URL traversal. +%% path_split("foo/bar") = {"foo", "bar"}, +%% path_split("/foo/bar") = {"", "foo/bar"}. +path_split(S) -> + path_split(S, []). + +path_split("", Acc) -> + {lists:reverse(Acc), ""}; +path_split("/" ++ Rest, Acc) -> + {lists:reverse(Acc), Rest}; +path_split([C | Rest], Acc) -> + path_split(Rest, [C | Acc]). + + +%% @spec urlunsplit({Scheme, Netloc, Path, Query, Fragment}) -> string() +%% @doc Assemble a URL from the 5-tuple. Path must be absolute. +urlunsplit({Scheme, Netloc, Path, Query, Fragment}) -> + lists:flatten([case Scheme of "" -> ""; _ -> [Scheme, "://"] end, + Netloc, + urlunsplit_path({Path, Query, Fragment})]). + +%% @spec urlunsplit_path({Path, Query, Fragment}) -> string() +%% @doc Assemble a URL path from the 3-tuple. +urlunsplit_path({Path, Query, Fragment}) -> + lists:flatten([Path, + case Query of "" -> ""; _ -> [$? | Query] end, + case Fragment of "" -> ""; _ -> [$# | Fragment] end]). + +%% @spec urlsplit_path(Url) -> {Path, Query, Fragment} +%% @doc Return a 3-tuple, does not expand % escapes. Only supports HTTP style +%% paths. +urlsplit_path(Path) -> + urlsplit_path(Path, []). + +urlsplit_path("", Acc) -> + {lists:reverse(Acc), "", ""}; +urlsplit_path("?" ++ Rest, Acc) -> + {Query, Fragment} = urlsplit_query(Rest), + {lists:reverse(Acc), Query, Fragment}; +urlsplit_path("#" ++ Rest, Acc) -> + {lists:reverse(Acc), "", Rest}; +urlsplit_path([C | Rest], Acc) -> + urlsplit_path(Rest, [C | Acc]). + +urlsplit_query(Query) -> + urlsplit_query(Query, []). + +urlsplit_query("", Acc) -> + {lists:reverse(Acc), ""}; +urlsplit_query("#" ++ Rest, Acc) -> + {lists:reverse(Acc), Rest}; +urlsplit_query([C | Rest], Acc) -> + urlsplit_query(Rest, [C | Acc]). + +%% @spec guess_mime(string()) -> string() +%% @doc Guess the mime type of a file by the extension of its filename. +guess_mime(File) -> + case filename:extension(File) of + ".html" -> + "text/html"; + ".xhtml" -> + "application/xhtml+xml"; + ".xml" -> + "application/xml"; + ".css" -> + "text/css"; + ".js" -> + "application/x-javascript"; + ".jpg" -> + "image/jpeg"; + ".gif" -> + "image/gif"; + ".png" -> + "image/png"; + ".swf" -> + "application/x-shockwave-flash"; + ".zip" -> + "application/zip"; + ".bz2" -> + "application/x-bzip2"; + ".gz" -> + "application/x-gzip"; + ".tar" -> + "application/x-tar"; + ".tgz" -> + "application/x-gzip"; + ".txt" -> + "text/plain"; + ".doc" -> + "application/msword"; + ".pdf" -> + "application/pdf"; + ".xls" -> + "application/vnd.ms-excel"; + ".rtf" -> + "application/rtf"; + ".mov" -> + "video/quicktime"; + ".mp3" -> + "audio/mpeg"; + ".z" -> + "application/x-compress"; + ".wav" -> + "audio/x-wav"; + ".ico" -> + "image/x-icon"; + ".bmp" -> + "image/bmp"; + ".m4a" -> + "audio/mpeg"; + ".m3u" -> + "audio/x-mpegurl"; + ".exe" -> + "application/octet-stream"; + ".csv" -> + "text/csv"; + _ -> + "text/plain" + end. + +%% @spec parse_header(string()) -> {Type, [{K, V}]} +%% @doc Parse a Content-Type like header, return the main Content-Type +%% and a property list of options. +parse_header(String) -> + %% TODO: This is exactly as broken as Python's cgi module. + %% Should parse properly like mochiweb_cookies. + [Type | Parts] = [string:strip(S) || S <- string:tokens(String, ";")], + F = fun (S, Acc) -> + case lists:splitwith(fun (C) -> C =/= $= end, S) of + {"", _} -> + %% Skip anything with no name + Acc; + {_, ""} -> + %% Skip anything with no value + Acc; + {Name, [$\= | Value]} -> + [{string:to_lower(string:strip(Name)), + unquote_header(string:strip(Value))} | Acc] + end + end, + {string:to_lower(Type), + lists:foldr(F, [], Parts)}. + +unquote_header("\"" ++ Rest) -> + unquote_header(Rest, []); +unquote_header(S) -> + S. + +unquote_header("", Acc) -> + lists:reverse(Acc); +unquote_header("\"", Acc) -> + lists:reverse(Acc); +unquote_header([$\\, C | Rest], Acc) -> + unquote_header(Rest, [C | Acc]); +unquote_header([C | Rest], Acc) -> + unquote_header(Rest, [C | Acc]). + +%% @spec record_to_proplist(Record, Fields) -> proplist() +%% @doc calls record_to_proplist/3 with a default TypeKey of '__record' +record_to_proplist(Record, Fields) -> + record_to_proplist(Record, Fields, '__record'). + +%% @spec record_to_proplist(Record, Fields, TypeKey) -> proplist() +%% @doc Return a proplist of the given Record with each field in the +%% Fields list set as a key with the corresponding value in the Record. +%% TypeKey is the key that is used to store the record type +%% Fields should be obtained by calling record_info(fields, record_type) +%% where record_type is the record type of Record +record_to_proplist(Record, Fields, TypeKey) + when is_tuple(Record), + is_list(Fields), + size(Record) - 1 =:= length(Fields) -> + lists:zip([TypeKey | Fields], tuple_to_list(Record)). + + +shell_quote([], Acc) -> + lists:reverse([$\" | Acc]); +shell_quote([C | Rest], Acc) when C =:= $\" orelse C =:= $\` orelse + C =:= $\\ orelse C =:= $\$ -> + shell_quote(Rest, [C, $\\ | Acc]); +shell_quote([C | Rest], Acc) -> + shell_quote(Rest, [C | Acc]). + +test() -> + test_join(), + test_quote_plus(), + test_unquote(), + test_urlencode(), + test_parse_qs(), + test_urlsplit_path(), + test_urlunsplit_path(), + test_urlsplit(), + test_urlunsplit(), + test_path_split(), + test_guess_mime(), + test_parse_header(), + test_shell_quote(), + test_cmd(), + test_cmd_string(), + test_partition(), + test_safe_relative_path(), + ok. + +test_shell_quote() -> + "\"foo \\$bar\\\"\\`' baz\"" = shell_quote("foo $bar\"`' baz"), + ok. + +test_cmd() -> + "$bling$ `word`!\n" = cmd(["echo", "$bling$ `word`!"]), + ok. + +test_cmd_string() -> + "\"echo\" \"\\$bling\\$ \\`word\\`!\"" = cmd_string(["echo", "$bling$ `word`!"]), + ok. + +test_parse_header() -> + {"multipart/form-data", [{"boundary", "AaB03x"}]} = + parse_header("multipart/form-data; boundary=AaB03x"), + ok. + +test_guess_mime() -> + "text/plain" = guess_mime(""), + "text/plain" = guess_mime(".text"), + "application/zip" = guess_mime(".zip"), + "application/zip" = guess_mime("x.zip"), + "text/html" = guess_mime("x.html"), + "application/xhtml+xml" = guess_mime("x.xhtml"), + ok. + +test_path_split() -> + {"", "foo/bar"} = path_split("/foo/bar"), + {"foo", "bar"} = path_split("foo/bar"), + {"bar", ""} = path_split("bar"), + ok. + +test_urlsplit() -> + {"", "", "/foo", "", "bar?baz"} = urlsplit("/foo#bar?baz"), + {"http", "host:port", "/foo", "", "bar?baz"} = + urlsplit("http://host:port/foo#bar?baz"), + ok. + +test_urlsplit_path() -> + {"/foo/bar", "", ""} = urlsplit_path("/foo/bar"), + {"/foo", "baz", ""} = urlsplit_path("/foo?baz"), + {"/foo", "", "bar?baz"} = urlsplit_path("/foo#bar?baz"), + {"/foo", "", "bar?baz#wibble"} = urlsplit_path("/foo#bar?baz#wibble"), + {"/foo", "bar", "baz"} = urlsplit_path("/foo?bar#baz"), + {"/foo", "bar?baz", "baz"} = urlsplit_path("/foo?bar?baz#baz"), + ok. + +test_urlunsplit() -> + "/foo#bar?baz" = urlunsplit({"", "", "/foo", "", "bar?baz"}), + "http://host:port/foo#bar?baz" = + urlunsplit({"http", "host:port", "/foo", "", "bar?baz"}), + ok. + +test_urlunsplit_path() -> + "/foo/bar" = urlunsplit_path({"/foo/bar", "", ""}), + "/foo?baz" = urlunsplit_path({"/foo", "baz", ""}), + "/foo#bar?baz" = urlunsplit_path({"/foo", "", "bar?baz"}), + "/foo#bar?baz#wibble" = urlunsplit_path({"/foo", "", "bar?baz#wibble"}), + "/foo?bar#baz" = urlunsplit_path({"/foo", "bar", "baz"}), + "/foo?bar?baz#baz" = urlunsplit_path({"/foo", "bar?baz", "baz"}), + ok. + +test_join() -> + "foo,bar,baz" = join(["foo", "bar", "baz"], $,), + "foo,bar,baz" = join(["foo", "bar", "baz"], ","), + "foo bar" = join([["foo", " bar"]], ","), + "foo bar,baz" = join([["foo", " bar"], "baz"], ","), + "foo" = join(["foo"], ","), + "foobarbaz" = join(["foo", "bar", "baz"], ""), + ok. + +test_quote_plus() -> + "foo" = quote_plus(foo), + "1" = quote_plus(1), + "1.1" = quote_plus(1.1), + "foo" = quote_plus("foo"), + "foo+bar" = quote_plus("foo bar"), + "foo%0A" = quote_plus("foo\n"), + "foo%0A" = quote_plus("foo\n"), + "foo%3B%26%3D" = quote_plus("foo;&="), + ok. + +test_unquote() -> + "foo bar" = unquote("foo+bar"), + "foo bar" = unquote("foo%20bar"), + "foo\r\n" = unquote("foo%0D%0A"), + ok. + +test_urlencode() -> + "foo=bar&baz=wibble+%0D%0A&z=1" = urlencode([{foo, "bar"}, + {"baz", "wibble \r\n"}, + {z, 1}]), + ok. + +test_parse_qs() -> + [{"foo", "bar"}, {"baz", "wibble \r\n"}, {"z", "1"}] = + parse_qs("foo=bar&baz=wibble+%0D%0A&z=1"), + ok. + +test_partition() -> + {"foo", "", ""} = partition("foo", "/"), + {"foo", "/", "bar"} = partition("foo/bar", "/"), + {"foo", "/", ""} = partition("foo/", "/"), + {"", "/", "bar"} = partition("/bar", "/"), + {"f", "oo/ba", "r"} = partition("foo/bar", "oo/ba"), + ok. + +test_safe_relative_path() -> + "foo" = safe_relative_path("foo"), + "foo/" = safe_relative_path("foo/"), + "foo" = safe_relative_path("foo/bar/.."), + "bar" = safe_relative_path("foo/../bar"), + "bar/" = safe_relative_path("foo/../bar/"), + "" = safe_relative_path("foo/.."), + "" = safe_relative_path("foo/../"), + undefined = safe_relative_path("/foo"), + undefined = safe_relative_path("../foo"), + undefined = safe_relative_path("foo/../.."), + undefined = safe_relative_path("foo//"), + ok. diff --git a/web/api/webmachine/deps/mochiweb/src/reloader.erl b/web/api/webmachine/deps/mochiweb/src/reloader.erl new file mode 100644 index 0000000..2ff154b --- /dev/null +++ b/web/api/webmachine/deps/mochiweb/src/reloader.erl @@ -0,0 +1,124 @@ +%% @copyright 2007 Mochi Media, Inc. +%% @author Matthew Dempsky +%% +%% @doc Erlang module for automatically reloading modified modules +%% during development. + +-module(reloader). +-author("Matthew Dempsky "). + +-include_lib("kernel/include/file.hrl"). + +-behaviour(gen_server). +-export([start/0, start_link/0]). +-export([stop/0]). +-export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]). + +-record(state, {last, tref}). + +%% External API + +%% @spec start() -> ServerRet +%% @doc Start the reloader. +start() -> + gen_server:start({local, ?MODULE}, ?MODULE, [], []). + +%% @spec start_link() -> ServerRet +%% @doc Start the reloader. +start_link() -> + gen_server:start_link({local, ?MODULE}, ?MODULE, [], []). + +%% @spec stop() -> ok +%% @doc Stop the reloader. +stop() -> + gen_server:call(?MODULE, stop). + +%% gen_server callbacks + +%% @spec init([]) -> {ok, State} +%% @doc gen_server init, opens the server in an initial state. +init([]) -> + {ok, TRef} = timer:send_interval(timer:seconds(1), doit), + {ok, #state{last = stamp(), tref = TRef}}. + +%% @spec handle_call(Args, From, State) -> tuple() +%% @doc gen_server callback. +handle_call(stop, _From, State) -> + {stop, shutdown, stopped, State}; +handle_call(_Req, _From, State) -> + {reply, {error, badrequest}, State}. + +%% @spec handle_cast(Cast, State) -> tuple() +%% @doc gen_server callback. +handle_cast(_Req, State) -> + {noreply, State}. + +%% @spec handle_info(Info, State) -> tuple() +%% @doc gen_server callback. +handle_info(doit, State) -> + Now = stamp(), + doit(State#state.last, Now), + {noreply, State#state{last = Now}}; +handle_info(_Info, State) -> + {noreply, State}. + +%% @spec terminate(Reason, State) -> ok +%% @doc gen_server termination callback. +terminate(_Reason, State) -> + {ok, cancel} = timer:cancel(State#state.tref), + ok. + + +%% @spec code_change(_OldVsn, State, _Extra) -> State +%% @doc gen_server code_change callback (trivial). +code_change(_Vsn, State, _Extra) -> + {ok, State}. + +%% Internal API + +doit(From, To) -> + [case file:read_file_info(Filename) of + {ok, FileInfo} when FileInfo#file_info.mtime >= From, + FileInfo#file_info.mtime < To -> + reload(Module); + {ok, _} -> + unmodified; + {error, enoent} -> + %% The Erlang compiler deletes existing .beam files if + %% recompiling fails. Maybe it's worth spitting out a + %% warning here, but I'd want to limit it to just once. + gone; + {error, Reason} -> + io:format("Error reading ~s's file info: ~p~n", + [Filename, Reason]), + error + end || {Module, Filename} <- code:all_loaded(), is_list(Filename)]. + +reload(Module) -> + io:format("Reloading ~p ...", [Module]), + code:purge(Module), + case code:load_file(Module) of + {module, Module} -> + io:format(" ok.~n"), + case erlang:function_exported(Module, test, 0) of + true -> + io:format(" - Calling ~p:test() ...", [Module]), + case catch Module:test() of + ok -> + io:format(" ok.~n"), + reload; + Reason -> + io:format(" fail: ~p.~n", [Reason]), + reload_but_test_failed + end; + false -> + reload + end; + {error, Reason} -> + io:format(" fail: ~p.~n", [Reason]), + error + end. + + +stamp() -> + erlang:localtime(). diff --git a/web/api/webmachine/deps/mochiweb/support/include.mk b/web/api/webmachine/deps/mochiweb/support/include.mk new file mode 100644 index 0000000..065e409 --- /dev/null +++ b/web/api/webmachine/deps/mochiweb/support/include.mk @@ -0,0 +1,39 @@ +## -*- makefile -*- + +###################################################################### +## Erlang + +ERL := erl +ERLC := $(ERL)c + +INCLUDE_DIRS := ../include $(wildcard ../deps/*/include) +EBIN_DIRS := $(wildcard ../deps/*/ebin) +ERLC_FLAGS := -W $(INCLUDE_DIRS:../%=-I ../%) $(EBIN_DIRS:%=-pa %) + +ifndef no_debug_info + ERLC_FLAGS += +debug_info +endif + +ifdef debug + ERLC_FLAGS += -Ddebug +endif + +EBIN_DIR := ../ebin +EMULATOR := beam + +ERL_SOURCES := $(wildcard *.erl) +ERL_HEADERS := $(wildcard *.hrl) $(wildcard ../include/*.hrl) +ERL_OBJECTS := $(ERL_SOURCES:%.erl=$(EBIN_DIR)/%.$(EMULATOR)) +ERL_OBJECTS_LOCAL := $(ERL_SOURCES:%.erl=./%.$(EMULATOR)) +APP_FILES := $(wildcard *.app) +EBIN_FILES = $(ERL_OBJECTS) $(APP_FILES:%.app=../ebin/%.app) +MODULES = $(ERL_SOURCES:%.erl=%) + +../ebin/%.app: %.app + cp $< $@ + +$(EBIN_DIR)/%.$(EMULATOR): %.erl + $(ERLC) $(ERLC_FLAGS) -o $(EBIN_DIR) $< + +./%.$(EMULATOR): %.erl + $(ERLC) $(ERLC_FLAGS) -o . $< diff --git a/web/api/webmachine/docs/http-headers-status-v3.png b/web/api/webmachine/docs/http-headers-status-v3.png new file mode 100644 index 0000000..2313160 Binary files /dev/null and b/web/api/webmachine/docs/http-headers-status-v3.png differ diff --git a/web/api/webmachine/ebin/.hg_empty_dir b/web/api/webmachine/ebin/.hg_empty_dir new file mode 100644 index 0000000..e69de29 diff --git a/web/api/webmachine/ebin/webmachine.app b/web/api/webmachine/ebin/webmachine.app new file mode 100644 index 0000000..e9749fc --- /dev/null +++ b/web/api/webmachine/ebin/webmachine.app @@ -0,0 +1,27 @@ +{application, webmachine, + [{description, "webmachine"}, + {vsn, "1.5"}, + {modules, [ + webmachine, + webmachine_app, + webmachine_decision_core, + webmachine_deps, + webmachine_dispatcher, + webmachine_error_handler, + webmachine_logger, + webmachine_perf_logger, + webmachine_resource, + webmachine_request, + webmachine_request_srv, + webmachine_skel, + webmachine_sup, + webmachine_mochiweb, + webmachine_multipart, + webmachine_util, + wrq, + wmtrace_resource + ]}, + {registered, []}, + {mod, {webmachine_app, []}}, + {env, []}, + {applications, [kernel, stdlib, crypto]}]}. diff --git a/web/api/webmachine/ebin/webmachine.beam b/web/api/webmachine/ebin/webmachine.beam new file mode 100644 index 0000000..6125504 Binary files /dev/null and b/web/api/webmachine/ebin/webmachine.beam differ diff --git a/web/api/webmachine/ebin/webmachine_app.beam b/web/api/webmachine/ebin/webmachine_app.beam new file mode 100644 index 0000000..d9189be Binary files /dev/null and b/web/api/webmachine/ebin/webmachine_app.beam differ diff --git a/web/api/webmachine/ebin/webmachine_decision_core.beam b/web/api/webmachine/ebin/webmachine_decision_core.beam new file mode 100644 index 0000000..6a594d5 Binary files /dev/null and b/web/api/webmachine/ebin/webmachine_decision_core.beam differ diff --git a/web/api/webmachine/ebin/webmachine_deps.beam b/web/api/webmachine/ebin/webmachine_deps.beam new file mode 100644 index 0000000..d7603bc Binary files /dev/null and b/web/api/webmachine/ebin/webmachine_deps.beam differ diff --git a/web/api/webmachine/ebin/webmachine_dispatcher.beam b/web/api/webmachine/ebin/webmachine_dispatcher.beam new file mode 100644 index 0000000..7301aa5 Binary files /dev/null and b/web/api/webmachine/ebin/webmachine_dispatcher.beam differ diff --git a/web/api/webmachine/ebin/webmachine_error_handler.beam b/web/api/webmachine/ebin/webmachine_error_handler.beam new file mode 100644 index 0000000..28354bf Binary files /dev/null and b/web/api/webmachine/ebin/webmachine_error_handler.beam differ diff --git a/web/api/webmachine/ebin/webmachine_logger.beam b/web/api/webmachine/ebin/webmachine_logger.beam new file mode 100644 index 0000000..f0cf132 Binary files /dev/null and b/web/api/webmachine/ebin/webmachine_logger.beam differ diff --git a/web/api/webmachine/ebin/webmachine_mochiweb.beam b/web/api/webmachine/ebin/webmachine_mochiweb.beam new file mode 100644 index 0000000..fa12c19 Binary files /dev/null and b/web/api/webmachine/ebin/webmachine_mochiweb.beam differ diff --git a/web/api/webmachine/ebin/webmachine_multipart.beam b/web/api/webmachine/ebin/webmachine_multipart.beam new file mode 100644 index 0000000..47c3aa9 Binary files /dev/null and b/web/api/webmachine/ebin/webmachine_multipart.beam differ diff --git a/web/api/webmachine/ebin/webmachine_perf_logger.beam b/web/api/webmachine/ebin/webmachine_perf_logger.beam new file mode 100644 index 0000000..23700b8 Binary files /dev/null and b/web/api/webmachine/ebin/webmachine_perf_logger.beam differ diff --git a/web/api/webmachine/ebin/webmachine_request.beam b/web/api/webmachine/ebin/webmachine_request.beam new file mode 100644 index 0000000..03d5c99 Binary files /dev/null and b/web/api/webmachine/ebin/webmachine_request.beam differ diff --git a/web/api/webmachine/ebin/webmachine_request_srv.beam b/web/api/webmachine/ebin/webmachine_request_srv.beam new file mode 100644 index 0000000..682fbc6 Binary files /dev/null and b/web/api/webmachine/ebin/webmachine_request_srv.beam differ diff --git a/web/api/webmachine/ebin/webmachine_resource.beam b/web/api/webmachine/ebin/webmachine_resource.beam new file mode 100644 index 0000000..2cb4b52 Binary files /dev/null and b/web/api/webmachine/ebin/webmachine_resource.beam differ diff --git a/web/api/webmachine/ebin/webmachine_skel.beam b/web/api/webmachine/ebin/webmachine_skel.beam new file mode 100644 index 0000000..68bb9fa Binary files /dev/null and b/web/api/webmachine/ebin/webmachine_skel.beam differ diff --git a/web/api/webmachine/ebin/webmachine_sup.beam b/web/api/webmachine/ebin/webmachine_sup.beam new file mode 100644 index 0000000..f57062a Binary files /dev/null and b/web/api/webmachine/ebin/webmachine_sup.beam differ diff --git a/web/api/webmachine/ebin/webmachine_util.beam b/web/api/webmachine/ebin/webmachine_util.beam new file mode 100644 index 0000000..94059b1 Binary files /dev/null and b/web/api/webmachine/ebin/webmachine_util.beam differ diff --git a/web/api/webmachine/ebin/wmtrace_resource.beam b/web/api/webmachine/ebin/wmtrace_resource.beam new file mode 100644 index 0000000..38e3dc8 Binary files /dev/null and b/web/api/webmachine/ebin/wmtrace_resource.beam differ diff --git a/web/api/webmachine/ebin/wrq.beam b/web/api/webmachine/ebin/wrq.beam new file mode 100644 index 0000000..f1cd9ed Binary files /dev/null and b/web/api/webmachine/ebin/wrq.beam differ diff --git a/web/api/webmachine/erl_crash.dump b/web/api/webmachine/erl_crash.dump new file mode 100644 index 0000000..15a6bab --- /dev/null +++ b/web/api/webmachine/erl_crash.dump @@ -0,0 +1,9735 @@ +=erl_crash_dump:0.1 +Tue Nov 24 20:29:08 2009 +Slogan: Kernel pid terminated (application_controller) ({application_start_failure,kernel,{shutdown,{kernel,start,[normal,[]]}}}) +System version: Erlang R13B02 (erts-5.7.3) [source] [smp:2:2] [rq:2] [async-threads:0] [hipe] [kernel-poll:true] +Compiled: Thu Nov 12 18:10:15 2009 +Atoms: 4062 +=memory +total: 2617936 +processes: 279780 +processes_used: 271004 +system: 2338156 +atom: 193585 +atom_used: 164667 +binary: 96232 +code: 1065384 +ets: 31892 +=hash_table:atom_tab +size: 3203 +used: 2293 +objs: 4062 +depth: 6 +=index_table:atom_tab +size: 4096 +limit: 1048576 +entries: 4062 +=hash_table:module_code +size: 47 +used: 29 +objs: 46 +depth: 4 +=index_table:module_code +size: 1024 +limit: 65536 +entries: 46 +=hash_table:export_list +size: 3203 +used: 1500 +objs: 2015 +depth: 4 +=index_table:export_list +size: 2048 +limit: 524288 +entries: 2015 +=hash_table:secondary_export_table +size: 97 +used: 0 +objs: 0 +depth: 0 +=hash_table:process_reg +size: 11 +used: 2 +objs: 3 +depth: 2 +=hash_table:fun_table +size: 197 +used: 138 +objs: 236 +depth: 5 +=hash_table:node_table +size: 11 +used: 1 +objs: 1 +depth: 1 +=hash_table:dist_table +size: 11 +used: 1 +objs: 1 +depth: 1 +=allocated_areas +sys_misc: 62362 +static: 499712 +atom_space: 65544 37058 +atom_table: 29277 +module_table: 4368 +export_table: 21088 +register_table: 108 +fun_table: 850 +module_refs: 1024 +loaded_code: 925730 +dist_table: 399 +node_table: 131 +bits_bufs_size: 0 +bif_timer: 40028 +link_lh: 0 +proc: 7872 1600 +atom_entry: 98764 98332 +export_entry: 97392 97152 +module_entry: 2440 1880 +reg_proc: 512 104 +monitor_sh: 832 32 +nlink_sh: 1480 184 +fun_entry: 13516 13292 +db_tab: 5952 624 +driver_event_data_state: 28 28 +driver_select_data_state: 352 32 +=allocator:sys_alloc +option e: true +option m: libc +option tt: 131072 +option tp: 0 +=allocator:temp_alloc[0] +versions: 2.1 2.2 +option e: true +option t: false +option ramv: false +option sbct: 524288 +option asbcst: 4145152 +option rsbcst: 90 +option rsbcmt: 80 +option rmbcmt: 100 +option mmbcs: 131072 +option mmsbc: 256 +option mmmbc: 10 +option lmbcs: 10485760 +option smbcs: 1048576 +option mbcgs: 10 +option mbsd: 3 +option as: gf +mbcs blocks: 0 145 145 +mbcs blocks size: 0 25088 25088 +mbcs carriers: 1 1 1 +mbcs mseg carriers: 0 +mbcs sys_alloc carriers: 1 +mbcs carriers size: 131104 131104 131104 +mbcs mseg carriers size: 0 +mbcs sys_alloc carriers size: 131104 +sbcs blocks: 0 0 0 +sbcs blocks size: 0 0 0 +sbcs carriers: 0 0 0 +sbcs mseg carriers: 0 +sbcs sys_alloc carriers: 0 +sbcs carriers size: 0 0 0 +sbcs mseg carriers size: 0 +sbcs sys_alloc carriers size: 0 +temp_alloc calls: 1170 +temp_free calls: 1170 +temp_realloc calls: 2 +mseg_alloc calls: 0 +mseg_dealloc calls: 0 +mseg_realloc calls: 0 +sys_alloc calls: 1 +sys_free calls: 0 +sys_realloc calls: 0 +=allocator:temp_alloc[1] +versions: 0.9 2.2 +option e: true +option t: 3 +option ramv: false +option sbct: 524288 +option asbcst: 4145152 +option rsbcst: 90 +option rsbcmt: 80 +option rmbcmt: 100 +option mmbcs: 131072 +option mmsbc: 256 +option mmmbc: 10 +option lmbcs: 10485760 +option smbcs: 1048576 +option mbcgs: 10 +option as: af +mbcs blocks: 0 256 256 +mbcs blocks size: 0 29760 29760 +mbcs carriers: 1 1 1 +mbcs mseg carriers: 0 +mbcs sys_alloc carriers: 1 +mbcs carriers size: 131104 131104 131104 +mbcs mseg carriers size: 0 +mbcs sys_alloc carriers size: 131104 +sbcs blocks: 0 0 0 +sbcs blocks size: 0 0 0 +sbcs carriers: 0 0 0 +sbcs mseg carriers: 0 +sbcs sys_alloc carriers: 0 +sbcs carriers size: 0 0 0 +sbcs mseg carriers size: 0 +sbcs sys_alloc carriers size: 0 +temp_alloc calls: 5241 +temp_free calls: 5241 +temp_realloc calls: 3 +mseg_alloc calls: 0 +mseg_dealloc calls: 0 +mseg_realloc calls: 0 +sys_alloc calls: 1 +sys_free calls: 0 +sys_realloc calls: 0 +=allocator:temp_alloc[2] +versions: 0.9 2.2 +option e: true +option t: 3 +option ramv: false +option sbct: 524288 +option asbcst: 4145152 +option rsbcst: 90 +option rsbcmt: 80 +option rmbcmt: 100 +option mmbcs: 131072 +option mmsbc: 256 +option mmmbc: 10 +option lmbcs: 10485760 +option smbcs: 1048576 +option mbcgs: 10 +option as: af +mbcs blocks: 0 1 1 +mbcs blocks size: 0 65544 65544 +mbcs carriers: 1 1 1 +mbcs mseg carriers: 0 +mbcs sys_alloc carriers: 1 +mbcs carriers size: 131104 131104 131104 +mbcs mseg carriers size: 0 +mbcs sys_alloc carriers size: 131104 +sbcs blocks: 0 0 0 +sbcs blocks size: 0 0 0 +sbcs carriers: 0 0 0 +sbcs mseg carriers: 0 +sbcs sys_alloc carriers: 0 +sbcs carriers size: 0 0 0 +sbcs mseg carriers size: 0 +sbcs sys_alloc carriers size: 0 +temp_alloc calls: 1 +temp_free calls: 1 +temp_realloc calls: 0 +mseg_alloc calls: 0 +mseg_dealloc calls: 0 +mseg_realloc calls: 0 +sys_alloc calls: 1 +sys_free calls: 0 +sys_realloc calls: 0 +=allocator:sl_alloc[1] +versions: 2.1 2.2 +option e: true +option t: 2 +option ramv: false +option sbct: 524288 +option asbcst: 4145152 +option rsbcst: 80 +option rsbcmt: 80 +option rmbcmt: 50 +option mmbcs: 65536 +option mmsbc: 256 +option mmmbc: 5 +option lmbcs: 5242880 +option smbcs: 524288 +option mbcgs: 10 +option mbsd: 3 +option as: gf +mbcs blocks: 0 1 1 +mbcs blocks size: 0 48 48 +mbcs carriers: 1 1 1 +mbcs mseg carriers: 0 +mbcs sys_alloc carriers: 1 +mbcs carriers size: 65560 65560 65560 +mbcs mseg carriers size: 0 +mbcs sys_alloc carriers size: 65560 +sbcs blocks: 0 0 0 +sbcs blocks size: 0 0 0 +sbcs carriers: 0 0 0 +sbcs mseg carriers: 0 +sbcs sys_alloc carriers: 0 +sbcs carriers size: 0 0 0 +sbcs mseg carriers size: 0 +sbcs sys_alloc carriers size: 0 +sl_alloc calls: 205 +sl_free calls: 205 +sl_realloc calls: 0 +mseg_alloc calls: 0 +mseg_dealloc calls: 0 +mseg_realloc calls: 0 +sys_alloc calls: 1 +sys_free calls: 0 +sys_realloc calls: 0 +=allocator:sl_alloc[2] +versions: 2.1 2.2 +option e: true +option t: 2 +option ramv: false +option sbct: 524288 +option asbcst: 4145152 +option rsbcst: 80 +option rsbcmt: 80 +option rmbcmt: 50 +option mmbcs: 65536 +option mmsbc: 256 +option mmmbc: 5 +option lmbcs: 5242880 +option smbcs: 524288 +option mbcgs: 10 +option mbsd: 3 +option as: gf +mbcs blocks: 0 0 0 +mbcs blocks size: 0 0 0 +mbcs carriers: 1 1 1 +mbcs mseg carriers: 0 +mbcs sys_alloc carriers: 1 +mbcs carriers size: 65560 65560 65560 +mbcs mseg carriers size: 0 +mbcs sys_alloc carriers size: 65560 +sbcs blocks: 0 0 0 +sbcs blocks size: 0 0 0 +sbcs carriers: 0 0 0 +sbcs mseg carriers: 0 +sbcs sys_alloc carriers: 0 +sbcs carriers size: 0 0 0 +sbcs mseg carriers size: 0 +sbcs sys_alloc carriers size: 0 +sl_alloc calls: 0 +sl_free calls: 0 +sl_realloc calls: 0 +mseg_alloc calls: 0 +mseg_dealloc calls: 0 +mseg_realloc calls: 0 +sys_alloc calls: 1 +sys_free calls: 0 +sys_realloc calls: 0 +=allocator:std_alloc[1] +versions: 0.9 2.2 +option e: true +option t: 2 +option ramv: false +option sbct: 524288 +option asbcst: 4145152 +option rsbcst: 20 +option rsbcmt: 80 +option rmbcmt: 50 +option mmbcs: 65536 +option mmsbc: 256 +option mmmbc: 5 +option lmbcs: 5242880 +option smbcs: 524288 +option mbcgs: 10 +option as: bf +mbcs blocks: 213 216 216 +mbcs blocks size: 7744 17720 17720 +mbcs carriers: 1 1 1 +mbcs mseg carriers: 0 +mbcs sys_alloc carriers: 1 +mbcs carriers size: 65560 65560 65560 +mbcs mseg carriers size: 0 +mbcs sys_alloc carriers size: 65560 +sbcs blocks: 0 0 0 +sbcs blocks size: 0 0 0 +sbcs carriers: 0 0 0 +sbcs mseg carriers: 0 +sbcs sys_alloc carriers: 0 +sbcs carriers size: 0 0 0 +sbcs mseg carriers size: 0 +sbcs sys_alloc carriers size: 0 +std_alloc calls: 470 +std_free calls: 257 +std_realloc calls: 0 +mseg_alloc calls: 0 +mseg_dealloc calls: 0 +mseg_realloc calls: 0 +sys_alloc calls: 1 +sys_free calls: 0 +sys_realloc calls: 0 +=allocator:std_alloc[2] +versions: 0.9 2.2 +option e: true +option t: 2 +option ramv: false +option sbct: 524288 +option asbcst: 4145152 +option rsbcst: 20 +option rsbcmt: 80 +option rmbcmt: 50 +option mmbcs: 65536 +option mmsbc: 256 +option mmmbc: 5 +option lmbcs: 5242880 +option smbcs: 524288 +option mbcgs: 10 +option as: bf +mbcs blocks: 17 20 20 +mbcs blocks size: 2360 12048 12048 +mbcs carriers: 1 1 1 +mbcs mseg carriers: 0 +mbcs sys_alloc carriers: 1 +mbcs carriers size: 65560 65560 65560 +mbcs mseg carriers size: 0 +mbcs sys_alloc carriers size: 65560 +sbcs blocks: 0 0 0 +sbcs blocks size: 0 0 0 +sbcs carriers: 0 0 0 +sbcs mseg carriers: 0 +sbcs sys_alloc carriers: 0 +sbcs carriers size: 0 0 0 +sbcs mseg carriers size: 0 +sbcs sys_alloc carriers size: 0 +std_alloc calls: 29 +std_free calls: 12 +std_realloc calls: 0 +mseg_alloc calls: 0 +mseg_dealloc calls: 0 +mseg_realloc calls: 0 +sys_alloc calls: 1 +sys_free calls: 0 +sys_realloc calls: 0 +=allocator:ll_alloc +versions: 0.9 2.2 +option e: true +option t: false +option ramv: false +option sbct: 4294967295 +option asbcst: 0 +option rsbcst: 0 +option rsbcmt: 0 +option rmbcmt: 0 +option mmbcs: 2097152 +option mmsbc: 0 +option mmmbc: 0 +option lmbcs: 10485760 +option smbcs: 1048576 +option mbcgs: 10 +option as: aobf +mbcs blocks: 438 439 439 +mbcs blocks size: 2406768 2477616 2477616 +mbcs carriers: 2 2 2 +mbcs mseg carriers: 0 +mbcs sys_alloc carriers: 2 +mbcs carriers size: 3145760 3145760 3145760 +mbcs mseg carriers size: 0 +mbcs sys_alloc carriers size: 3145760 +sbcs blocks: 0 0 0 +sbcs blocks size: 0 0 0 +sbcs carriers: 0 0 0 +sbcs mseg carriers: 0 +sbcs sys_alloc carriers: 0 +sbcs carriers size: 0 0 0 +sbcs mseg carriers size: 0 +sbcs sys_alloc carriers size: 0 +ll_alloc calls: 439 +ll_free calls: 1 +ll_realloc calls: 106 +mseg_alloc calls: 0 +mseg_dealloc calls: 0 +mseg_realloc calls: 0 +sys_alloc calls: 2 +sys_free calls: 0 +sys_realloc calls: 0 +=allocator:eheap_alloc[1] +versions: 2.1 2.2 +option e: true +option t: 2 +option ramv: false +option sbct: 524288 +option asbcst: 4145152 +option rsbcst: 50 +option rsbcmt: 80 +option rmbcmt: 50 +option mmbcs: 262144 +option mmsbc: 256 +option mmmbc: 100 +option lmbcs: 5242880 +option smbcs: 524288 +option mbcgs: 10 +option mbsd: 3 +option as: gf +mbcs blocks: 7 33 33 +mbcs blocks size: 97984 233064 233064 +mbcs carriers: 2 2 2 +mbcs mseg carriers: 1 +mbcs sys_alloc carriers: 1 +mbcs carriers size: 786456 786456 786456 +mbcs mseg carriers size: 524288 +mbcs sys_alloc carriers size: 262168 +sbcs blocks: 0 0 0 +sbcs blocks size: 0 0 0 +sbcs carriers: 0 0 0 +sbcs mseg carriers: 0 +sbcs sys_alloc carriers: 0 +sbcs carriers size: 0 0 0 +sbcs mseg carriers size: 0 +sbcs sys_alloc carriers size: 0 +eheap_alloc calls: 231 +eheap_free calls: 224 +eheap_realloc calls: 20 +mseg_alloc calls: 1 +mseg_dealloc calls: 0 +mseg_realloc calls: 0 +sys_alloc calls: 1 +sys_free calls: 0 +sys_realloc calls: 0 +=allocator:eheap_alloc[2] +versions: 2.1 2.2 +option e: true +option t: 2 +option ramv: false +option sbct: 524288 +option asbcst: 4145152 +option rsbcst: 50 +option rsbcmt: 80 +option rmbcmt: 50 +option mmbcs: 262144 +option mmsbc: 256 +option mmmbc: 100 +option lmbcs: 5242880 +option smbcs: 524288 +option mbcgs: 10 +option mbsd: 3 +option as: gf +mbcs blocks: 0 26 26 +mbcs blocks size: 0 2504 2504 +mbcs carriers: 1 1 1 +mbcs mseg carriers: 0 +mbcs sys_alloc carriers: 1 +mbcs carriers size: 262168 262168 262168 +mbcs mseg carriers size: 0 +mbcs sys_alloc carriers size: 262168 +sbcs blocks: 0 0 0 +sbcs blocks size: 0 0 0 +sbcs carriers: 0 0 0 +sbcs mseg carriers: 0 +sbcs sys_alloc carriers: 0 +sbcs carriers size: 0 0 0 +sbcs mseg carriers size: 0 +sbcs sys_alloc carriers size: 0 +eheap_alloc calls: 26 +eheap_free calls: 26 +eheap_realloc calls: 0 +mseg_alloc calls: 0 +mseg_dealloc calls: 0 +mseg_realloc calls: 0 +sys_alloc calls: 1 +sys_free calls: 0 +sys_realloc calls: 0 +=allocator:ets_alloc[1] +versions: 0.9 2.2 +option e: true +option t: 2 +option ramv: false +option sbct: 524288 +option asbcst: 4145152 +option rsbcst: 20 +option rsbcmt: 80 +option rmbcmt: 50 +option mmbcs: 65536 +option mmsbc: 256 +option mmmbc: 100 +option lmbcs: 5242880 +option smbcs: 524288 +option mbcgs: 10 +option as: bf +mbcs blocks: 0 83 83 +mbcs blocks size: 0 22384 22384 +mbcs carriers: 1 1 1 +mbcs mseg carriers: 0 +mbcs sys_alloc carriers: 1 +mbcs carriers size: 65560 65560 65560 +mbcs mseg carriers size: 0 +mbcs sys_alloc carriers size: 65560 +sbcs blocks: 0 0 0 +sbcs blocks size: 0 0 0 +sbcs carriers: 0 0 0 +sbcs mseg carriers: 0 +sbcs sys_alloc carriers: 0 +sbcs carriers size: 0 0 0 +sbcs mseg carriers size: 0 +sbcs sys_alloc carriers size: 0 +ets_alloc calls: 86 +ets_free calls: 86 +ets_realloc calls: 5 +mseg_alloc calls: 0 +mseg_dealloc calls: 0 +mseg_realloc calls: 0 +sys_alloc calls: 1 +sys_free calls: 0 +sys_realloc calls: 0 +=allocator:ets_alloc[2] +versions: 0.9 2.2 +option e: true +option t: 2 +option ramv: false +option sbct: 524288 +option asbcst: 4145152 +option rsbcst: 20 +option rsbcmt: 80 +option rmbcmt: 50 +option mmbcs: 65536 +option mmsbc: 256 +option mmmbc: 100 +option lmbcs: 5242880 +option smbcs: 524288 +option mbcgs: 10 +option as: bf +mbcs blocks: 4 4 4 +mbcs blocks size: 6704 6704 6704 +mbcs carriers: 1 1 1 +mbcs mseg carriers: 0 +mbcs sys_alloc carriers: 1 +mbcs carriers size: 65560 65560 65560 +mbcs mseg carriers size: 0 +mbcs sys_alloc carriers size: 65560 +sbcs blocks: 0 0 0 +sbcs blocks size: 0 0 0 +sbcs carriers: 0 0 0 +sbcs mseg carriers: 0 +sbcs sys_alloc carriers: 0 +sbcs carriers size: 0 0 0 +sbcs mseg carriers size: 0 +sbcs sys_alloc carriers size: 0 +ets_alloc calls: 4 +ets_free calls: 0 +ets_realloc calls: 0 +mseg_alloc calls: 0 +mseg_dealloc calls: 0 +mseg_realloc calls: 0 +sys_alloc calls: 1 +sys_free calls: 0 +sys_realloc calls: 0 +=allocator:fix_alloc +option e: true +proc: 7872 1600 +atom_entry: 98764 98332 +export_entry: 97392 97152 +module_entry: 2440 1880 +reg_proc: 512 104 +monitor_sh: 832 32 +nlink_sh: 1480 184 +fun_entry: 13516 13292 +db_tab: 5952 624 +driver_event_data_state: 28 28 +driver_select_data_state: 352 32 +=allocator:binary_alloc[1] +versions: 0.9 2.2 +option e: true +option t: 2 +option ramv: false +option sbct: 524288 +option asbcst: 4145152 +option rsbcst: 20 +option rsbcmt: 80 +option rmbcmt: 50 +option mmbcs: 65536 +option mmsbc: 256 +option mmmbc: 50 +option lmbcs: 5242880 +option smbcs: 524288 +option mbcgs: 10 +option as: bf +mbcs blocks: 5 10 10 +mbcs blocks size: 96232 200984 200984 +mbcs carriers: 2 2 2 +mbcs mseg carriers: 1 +mbcs sys_alloc carriers: 1 +mbcs carriers size: 589848 589848 589848 +mbcs mseg carriers size: 524288 +mbcs sys_alloc carriers size: 65560 +sbcs blocks: 0 0 0 +sbcs blocks size: 0 0 0 +sbcs carriers: 0 0 0 +sbcs mseg carriers: 0 +sbcs sys_alloc carriers: 0 +sbcs carriers size: 0 0 0 +sbcs mseg carriers size: 0 +sbcs sys_alloc carriers size: 0 +binary_alloc calls: 297 +binary_free calls: 292 +binary_realloc calls: 0 +mseg_alloc calls: 5 +mseg_dealloc calls: 4 +mseg_realloc calls: 0 +sys_alloc calls: 1 +sys_free calls: 0 +sys_realloc calls: 0 +=allocator:binary_alloc[2] +versions: 0.9 2.2 +option e: true +option t: 2 +option ramv: false +option sbct: 524288 +option asbcst: 4145152 +option rsbcst: 20 +option rsbcmt: 80 +option rmbcmt: 50 +option mmbcs: 65536 +option mmsbc: 256 +option mmmbc: 50 +option lmbcs: 5242880 +option smbcs: 524288 +option mbcgs: 10 +option as: bf +mbcs blocks: 0 1 1 +mbcs blocks size: 0 56 56 +mbcs carriers: 1 1 1 +mbcs mseg carriers: 0 +mbcs sys_alloc carriers: 1 +mbcs carriers size: 65560 65560 65560 +mbcs mseg carriers size: 0 +mbcs sys_alloc carriers size: 65560 +sbcs blocks: 0 0 0 +sbcs blocks size: 0 0 0 +sbcs carriers: 0 0 0 +sbcs mseg carriers: 0 +sbcs sys_alloc carriers: 0 +sbcs carriers size: 0 0 0 +sbcs mseg carriers size: 0 +sbcs sys_alloc carriers size: 0 +binary_alloc calls: 2 +binary_free calls: 2 +binary_realloc calls: 0 +mseg_alloc calls: 0 +mseg_dealloc calls: 0 +mseg_realloc calls: 0 +sys_alloc calls: 1 +sys_free calls: 0 +sys_realloc calls: 0 +=allocator:driver_alloc[1] +versions: 0.9 2.2 +option e: true +option t: 2 +option ramv: false +option sbct: 524288 +option asbcst: 4145152 +option rsbcst: 20 +option rsbcmt: 80 +option rmbcmt: 50 +option mmbcs: 65536 +option mmsbc: 256 +option mmmbc: 10 +option lmbcs: 5242880 +option smbcs: 524288 +option mbcgs: 10 +option as: bf +mbcs blocks: 1 4 4 +mbcs blocks size: 96 8648 8648 +mbcs carriers: 1 1 1 +mbcs mseg carriers: 0 +mbcs sys_alloc carriers: 1 +mbcs carriers size: 65560 65560 65560 +mbcs mseg carriers size: 0 +mbcs sys_alloc carriers size: 65560 +sbcs blocks: 0 0 0 +sbcs blocks size: 0 0 0 +sbcs carriers: 0 0 0 +sbcs mseg carriers: 0 +sbcs sys_alloc carriers: 0 +sbcs carriers size: 0 0 0 +sbcs mseg carriers size: 0 +sbcs sys_alloc carriers size: 0 +driver_alloc calls: 441 +driver_free calls: 440 +driver_realloc calls: 0 +mseg_alloc calls: 0 +mseg_dealloc calls: 0 +mseg_realloc calls: 0 +sys_alloc calls: 1 +sys_free calls: 0 +sys_realloc calls: 0 +=allocator:driver_alloc[2] +versions: 0.9 2.2 +option e: true +option t: 2 +option ramv: false +option sbct: 524288 +option asbcst: 4145152 +option rsbcst: 20 +option rsbcmt: 80 +option rmbcmt: 50 +option mmbcs: 65536 +option mmsbc: 256 +option mmmbc: 10 +option lmbcs: 5242880 +option smbcs: 524288 +option mbcgs: 10 +option as: bf +mbcs blocks: 1 1 1 +mbcs blocks size: 48 48 48 +mbcs carriers: 1 1 1 +mbcs mseg carriers: 0 +mbcs sys_alloc carriers: 1 +mbcs carriers size: 65560 65560 65560 +mbcs mseg carriers size: 0 +mbcs sys_alloc carriers size: 65560 +sbcs blocks: 0 0 0 +sbcs blocks size: 0 0 0 +sbcs carriers: 0 0 0 +sbcs mseg carriers: 0 +sbcs sys_alloc carriers: 0 +sbcs carriers size: 0 0 0 +sbcs mseg carriers size: 0 +sbcs sys_alloc carriers size: 0 +driver_alloc calls: 1 +driver_free calls: 0 +driver_realloc calls: 0 +mseg_alloc calls: 0 +mseg_dealloc calls: 0 +mseg_realloc calls: 0 +sys_alloc calls: 1 +sys_free calls: 0 +sys_realloc calls: 0 +=allocator:mseg_alloc +version: 0.9 +option amcbf: 4194304 +option rmcbf: 20 +option mcs: 5 +option cci: 1000 +cached_segments: 0 +cache_hits: 4 +segments: 2 2 2 +segments_size: 1048576 1048576 1048576 +segments_watermark: 2 +mseg_alloc calls: 6 +mseg_dealloc calls: 4 +mseg_realloc calls: 0 +mseg_create calls: 2 +mseg_destroy calls: 0 +mseg_recreate calls: 0 +mseg_clear_cache calls: 0 +mseg_check_cache calls: 0 +=allocator:alloc_util +option mmc: 1024 +option ycs: 1048576 +=allocator:instr +option m: false +option s: false +option t: false +=proc:<0.0.0> +State: Running +Name: init +Spawned as: otp_ring0:start/2 +Spawned by: [] +Started: Tue Nov 24 20:29:07 2009 +Message queue length: 1 +Number of heap fragments: 0 +Heap fragment data: 0 +Link list: [<0.4.0>, <0.2.0>] +Reductions: 3721 +Stack+heap: 6765 +OldHeap: 10946 +Heap unused: 2403 +OldHeap unused: 10946 +Program counter: 0xb7ca47e4 (init:sleep/1 + 32) +CP: 0x00000000 (invalid) +=proc:<0.2.0> +State: Waiting +Name: erl_prim_loader +Spawned as: erlang:apply/2 +Spawned by: <0.1.0> +Started: Tue Nov 24 20:29:07 2009 +Message queue length: 0 +Number of heap fragments: 0 +Heap fragment data: 0 +Link list: [<0.0.0>, #Port<0.1>] +Reductions: 55242 +Stack+heap: 987 +OldHeap: 4181 +Heap unused: 458 +OldHeap unused: 4181 +Program counter: 0xb7cd2700 (erl_prim_loader:loop/3 + 92) +CP: 0x00000000 (invalid) +arity = 0 +=proc:<0.4.0> +State: Waiting +Name: error_logger +Spawned as: proc_lib:init_p/5 +Spawned by: <0.1.0> +Started: Tue Nov 24 20:29:07 2009 +Message queue length: 0 +Number of heap fragments: 0 +Heap fragment data: 0 +Link list: [<0.0.0>] +Reductions: 229 +Stack+heap: 987 +OldHeap: 377 +Heap unused: 429 +OldHeap unused: 377 +Program counter: 0xb7ccb7f0 (gen_event:fetch_msg/5 + 44) +CP: 0x00000000 (invalid) +arity = 0 +=proc:<0.12.0> +State: Waiting +Spawned as: erlang:apply/2 +Spawned by: <0.11.0> +Started: Tue Nov 24 20:29:07 2009 +Message queue length: 0 +Number of heap fragments: 0 +Heap fragment data: 0 +Reductions: 25 +Stack+heap: 233 +OldHeap: 0 +Heap unused: 159 +OldHeap unused: 0 +Program counter: 0xb7d3e998 (global:loop_the_locker/1 + 588) +CP: 0x00000000 (invalid) +arity = 0 +=port:#Port<0.1> +Slot: 1 +Connected: <0.2.0> +Links: <0.2.0> +Port controls linked-in driver: efile +=node:'nonode@nohost' +=no_distribution +=loaded_modules +Current code: 925730 +Old code: 0 +=mod:otp_ring0 +Current size: 620 +=mod:init +Current size: 34148 +=mod:prim_inet +Current size: 49412 +=mod:prim_file +Current size: 22153 +=mod:zlib +Current size: 6948 +=mod:prim_zip +Current size: 14200 +=mod:erl_prim_loader +Current size: 39956 +=mod:erlang +Current size: 17368 +=mod:error_handler +Current size: 3181 +Current attributes: 836C00000001680264000376736E6C000000016E10001EF237E173394B9A9695D5F1C40CB9386A6A +Current compilation info: 836C0000000468026400076F7074696F6E736C0000000468026400036377646B005E2F6E65742F6973696C6475722F6C6469736B2F6461696C795F6275696C642F6F74705F7072656275696C645F7231336230322E323030392D30392D32315F31312F6F74705F7372635F5231334230322F6C69622F6B65726E656C2F73726368026400066F75746469726B00662F6E65742F6973696C6475722F6C6469736B2F6461696C795F6275696C642F6F74705F7072656275696C645F7231336230322E323030392D30392D32315F31312F6F74705F7372635F5231334230322F6C69622F6B65726E656C2F7372632F2E2E2F6562696E6802640001696B00692F6E65742F6973696C6475722F6C6469736B2F6461696C795F6275696C642F6F74705F7072656275696C645F7231336230322E323030392D30392D32315F31312F6F74705F7372635F5231334230322F6C69622F6B65726E656C2F7372632F2E2E2F696E636C75646564000A64656275675F696E666F6A680264000776657273696F6E6B0005342E362E33680264000474696D65680662000007D961096115610A612F61116802640006736F757263656B00702F6E65742F6973696C6475722F6C6469736B2F6461696C795F6275696C642F6F74705F7072656275696C645F7231336230322E323030392D30392D32315F31312F6F74705F7372635F5231334230322F6C69622F6B65726E656C2F7372632F6572726F725F68616E646C65722E65726C6A +=mod:heart +Current size: 6781 +Current attributes: 836C00000001680264000376736E6C000000016E1000F5A6A244BEEFD33093DB3EF2F61C49366A6A +Current compilation info: 836C0000000468026400076F7074696F6E736C0000000468026400036377646B005E2F6E65742F6973696C6475722F6C6469736B2F6461696C795F6275696C642F6F74705F7072656275696C645F7231336230322E323030392D30392D32315F31312F6F74705F7372635F5231334230322F6C69622F6B65726E656C2F73726368026400066F75746469726B00662F6E65742F6973696C6475722F6C6469736B2F6461696C795F6275696C642F6F74705F7072656275696C645F7231336230322E323030392D30392D32315F31312F6F74705F7372635F5231334230322F6C69622F6B65726E656C2F7372632F2E2E2F6562696E6802640001696B00692F6E65742F6973696C6475722F6C6469736B2F6461696C795F6275696C642F6F74705F7072656275696C645F7231336230322E323030392D30392D32315F31312F6F74705F7372635F5231334230322F6C69622F6B65726E656C2F7372632F2E2E2F696E636C75646564000A64656275675F696E666F6A680264000776657273696F6E6B0005342E362E33680264000474696D65680662000007D961096115610A612F61146802640006736F757263656B00682F6E65742F6973696C6475722F6C6469736B2F6461696C795F6275696C642F6F74705F7072656275696C645F7231336230322E323030392D30392D32315F31312F6F74705F7372635F5231334230322F6C69622F6B65726E656C2F7372632F68656172742E65726C6A +=mod:error_logger +Current size: 6696 +Current attributes: 836C00000001680264000376736E6C000000016E10009A2DC18545547E5BD5608453264F37396A6A +Current compilation info: 836C0000000468026400076F7074696F6E736C0000000468026400036377646B005E2F6E65742F6973696C6475722F6C6469736B2F6461696C795F6275696C642F6F74705F7072656275696C645F7231336230322E323030392D30392D32315F31312F6F74705F7372635F5231334230322F6C69622F6B65726E656C2F73726368026400066F75746469726B00662F6E65742F6973696C6475722F6C6469736B2F6461696C795F6275696C642F6F74705F7072656275696C645F7231336230322E323030392D30392D32315F31312F6F74705F7372635F5231334230322F6C69622F6B65726E656C2F7372632F2E2E2F6562696E6802640001696B00692F6E65742F6973696C6475722F6C6469736B2F6461696C795F6275696C642F6F74705F7072656275696C645F7231336230322E323030392D30392D32315F31312F6F74705F7372635F5231334230322F6C69622F6B65726E656C2F7372632F2E2E2F696E636C75646564000A64656275675F696E666F6A680264000776657273696F6E6B0005342E362E33680264000474696D65680662000007D961096115610A612F61116802640006736F757263656B006F2F6E65742F6973696C6475722F6C6469736B2F6461696C795F6275696C642F6F74705F7072656275696C645F7231336230322E323030392D30392D32315F31312F6F74705F7372635F5231334230322F6C69622F6B65726E656C2F7372632F6572726F725F6C6F676765722E65726C6A +=mod:gen_event +Current size: 23709 +Current attributes: 836C00000001680264000376736E6C000000016E10008B637AE6083D8167590CC8F0538FA9646A6A +Current compilation info: 836C0000000468026400076F7074696F6E736C0000000568026400036377646B005E2F6E65742F6973696C6475722F6C6469736B2F6461696C795F6275696C642F6F74705F7072656275696C645F7231336230322E323030392D30392D32315F31312F6F74705F7372635F5231334230322F6C69622F7374646C69622F73726368026400066F75746469726B00662F6E65742F6973696C6475722F6C6469736B2F6461696C795F6275696C642F6F74705F7072656275696C645F7231336230322E323030392D30392D32315F31312F6F74705F7372635F5231334230322F6C69622F7374646C69622F7372632F2E2E2F6562696E6802640001696B00692F6E65742F6973696C6475722F6C6469736B2F6461696C795F6275696C642F6F74705F7072656275696C645F7231336230322E323030392D30392D32315F31312F6F74705F7372635F5231334230322F6C69622F7374646C69622F7372632F2E2E2F696E636C7564656802640001696B00732F6E65742F6973696C6475722F6C6469736B2F6461696C795F6275696C642F6F74705F7072656275696C645F7231336230322E323030392D30392D32315F31312F6F74705F7372635F5231334230322F6C69622F7374646C69622F7372632F2E2E2F2E2E2F6B65726E656C2F696E636C75646564000A64656275675F696E666F6A680264000776657273696F6E6B0005342E362E33680264000474696D65680662000007D961096115610A612F61026802640006736F757263656B006C2F6E65742F6973696C6475722F6C6469736B2F6461696C795F6275696C642F6F74705F7072656275696C645F7231336230322E323030392D30392D32315F31312F6F74705F7372635F5231334230322F6C69622F7374646C69622F7372632F67656E5F6576656E742E65726C6A +=mod:gen +Current size: 6267 +Current attributes: 836C00000001680264000376736E6C000000016E1000DB1E9EA3C1B540E2AA5EF6C90DCEC4C56A6A +Current compilation info: 836C0000000468026400076F7074696F6E736C0000000568026400036377646B005E2F6E65742F6973696C6475722F6C6469736B2F6461696C795F6275696C642F6F74705F7072656275696C645F7231336230322E323030392D30392D32315F31312F6F74705F7372635F5231334230322F6C69622F7374646C69622F73726368026400066F75746469726B00662F6E65742F6973696C6475722F6C6469736B2F6461696C795F6275696C642F6F74705F7072656275696C645F7231336230322E323030392D30392D32315F31312F6F74705F7372635F5231334230322F6C69622F7374646C69622F7372632F2E2E2F6562696E6802640001696B00692F6E65742F6973696C6475722F6C6469736B2F6461696C795F6275696C642F6F74705F7072656275696C645F7231336230322E323030392D30392D32315F31312F6F74705F7372635F5231334230322F6C69622F7374646C69622F7372632F2E2E2F696E636C7564656802640001696B00732F6E65742F6973696C6475722F6C6469736B2F6461696C795F6275696C642F6F74705F7072656275696C645F7231336230322E323030392D30392D32315F31312F6F74705F7372635F5231334230322F6C69622F7374646C69622F7372632F2E2E2F2E2E2F6B65726E656C2F696E636C75646564000A64656275675F696E666F6A680264000776657273696F6E6B0005342E362E33680264000474696D65680662000007D961096115610A612F61026802640006736F757263656B00662F6E65742F6973696C6475722F6C6469736B2F6461696C795F6275696C642F6F74705F7072656275696C645F7231336230322E323030392D30392D32315F31312F6F74705F7372635F5231334230322F6C69622F7374646C69622F7372632F67656E2E65726C6A +=mod:proc_lib +Current size: 14768 +Current attributes: 836C00000001680264000376736E6C000000016E10004AC3FBDE08DED7F527D06D408676FBB36A6A +Current compilation info: 836C0000000468026400076F7074696F6E736C0000000568026400036377646B005E2F6E65742F6973696C6475722F6C6469736B2F6461696C795F6275696C642F6F74705F7072656275696C645F7231336230322E323030392D30392D32315F31312F6F74705F7372635F5231334230322F6C69622F7374646C69622F73726368026400066F75746469726B00662F6E65742F6973696C6475722F6C6469736B2F6461696C795F6275696C642F6F74705F7072656275696C645F7231336230322E323030392D30392D32315F31312F6F74705F7372635F5231334230322F6C69622F7374646C69622F7372632F2E2E2F6562696E6802640001696B00692F6E65742F6973696C6475722F6C6469736B2F6461696C795F6275696C642F6F74705F7072656275696C645F7231336230322E323030392D30392D32315F31312F6F74705F7372635F5231334230322F6C69622F7374646C69622F7372632F2E2E2F696E636C7564656802640001696B00732F6E65742F6973696C6475722F6C6469736B2F6461696C795F6275696C642F6F74705F7072656275696C645F7231336230322E323030392D30392D32315F31312F6F74705F7372635F5231334230322F6C69622F7374646C69622F7372632F2E2E2F2E2E2F6B65726E656C2F696E636C75646564000A64656275675F696E666F6A680264000776657273696F6E6B0005342E362E33680264000474696D65680662000007D961096115610A612F61066802640006736F757263656B006B2F6E65742F6973696C6475722F6C6469736B2F6461696C795F6275696C642F6F74705F7072656275696C645F7231336230322E323030392D30392D32315F31312F6F74705F7372635F5231334230322F6C69622F7374646C69622F7372632F70726F635F6C69622E65726C6A +=mod:application_controller +Current size: 56502 +Current attributes: 836C00000001680264000376736E6C000000016E1000EBA2CA7AE8F273291D48E8C31C6521756A6A +Current compilation info: 836C0000000468026400076F7074696F6E736C0000000468026400036377646B005E2F6E65742F6973696C6475722F6C6469736B2F6461696C795F6275696C642F6F74705F7072656275696C645F7231336230322E323030392D30392D32315F31312F6F74705F7372635F5231334230322F6C69622F6B65726E656C2F73726368026400066F75746469726B00662F6E65742F6973696C6475722F6C6469736B2F6461696C795F6275696C642F6F74705F7072656275696C645F7231336230322E323030392D30392D32315F31312F6F74705F7372635F5231334230322F6C69622F6B65726E656C2F7372632F2E2E2F6562696E6802640001696B00692F6E65742F6973696C6475722F6C6469736B2F6461696C795F6275696C642F6F74705F7072656275696C645F7231336230322E323030392D30392D32315F31312F6F74705F7372635F5231334230322F6C69622F6B65726E656C2F7372632F2E2E2F696E636C75646564000A64656275675F696E666F6A680264000776657273696F6E6B0005342E362E33680264000474696D65680662000007D961096115610A612F610D6802640006736F757263656B00792F6E65742F6973696C6475722F6C6469736B2F6461696C795F6275696C642F6F74705F7072656275696C645F7231336230322E323030392D30392D32315F31312F6F74705F7372635F5231334230322F6C69622F6B65726E656C2F7372632F6170706C69636174696F6E5F636F6E74726F6C6C65722E65726C6A +=mod:lists +Current size: 59392 +Current attributes: 836C00000002680264000376736E6C000000016E1000D6B0FC3266659B37B0305E511596BEA26A680264000A646570726563617465646C00000001680264000B666C61745F6C656E67746861016A6A +Current compilation info: 836C0000000468026400076F7074696F6E736C0000000D6802640006696E6C696E656C0000000468026400096D65726765335F3132610768026400096D65726765335F32316107680264000A726D65726765335F31326107680264000A726D65726765335F323161076A6802640006696E6C696E656C00000004680264000A756D65726765335F31326108680264000A756D65726765335F32316108680264000C72756D65726765335F3132616107680264000C72756D65726765335F31326261086A6802640006696E6C696E656C00000004680264000C6B65796D65726765335F3132610C680264000C6B65796D65726765335F3231610C680264000D726B65796D65726765335F3132610C680264000D726B65796D65726765335F3231610C6A6802640006696E6C696E656C00000006680264000D756B65796D65726765335F3132610D680264000D756B65796D65726765335F3231610D680264000F72756B65796D65726765335F313261610B680264000F72756B65796D65726765335F323161610D680264000F72756B65796D65726765335F313262610C680264000F72756B65796D65726765335F323162610C6A68026400036377646B005E2F6E65742F6973696C6475722F6C6469736B2F6461696C795F6275696C642F6F74705F7072656275696C645F7231336230322E323030392D30392D32315F31312F6F74705F7372635F5231334230322F6C69622F7374646C69622F73726368026400066F75746469726B00662F6E65742F6973696C6475722F6C6469736B2F6461696C795F6275696C642F6F74705F7072656275696C645F7231336230322E323030392D30392D32315F31312F6F74705F7372635F5231334230322F6C69622F7374646C69622F7372632F2E2E2F6562696E6802640001696B00692F6E65742F6973696C6475722F6C6469736B2F6461696C795F6275696C642F6F74705F7072656275696C645F7231336230322E323030392D30392D32315F31312F6F74705F7372635F5231334230322F6C69622F7374646C69622F7372632F2E2E2F696E636C7564656802640001696B00732F6E65742F6973696C6475722F6C6469736B2F6461696C795F6275696C642F6F74705F7072656275696C645F7231336230322E323030392D30392D32315F31312F6F74705F7372635F5231334230322F6C69622F7374646C69622F7372632F2E2E2F2E2E2F6B65726E656C2F696E636C75646564000A64656275675F696E666F6802640006696E6C696E656C0000000468026400096D65726765335F3132610768026400096D65726765335F32316107680264000A726D65726765335F31326107680264000A726D65726765335F323161076A6802640006696E6C696E656C00000004680264000A756D65726765335F31326108680264000A756D65726765335F32316108680264000C72756D65726765335F3132616107680264000C72756D65726765335F31326261086A6802640006696E6C696E656C00000004680264000C6B65796D65726765335F3132610C680264000C6B65796D65726765335F3231610C680264000D726B65796D65726765335F3132610C680264000D726B65796D65726765335F3231610C6A6802640006696E6C696E656C00000006680264000D756B65796D65726765335F3132610D680264000D756B65796D65726765335F3231610D680264000F72756B65796D65726765335F313261610B680264000F72756B65796D65726765335F323161610D680264000F72756B65796D65726765335F313262610C680264000F72756B65796D65726765335F323162610C6A6A680264000776657273696F6E6B0005342E362E33680264000474696D65680662000007D961096115610A612F61046802640006736F757263656B00682F6E65742F6973696C6475722F6C6469736B2F6461696C795F6275696C642F6F74705F7072656275696C645F7231336230322E323030392D30392D32315F31312F6F74705F7372635F5231334230322F6C69622F7374646C69622F7372632F6C697374732E65726C6A +=mod:gen_server +Current size: 22474 +Current attributes: 836C00000001680264000376736E6C000000016E1000055DD2EFD55034B3AD3BDAA89526651A6A6A +Current compilation info: 836C0000000468026400076F7074696F6E736C0000000568026400036377646B005E2F6E65742F6973696C6475722F6C6469736B2F6461696C795F6275696C642F6F74705F7072656275696C645F7231336230322E323030392D30392D32315F31312F6F74705F7372635F5231334230322F6C69622F7374646C69622F73726368026400066F75746469726B00662F6E65742F6973696C6475722F6C6469736B2F6461696C795F6275696C642F6F74705F7072656275696C645F7231336230322E323030392D30392D32315F31312F6F74705F7372635F5231334230322F6C69622F7374646C69622F7372632F2E2E2F6562696E6802640001696B00692F6E65742F6973696C6475722F6C6469736B2F6461696C795F6275696C642F6F74705F7072656275696C645F7231336230322E323030392D30392D32315F31312F6F74705F7372635F5231334230322F6C69622F7374646C69622F7372632F2E2E2F696E636C7564656802640001696B00732F6E65742F6973696C6475722F6C6469736B2F6461696C795F6275696C642F6F74705F7072656275696C645F7231336230322E323030392D30392D32315F31312F6F74705F7372635F5231334230322F6C69622F7374646C69622F7372632F2E2E2F2E2E2F6B65726E656C2F696E636C75646564000A64656275675F696E666F6A680264000776657273696F6E6B0005342E362E33680264000474696D65680662000007D961096115610A612F61026802640006736F757263656B006D2F6E65742F6973696C6475722F6C6469736B2F6461696C795F6275696C642F6F74705F7072656275696C645F7231336230322E323030392D30392D32315F31312F6F74705F7372635F5231334230322F6C69622F7374646C69622F7372632F67656E5F7365727665722E65726C6A +=mod:application +Current size: 3063 +Current attributes: 836C00000001680264000376736E6C000000016E1000B8769A585260B8FA3272E2A3133343806A6A +Current compilation info: 836C0000000468026400076F7074696F6E736C0000000468026400036377646B005E2F6E65742F6973696C6475722F6C6469736B2F6461696C795F6275696C642F6F74705F7072656275696C645F7231336230322E323030392D30392D32315F31312F6F74705F7372635F5231334230322F6C69622F6B65726E656C2F73726368026400066F75746469726B00662F6E65742F6973696C6475722F6C6469736B2F6461696C795F6275696C642F6F74705F7072656275696C645F7231336230322E323030392D30392D32315F31312F6F74705F7372635F5231334230322F6C69622F6B65726E656C2F7372632F2E2E2F6562696E6802640001696B00692F6E65742F6973696C6475722F6C6469736B2F6461696C795F6275696C642F6F74705F7072656275696C645F7231336230322E323030392D30392D32315F31312F6F74705F7372635F5231334230322F6C69622F6B65726E656C2F7372632F2E2E2F696E636C75646564000A64656275675F696E666F6A680264000776657273696F6E6B0005342E362E33680264000474696D65680662000007D961096115610A612F610C6802640006736F757263656B006E2F6E65742F6973696C6475722F6C6469736B2F6461696C795F6275696C642F6F74705F7072656275696C645F7231336230322E323030392D30392D32315F31312F6F74705F7372635F5231334230322F6C69622F6B65726E656C2F7372632F6170706C69636174696F6E2E65726C6A +=mod:sys +Current size: 12291 +Current attributes: 836C00000001680264000376736E6C000000016E1000452F0CB6A2ECE4BBB27C142C489059606A6A +Current compilation info: 836C0000000468026400076F7074696F6E736C0000000568026400036377646B005E2F6E65742F6973696C6475722F6C6469736B2F6461696C795F6275696C642F6F74705F7072656275696C645F7231336230322E323030392D30392D32315F31312F6F74705F7372635F5231334230322F6C69622F7374646C69622F73726368026400066F75746469726B00662F6E65742F6973696C6475722F6C6469736B2F6461696C795F6275696C642F6F74705F7072656275696C645F7231336230322E323030392D30392D32315F31312F6F74705F7372635F5231334230322F6C69622F7374646C69622F7372632F2E2E2F6562696E6802640001696B00692F6E65742F6973696C6475722F6C6469736B2F6461696C795F6275696C642F6F74705F7072656275696C645F7231336230322E323030392D30392D32315F31312F6F74705F7372635F5231334230322F6C69622F7374646C69622F7372632F2E2E2F696E636C7564656802640001696B00732F6E65742F6973696C6475722F6C6469736B2F6461696C795F6275696C642F6F74705F7072656275696C645F7231336230322E323030392D30392D32315F31312F6F74705F7372635F5231334230322F6C69622F7374646C69622F7372632F2E2E2F2E2E2F6B65726E656C2F696E636C75646564000A64656275675F696E666F6A680264000776657273696F6E6B0005342E362E33680264000474696D65680662000007D961096115610A612F610B6802640006736F757263656B00662F6E65742F6973696C6475722F6C6469736B2F6461696C795F6275696C642F6F74705F7072656275696C645F7231336230322E323030392D30392D32315F31312F6F74705F7372635F5231334230322F6C69622F7374646C69622F7372632F7379732E65726C6A +=mod:application_master +Current size: 10462 +Current attributes: 836C00000001680264000376736E6C000000016E10003BCE6B8EE1A43809BD5CA64F3EA81B3C6A6A +Current compilation info: 836C0000000468026400076F7074696F6E736C0000000468026400036377646B005E2F6E65742F6973696C6475722F6C6469736B2F6461696C795F6275696C642F6F74705F7072656275696C645F7231336230322E323030392D30392D32315F31312F6F74705F7372635F5231334230322F6C69622F6B65726E656C2F73726368026400066F75746469726B00662F6E65742F6973696C6475722F6C6469736B2F6461696C795F6275696C642F6F74705F7072656275696C645F7231336230322E323030392D30392D32315F31312F6F74705F7372635F5231334230322F6C69622F6B65726E656C2F7372632F2E2E2F6562696E6802640001696B00692F6E65742F6973696C6475722F6C6469736B2F6461696C795F6275696C642F6F74705F7072656275696C645F7231336230322E323030392D30392D32315F31312F6F74705F7372635F5231334230322F6C69622F6B65726E656C2F7372632F2E2E2F696E636C75646564000A64656275675F696E666F6A680264000776657273696F6E6B0005342E362E33680264000474696D65680662000007D961096115610A612F610D6802640006736F757263656B00752F6E65742F6973696C6475722F6C6469736B2F6461696C795F6275696C642F6F74705F7072656275696C645F7231336230322E323030392D30392D32315F31312F6F74705F7372635F5231334230322F6C69622F6B65726E656C2F7372632F6170706C69636174696F6E5F6D61737465722E65726C6A +=mod:kernel +Current size: 7931 +Current attributes: 836C00000002680264000376736E6C000000016E1000490C9A7EB0AE1A005D3F169BC6F592FE6A68026400096265686176696F75726C0000000164000A73757065727669736F726A6A +Current compilation info: 836C0000000468026400076F7074696F6E736C0000000468026400036377646B005E2F6E65742F6973696C6475722F6C6469736B2F6461696C795F6275696C642F6F74705F7072656275696C645F7231336230322E323030392D30392D32315F31312F6F74705F7372635F5231334230322F6C69622F6B65726E656C2F73726368026400066F75746469726B00662F6E65742F6973696C6475722F6C6469736B2F6461696C795F6275696C642F6F74705F7072656275696C645F7231336230322E323030392D30392D32315F31312F6F74705F7372635F5231334230322F6C69622F6B65726E656C2F7372632F2E2E2F6562696E6802640001696B00692F6E65742F6973696C6475722F6C6469736B2F6461696C795F6275696C642F6F74705F7072656275696C645F7231336230322E323030392D30392D32315F31312F6F74705F7372635F5231334230322F6C69622F6B65726E656C2F7372632F2E2E2F696E636C75646564000A64656275675F696E666F6A680264000776657273696F6E6B0005342E362E33680264000474696D65680662000007D961096115610A612F61186802640006736F757263656B00692F6E65742F6973696C6475722F6C6469736B2F6461696C795F6275696C642F6F74705F7072656275696C645F7231336230322E323030392D30392D32315F31312F6F74705F7372635F5231334230322F6C69622F6B65726E656C2F7372632F6B65726E656C2E65726C6A +=mod:supervisor +Current size: 25975 +Current attributes: 836C00000002680264000376736E6C000000016E1000BB517CBAE733A4C30300A2C50EAEC9A06A68026400096265686176696F75726C0000000164000A67656E5F7365727665726A6A +Current compilation info: 836C0000000468026400076F7074696F6E736C0000000568026400036377646B005E2F6E65742F6973696C6475722F6C6469736B2F6461696C795F6275696C642F6F74705F7072656275696C645F7231336230322E323030392D30392D32315F31312F6F74705F7372635F5231334230322F6C69622F7374646C69622F73726368026400066F75746469726B00662F6E65742F6973696C6475722F6C6469736B2F6461696C795F6275696C642F6F74705F7072656275696C645F7231336230322E323030392D30392D32315F31312F6F74705F7372635F5231334230322F6C69622F7374646C69622F7372632F2E2E2F6562696E6802640001696B00692F6E65742F6973696C6475722F6C6469736B2F6461696C795F6275696C642F6F74705F7072656275696C645F7231336230322E323030392D30392D32315F31312F6F74705F7372635F5231334230322F6C69622F7374646C69622F7372632F2E2E2F696E636C7564656802640001696B00732F6E65742F6973696C6475722F6C6469736B2F6461696C795F6275696C642F6F74705F7072656275696C645F7231336230322E323030392D30392D32315F31312F6F74705F7372635F5231334230322F6C69622F7374646C69622F7372632F2E2E2F2E2E2F6B65726E656C2F696E636C75646564000A64656275675F696E666F6A680264000776657273696F6E6B0005342E362E33680264000474696D65680662000007D961096115610A612F610B6802640006736F757263656B006D2F6E65742F6973696C6475722F6C6469736B2F6461696C795F6275696C642F6F74705F7072656275696C645F7231336230322E323030392D30392D32315F31312F6F74705F7372635F5231334230322F6C69622F7374646C69622F7372632F73757065727669736F722E65726C6A +=mod:dict +Current size: 16472 +Current attributes: 836C00000001680264000376736E6C000000016E100039984ED8C4E2F0C8E9357673461A2ECD6A6A +Current compilation info: 836C0000000468026400076F7074696F6E736C0000000568026400036377646B005E2F6E65742F6973696C6475722F6C6469736B2F6461696C795F6275696C642F6F74705F7072656275696C645F7231336230322E323030392D30392D32315F31312F6F74705F7372635F5231334230322F6C69622F7374646C69622F73726368026400066F75746469726B00662F6E65742F6973696C6475722F6C6469736B2F6461696C795F6275696C642F6F74705F7072656275696C645F7231336230322E323030392D30392D32315F31312F6F74705F7372635F5231334230322F6C69622F7374646C69622F7372632F2E2E2F6562696E6802640001696B00692F6E65742F6973696C6475722F6C6469736B2F6461696C795F6275696C642F6F74705F7072656275696C645F7231336230322E323030392D30392D32315F31312F6F74705F7372635F5231334230322F6C69622F7374646C69622F7372632F2E2E2F696E636C7564656802640001696B00732F6E65742F6973696C6475722F6C6469736B2F6461696C795F6275696C642F6F74705F7072656275696C645F7231336230322E323030392D30392D32315F31312F6F74705F7372635F5231334230322F6C69622F7374646C69622F7372632F2E2E2F2E2E2F6B65726E656C2F696E636C75646564000A64656275675F696E666F6A680264000776657273696F6E6B0005342E362E33680264000474696D65680662000007D961096115610A612E61306802640006736F757263656B00672F6E65742F6973696C6475722F6C6469736B2F6461696C795F6275696C642F6F74705F7072656275696C645F7231336230322E323030392D30392D32315F31312F6F74705F7372635F5231334230322F6C69622F7374646C69622F7372632F646963742E65726C6A +=mod:rpc +Current size: 13695 +Current attributes: 836C00000003680264000376736E6C000000016E10006899F4CF28BB8739E03F88D447F5BBBA6A68026400096265686176696F75726C0000000164000A67656E5F7365727665726A680264000A646570726563617465646C000000026802640016736166655F6D756C74695F7365727665725F63616C6C61026802640016736166655F6D756C74695F7365727665725F63616C6C61036A6A +Current compilation info: 836C0000000468026400076F7074696F6E736C0000000468026400036377646B005E2F6E65742F6973696C6475722F6C6469736B2F6461696C795F6275696C642F6F74705F7072656275696C645F7231336230322E323030392D30392D32315F31312F6F74705F7372635F5231334230322F6C69622F6B65726E656C2F73726368026400066F75746469726B00662F6E65742F6973696C6475722F6C6469736B2F6461696C795F6275696C642F6F74705F7072656275696C645F7231336230322E323030392D30392D32315F31312F6F74705F7372635F5231334230322F6C69622F6B65726E656C2F7372632F2E2E2F6562696E6802640001696B00692F6E65742F6973696C6475722F6C6469736B2F6461696C795F6275696C642F6F74705F7072656275696C645F7231336230322E323030392D30392D32315F31312F6F74705F7372635F5231334230322F6C69622F6B65726E656C2F7372632F2E2E2F696E636C75646564000A64656275675F696E666F6A680264000776657273696F6E6B0005342E362E33680264000474696D65680662000007D961096115610A612F61196802640006736F757263656B00662F6E65742F6973696C6475722F6C6469736B2F6461696C795F6275696C642F6F74705F7072656275696C645F7231336230322E323030392D30392D32315F31312F6F74705F7372635F5231334230322F6C69622F6B65726E656C2F7372632F7270632E65726C6A +=mod:gb_trees +Current size: 8840 +Current attributes: 836C00000001680264000376736E6C000000016E100027ADE8D1E1F3910206D318DBD63081466A6A +Current compilation info: 836C0000000468026400076F7074696F6E736C0000000568026400036377646B005E2F6E65742F6973696C6475722F6C6469736B2F6461696C795F6275696C642F6F74705F7072656275696C645F7231336230322E323030392D30392D32315F31312F6F74705F7372635F5231334230322F6C69622F7374646C69622F73726368026400066F75746469726B00662F6E65742F6973696C6475722F6C6469736B2F6461696C795F6275696C642F6F74705F7072656275696C645F7231336230322E323030392D30392D32315F31312F6F74705F7372635F5231334230322F6C69622F7374646C69622F7372632F2E2E2F6562696E6802640001696B00692F6E65742F6973696C6475722F6C6469736B2F6461696C795F6275696C642F6F74705F7072656275696C645F7231336230322E323030392D30392D32315F31312F6F74705F7372635F5231334230322F6C69622F7374646C69622F7372632F2E2E2F696E636C7564656802640001696B00732F6E65742F6973696C6475722F6C6469736B2F6461696C795F6275696C642F6F74705F7072656275696C645F7231336230322E323030392D30392D32315F31312F6F74705F7372635F5231334230322F6C69622F7374646C69622F7372632F2E2E2F2E2E2F6B65726E656C2F696E636C75646564000A64656275675F696E666F6A680264000776657273696F6E6B0005342E362E33680264000474696D65680662000007D961096115610A612F61016802640006736F757263656B006B2F6E65742F6973696C6475722F6C6469736B2F6461696C795F6275696C642F6F74705F7072656275696C645F7231336230322E323030392D30392D32315F31312F6F74705F7372635F5231334230322F6C69622F7374646C69622F7372632F67625F74726565732E65726C6A +=mod:global +Current size: 58903 +Current attributes: 836C00000002680264000376736E6C000000016E1000806F436A1663F764DCD60E02B86BC8DE6A68026400096265686176696F75726C0000000164000A67656E5F7365727665726A6A +Current compilation info: 836C0000000468026400076F7074696F6E736C0000000468026400036377646B005E2F6E65742F6973696C6475722F6C6469736B2F6461696C795F6275696C642F6F74705F7072656275696C645F7231336230322E323030392D30392D32315F31312F6F74705F7372635F5231334230322F6C69622F6B65726E656C2F73726368026400066F75746469726B00662F6E65742F6973696C6475722F6C6469736B2F6461696C795F6275696C642F6F74705F7072656275696C645F7231336230322E323030392D30392D32315F31312F6F74705F7372635F5231334230322F6C69622F6B65726E656C2F7372632F2E2E2F6562696E6802640001696B00692F6E65742F6973696C6475722F6C6469736B2F6461696C795F6275696C642F6F74705F7072656275696C645F7231336230322E323030392D30392D32315F31312F6F74705F7372635F5231334230322F6C69622F6B65726E656C2F7372632F2E2E2F696E636C75646564000A64656275675F696E666F6A680264000776657273696F6E6B0005342E362E33680264000474696D65680662000007D961096115610A612F61136802640006736F757263656B00692F6E65742F6973696C6475722F6C6469736B2F6461696C795F6275696C642F6F74705F7072656275696C645F7231336230322E323030392D30392D32315F31312F6F74705F7372635F5231334230322F6C69622F6B65726E656C2F7372632F676C6F62616C2E65726C6A +=mod:inet_db +Current size: 40547 +Current attributes: 836C00000001680264000376736E6C000000016E100051E2C849F3AD52500D6826BBCFCB91466A6A +Current compilation info: 836C0000000468026400076F7074696F6E736C0000000468026400036377646B005E2F6E65742F6973696C6475722F6C6469736B2F6461696C795F6275696C642F6F74705F7072656275696C645F7231336230322E323030392D30392D32315F31312F6F74705F7372635F5231334230322F6C69622F6B65726E656C2F73726368026400066F75746469726B00662F6E65742F6973696C6475722F6C6469736B2F6461696C795F6275696C642F6F74705F7072656275696C645F7231336230322E323030392D30392D32315F31312F6F74705F7372635F5231334230322F6C69622F6B65726E656C2F7372632F2E2E2F6562696E6802640001696B00692F6E65742F6973696C6475722F6C6469736B2F6461696C795F6275696C642F6F74705F7072656275696C645F7231336230322E323030392D30392D32315F31312F6F74705F7372635F5231334230322F6C69622F6B65726E656C2F7372632F2E2E2F696E636C75646564000A64656275675F696E666F6A680264000776657273696F6E6B0005342E362E33680264000474696D65680662000007D961096115610A612F61156802640006736F757263656B006A2F6E65742F6973696C6475722F6C6469736B2F6461696C795F6275696C642F6F74705F7072656275696C645F7231336230322E323030392D30392D32315F31312F6F74705F7372635F5231334230322F6C69622F6B65726E656C2F7372632F696E65745F64622E65726C6A +=mod:inet_config +Current size: 17495 +Current attributes: 836C00000001680264000376736E6C000000016E100050F0A9A329BACF8CE6BD6CB7CC8717026A6A +Current compilation info: 836C0000000468026400076F7074696F6E736C0000000468026400036377646B005E2F6E65742F6973696C6475722F6C6469736B2F6461696C795F6275696C642F6F74705F7072656275696C645F7231336230322E323030392D30392D32315F31312F6F74705F7372635F5231334230322F6C69622F6B65726E656C2F73726368026400066F75746469726B00662F6E65742F6973696C6475722F6C6469736B2F6461696C795F6275696C642F6F74705F7072656275696C645F7231336230322E323030392D30392D32315F31312F6F74705F7372635F5231334230322F6C69622F6B65726E656C2F7372632F2E2E2F6562696E6802640001696B00692F6E65742F6973696C6475722F6C6469736B2F6461696C795F6275696C642F6F74705F7072656275696C645F7231336230322E323030392D30392D32315F31312F6F74705F7372635F5231334230322F6C69622F6B65726E656C2F7372632F2E2E2F696E636C75646564000A64656275675F696E666F6A680264000776657273696F6E6B0005342E362E33680264000474696D65680662000007D961096115610A612F61156802640006736F757263656B006E2F6E65742F6973696C6475722F6C6469736B2F6461696C795F6275696C642F6F74705F7072656275696C645F7231336230322E323030392D30392D32315F31312F6F74705F7372635F5231334230322F6C69622F6B65726E656C2F7372632F696E65745F636F6E6669672E65726C6A +=mod:os +Current size: 7434 +Current attributes: 836C00000001680264000376736E6C000000016E10008DF86B578CA6FDE765FB503541F621D16A6A +Current compilation info: 836C0000000468026400076F7074696F6E736C0000000468026400036377646B005E2F6E65742F6973696C6475722F6C6469736B2F6461696C795F6275696C642F6F74705F7072656275696C645F7231336230322E323030392D30392D32315F31312F6F74705F7372635F5231334230322F6C69622F6B65726E656C2F73726368026400066F75746469726B00662F6E65742F6973696C6475722F6C6469736B2F6461696C795F6275696C642F6F74705F7072656275696C645F7231336230322E323030392D30392D32315F31312F6F74705F7372635F5231334230322F6C69622F6B65726E656C2F7372632F2E2E2F6562696E6802640001696B00692F6E65742F6973696C6475722F6C6469736B2F6461696C795F6275696C642F6F74705F7072656275696C645F7231336230322E323030392D30392D32315F31312F6F74705F7372635F5231334230322F6C69622F6B65726E656C2F7372632F2E2E2F696E636C75646564000A64656275675F696E666F6A680264000776657273696F6E6B0005342E362E33680264000474696D65680662000007D961096115610A612F61196802640006736F757263656B00652F6E65742F6973696C6475722F6C6469736B2F6461696C795F6275696C642F6F74705F7072656275696C645F7231336230322E323030392D30392D32315F31312F6F74705F7372635F5231334230322F6C69622F6B65726E656C2F7372632F6F732E65726C6A +=mod:inet_udp +Current size: 2876 +Current attributes: 836C00000001680264000376736E6C000000016E1000D8E1BB7D93CCB2ACB32140F4C0E11C3C6A6A +Current compilation info: 836C0000000468026400076F7074696F6E736C0000000468026400036377646B005E2F6E65742F6973696C6475722F6C6469736B2F6461696C795F6275696C642F6F74705F7072656275696C645F7231336230322E323030392D30392D32315F31312F6F74705F7372635F5231334230322F6C69622F6B65726E656C2F73726368026400066F75746469726B00662F6E65742F6973696C6475722F6C6469736B2F6461696C795F6275696C642F6F74705F7072656275696C645F7231336230322E323030392D30392D32315F31312F6F74705F7372635F5231334230322F6C69622F6B65726E656C2F7372632F2E2E2F6562696E6802640001696B00692F6E65742F6973696C6475722F6C6469736B2F6461696C795F6275696C642F6F74705F7072656275696C645F7231336230322E323030392D30392D32315F31312F6F74705F7372635F5231334230322F6C69622F6B65726E656C2F7372632F2E2E2F696E636C75646564000A64656275675F696E666F6A680264000776657273696F6E6B0005342E362E33680264000474696D65680662000007D961096115610A612F61176802640006736F757263656B006B2F6E65742F6973696C6475722F6C6469736B2F6461696C795F6275696C642F6F74705F7072656275696C645F7231336230322E323030392D30392D32315F31312F6F74705F7372635F5231334230322F6C69622F6B65726E656C2F7372632F696E65745F7564702E65726C6A +=mod:inet +Current size: 30316 +Current attributes: 836C00000001680264000376736E6C000000016E1000204D68CC2E976CAF71EC4576648081DD6A6A +Current compilation info: 836C0000000468026400076F7074696F6E736C0000000468026400036377646B005E2F6E65742F6973696C6475722F6C6469736B2F6461696C795F6275696C642F6F74705F7072656275696C645F7231336230322E323030392D30392D32315F31312F6F74705F7372635F5231334230322F6C69622F6B65726E656C2F73726368026400066F75746469726B00662F6E65742F6973696C6475722F6C6469736B2F6461696C795F6275696C642F6F74705F7072656275696C645F7231336230322E323030392D30392D32315F31312F6F74705F7372635F5231334230322F6C69622F6B65726E656C2F7372632F2E2E2F6562696E6802640001696B00692F6E65742F6973696C6475722F6C6469736B2F6461696C795F6275696C642F6F74705F7072656275696C645F7231336230322E323030392D30392D32315F31312F6F74705F7372635F5231334230322F6C69622F6B65726E656C2F7372632F2E2E2F696E636C75646564000A64656275675F696E666F6A680264000776657273696F6E6B0005342E362E33680264000474696D65680662000007D961096115610A612F61146802640006736F757263656B00672F6E65742F6973696C6475722F6C6469736B2F6461696C795F6275696C642F6F74705F7072656275696C645F7231336230322E323030392D30392D32315F31312F6F74705F7372635F5231334230322F6C69622F6B65726E656C2F7372632F696E65742E65726C6A +=mod:inet_parse +Current size: 21290 +Current attributes: 836C00000001680264000376736E6C000000016E1000EAA86580FF00AC3E8C7C77E21981A2026A6A +Current compilation info: 836C0000000468026400076F7074696F6E736C0000000468026400036377646B005E2F6E65742F6973696C6475722F6C6469736B2F6461696C795F6275696C642F6F74705F7072656275696C645F7231336230322E323030392D30392D32315F31312F6F74705F7372635F5231334230322F6C69622F6B65726E656C2F73726368026400066F75746469726B00662F6E65742F6973696C6475722F6C6469736B2F6461696C795F6275696C642F6F74705F7072656275696C645F7231336230322E323030392D30392D32315F31312F6F74705F7372635F5231334230322F6C69622F6B65726E656C2F7372632F2E2E2F6562696E6802640001696B00692F6E65742F6973696C6475722F6C6469736B2F6461696C795F6275696C642F6F74705F7072656275696C645F7231336230322E323030392D30392D32315F31312F6F74705F7372635F5231334230322F6C69622F6B65726E656C2F7372632F2E2E2F696E636C75646564000A64656275675F696E666F6A680264000776657273696F6E6B0005342E362E33680264000474696D65680662000007D961096115610A612F61176802640006736F757263656B006D2F6E65742F6973696C6475722F6C6469736B2F6461696C795F6275696C642F6F74705F7072656275696C645F7231336230322E323030392D30392D32315F31312F6F74705F7372635F5231334230322F6C69622F6B65726E656C2F7372632F696E65745F70617273652E65726C6A +=mod:filename +Current size: 16448 +Current attributes: 836C00000001680264000376736E6C000000016E1000BE7434B3DD3172212C93C70B339930FF6A6A +Current compilation info: 836C0000000468026400076F7074696F6E736C0000000568026400036377646B005E2F6E65742F6973696C6475722F6C6469736B2F6461696C795F6275696C642F6F74705F7072656275696C645F7231336230322E323030392D30392D32315F31312F6F74705F7372635F5231334230322F6C69622F7374646C69622F73726368026400066F75746469726B00662F6E65742F6973696C6475722F6C6469736B2F6461696C795F6275696C642F6F74705F7072656275696C645F7231336230322E323030392D30392D32315F31312F6F74705F7372635F5231334230322F6C69622F7374646C69622F7372632F2E2E2F6562696E6802640001696B00692F6E65742F6973696C6475722F6C6469736B2F6461696C795F6275696C642F6F74705F7072656275696C645F7231336230322E323030392D30392D32315F31312F6F74705F7372635F5231334230322F6C69622F7374646C69622F7372632F2E2E2F696E636C7564656802640001696B00732F6E65742F6973696C6475722F6C6469736B2F6461696C795F6275696C642F6F74705F7072656275696C645F7231336230322E323030392D30392D32315F31312F6F74705F7372635F5231334230322F6C69622F7374646C69622F7372632F2E2E2F2E2E2F6B65726E656C2F696E636C75646564000A64656275675F696E666F6A680264000776657273696F6E6B0005342E362E33680264000474696D65680662000007D961096115610A612F61016802640006736F757263656B006B2F6E65742F6973696C6475722F6C6469736B2F6461696C795F6275696C642F6F74705F7072656275696C645F7231336230322E323030392D30392D32315F31312F6F74705F7372635F5231334230322F6C69622F7374646C69622F7372632F66696C656E616D652E65726C6A +=mod:inet_gethost_native +Current size: 16379 +Current attributes: 836C00000002680264000376736E6C000000016E10001FE1B9FDD80E32085974736824D6C09A6A68026400096265686176696F75726C0000000164001173757065727669736F725F6272696467656A6A +Current compilation info: 836C0000000468026400076F7074696F6E736C0000000468026400036377646B005E2F6E65742F6973696C6475722F6C6469736B2F6461696C795F6275696C642F6F74705F7072656275696C645F7231336230322E323030392D30392D32315F31312F6F74705F7372635F5231334230322F6C69622F6B65726E656C2F73726368026400066F75746469726B00662F6E65742F6973696C6475722F6C6469736B2F6461696C795F6275696C642F6F74705F7072656275696C645F7231336230322E323030392D30392D32315F31312F6F74705F7372635F5231334230322F6C69622F6B65726E656C2F7372632F2E2E2F6562696E6802640001696B00692F6E65742F6973696C6475722F6C6469736B2F6461696C795F6275696C642F6F74705F7072656275696C645F7231336230322E323030392D30392D32315F31312F6F74705F7372635F5231334230322F6C69622F6B65726E656C2F7372632F2E2E2F696E636C75646564000A64656275675F696E666F6A680264000776657273696F6E6B0005342E362E33680264000474696D65680662000007D961096115610A612F61166802640006736F757263656B00762F6E65742F6973696C6475722F6C6469736B2F6461696C795F6275696C642F6F74705F7072656275696C645F7231336230322E323030392D30392D32315F31312F6F74705F7372635F5231334230322F6C69622F6B65726E656C2F7372632F696E65745F676574686F73745F6E61746976652E65726C6A +=mod:ets +Current size: 32455 +Current attributes: 836C00000002680264000376736E6C000000016E100069B27A1DF7BF8C746E51D988A6916A666A68026400066F70617175656C00000001680364000F636F6D705F6D617463685F73706563680464000474797065616A640003616E796A6A6A6A +Current compilation info: 836C0000000468026400076F7074696F6E736C0000000568026400036377646B005E2F6E65742F6973696C6475722F6C6469736B2F6461696C795F6275696C642F6F74705F7072656275696C645F7231336230322E323030392D30392D32315F31312F6F74705F7372635F5231334230322F6C69622F7374646C69622F73726368026400066F75746469726B00662F6E65742F6973696C6475722F6C6469736B2F6461696C795F6275696C642F6F74705F7072656275696C645F7231336230322E323030392D30392D32315F31312F6F74705F7372635F5231334230322F6C69622F7374646C69622F7372632F2E2E2F6562696E6802640001696B00692F6E65742F6973696C6475722F6C6469736B2F6461696C795F6275696C642F6F74705F7072656275696C645F7231336230322E323030392D30392D32315F31312F6F74705F7372635F5231334230322F6C69622F7374646C69622F7372632F2E2E2F696E636C7564656802640001696B00732F6E65742F6973696C6475722F6C6469736B2F6461696C795F6275696C642F6F74705F7072656275696C645F7231336230322E323030392D30392D32315F31312F6F74705F7372635F5231334230322F6C69622F7374646C69622F7372632F2E2E2F2E2E2F6B65726E656C2F696E636C75646564000A64656275675F696E666F6A680264000776657273696F6E6B0005342E362E33680264000474696D65680662000007D961096115610A612F61006802640006736F757263656B00662F6E65742F6973696C6475722F6C6469736B2F6461696C795F6275696C642F6F74705F7072656275696C645F7231336230322E323030392D30392D32315F31312F6F74705F7372635F5231334230322F6C69622F7374646C69622F7372632F6574732E65726C6A +=mod:string +Current size: 8342 +Current attributes: 836C00000001680264000376736E6C000000016E1000F2DB2A999ACAAD6C1B6D560DC24869926A6A +Current compilation info: 836C0000000468026400076F7074696F6E736C0000000568026400036377646B005E2F6E65742F6973696C6475722F6C6469736B2F6461696C795F6275696C642F6F74705F7072656275696C645F7231336230322E323030392D30392D32315F31312F6F74705F7372635F5231334230322F6C69622F7374646C69622F73726368026400066F75746469726B00662F6E65742F6973696C6475722F6C6469736B2F6461696C795F6275696C642F6F74705F7072656275696C645F7231336230322E323030392D30392D32315F31312F6F74705F7372635F5231334230322F6C69622F7374646C69622F7372632F2E2E2F6562696E6802640001696B00692F6E65742F6973696C6475722F6C6469736B2F6461696C795F6275696C642F6F74705F7072656275696C645F7231336230322E323030392D30392D32315F31312F6F74705F7372635F5231334230322F6C69622F7374646C69622F7372632F2E2E2F696E636C7564656802640001696B00732F6E65742F6973696C6475722F6C6469736B2F6461696C795F6275696C642F6F74705F7072656275696C645F7231336230322E323030392D30392D32315F31312F6F74705F7372635F5231334230322F6C69622F7374646C69622F7372632F2E2E2F2E2E2F6B65726E656C2F696E636C75646564000A64656275675F696E666F6A680264000776657273696F6E6B0005342E362E33680264000474696D65680662000007D961096115610A612F610A6802640006736F757263656B00692F6E65742F6973696C6475722F6C6469736B2F6461696C795F6275696C642F6F74705F7072656275696C645F7231336230322E323030392D30392D32315F31312F6F74705F7372635F5231334230322F6C69622F7374646C69622F7372632F737472696E672E65726C6A +=mod:erl_distribution +Current size: 2609 +Current attributes: 836C00000002680264000376736E6C000000016E1000B3F924E399315DCE859D7FFA9CC7EA536A68026400096265686176696F75726C0000000164000A73757065727669736F726A6A +Current compilation info: 836C0000000468026400076F7074696F6E736C0000000468026400036377646B005E2F6E65742F6973696C6475722F6C6469736B2F6461696C795F6275696C642F6F74705F7072656275696C645F7231336230322E323030392D30392D32315F31312F6F74705F7372635F5231334230322F6C69622F6B65726E656C2F73726368026400066F75746469726B00662F6E65742F6973696C6475722F6C6469736B2F6461696C795F6275696C642F6F74705F7072656275696C645F7231336230322E323030392D30392D32315F31312F6F74705F7372635F5231334230322F6C69622F6B65726E656C2F7372632F2E2E2F6562696E6802640001696B00692F6E65742F6973696C6475722F6C6469736B2F6461696C795F6275696C642F6F74705F7072656275696C645F7231336230322E323030392D30392D32315F31312F6F74705F7372635F5231334230322F6C69622F6B65726E656C2F7372632F2E2E2F696E636C75646564000A64656275675F696E666F6A680264000776657273696F6E6B0005342E362E33680264000474696D65680662000007D961096115610A612F61106802640006736F757263656B00732F6E65742F6973696C6475722F6C6469736B2F6461696C795F6275696C642F6F74705F7072656275696C645F7231336230322E323030392D30392D32315F31312F6F74705F7372635F5231334230322F6C69622F6B65726E656C2F7372632F65726C5F646973747269627574696F6E2E65726C6A +=mod:net_kernel +Current size: 40195 +Current attributes: 836C00000002680264000376736E6C000000016E1000F44372760374119129D80868D668F3B36A68026400096265686176696F75726C0000000164000A67656E5F7365727665726A6A +Current compilation info: 836C0000000468026400076F7074696F6E736C0000000468026400036377646B005E2F6E65742F6973696C6475722F6C6469736B2F6461696C795F6275696C642F6F74705F7072656275696C645F7231336230322E323030392D30392D32315F31312F6F74705F7372635F5231334230322F6C69622F6B65726E656C2F73726368026400066F75746469726B00662F6E65742F6973696C6475722F6C6469736B2F6461696C795F6275696C642F6F74705F7072656275696C645F7231336230322E323030392D30392D32315F31312F6F74705F7372635F5231334230322F6C69622F6B65726E656C2F7372632F2E2E2F6562696E6802640001696B00692F6E65742F6973696C6475722F6C6469736B2F6461696C795F6275696C642F6F74705F7072656275696C645F7231336230322E323030392D30392D32315F31312F6F74705F7372635F5231334230322F6C69622F6B65726E656C2F7372632F2E2E2F696E636C75646564000A64656275675F696E666F6A680264000776657273696F6E6B0005342E362E33680264000474696D65680662000007D961096115610A612F61186802640006736F757263656B006D2F6E65742F6973696C6475722F6C6469736B2F6461696C795F6275696C642F6F74705F7072656275696C645F7231336230322E323030392D30392D32315F31312F6F74705F7372635F5231334230322F6C69622F6B65726E656C2F7372632F6E65745F6B65726E656C2E65726C6A +=mod:inet_tcp_dist +Current size: 11633 +Current attributes: 836C00000001680264000376736E6C000000016E10009088DBF366256CB891AF1640CB63B0DD6A6A +Current compilation info: 836C0000000468026400076F7074696F6E736C0000000468026400036377646B005E2F6E65742F6973696C6475722F6C6469736B2F6461696C795F6275696C642F6F74705F7072656275696C645F7231336230322E323030392D30392D32315F31312F6F74705F7372635F5231334230322F6C69622F6B65726E656C2F73726368026400066F75746469726B00662F6E65742F6973696C6475722F6C6469736B2F6461696C795F6275696C642F6F74705F7072656275696C645F7231336230322E323030392D30392D32315F31312F6F74705F7372635F5231334230322F6C69622F6B65726E656C2F7372632F2E2E2F6562696E6802640001696B00692F6E65742F6973696C6475722F6C6469736B2F6461696C795F6275696C642F6F74705F7072656275696C645F7231336230322E323030392D30392D32315F31312F6F74705F7372635F5231334230322F6C69622F6B65726E656C2F7372632F2E2E2F696E636C75646564000A64656275675F696E666F6A680264000776657273696F6E6B0005342E362E33680264000474696D65680662000007D961096115610A612F61176802640006736F757263656B00702F6E65742F6973696C6475722F6C6469736B2F6461696C795F6275696C642F6F74705F7072656275696C645F7231336230322E323030392D30392D32315F31312F6F74705F7372635F5231334230322F6C69622F6B65726E656C2F7372632F696E65745F7463705F646973742E65726C6A +=mod:erl_epmd +Current size: 14665 +Current attributes: 836C00000002680264000376736E6C000000016E10000E834D3AD621AD3E4D75282A919F095B6A68026400096265686176696F75726C0000000164000A67656E5F7365727665726A6A +Current compilation info: 836C0000000468026400076F7074696F6E736C0000000968036400016464001265726C616E675F6461656D6F6E5F706F7274620000111168036400016464000E65706D645F646973745F68696768610568036400016464000D65706D645F646973745F6C6F77610568036400016464000E65706D645F6E6F64655F74797065616E68036400016464000C65706D645F706F72745F6E6F620000111168026400036377646B005E2F6E65742F6973696C6475722F6C6469736B2F6461696C795F6275696C642F6F74705F7072656275696C645F7231336230322E323030392D30392D32315F31312F6F74705F7372635F5231334230322F6C69622F6B65726E656C2F73726368026400066F75746469726B00662F6E65742F6973696C6475722F6C6469736B2F6461696C795F6275696C642F6F74705F7072656275696C645F7231336230322E323030392D30392D32315F31312F6F74705F7372635F5231334230322F6C69622F6B65726E656C2F7372632F2E2E2F6562696E6802640001696B00692F6E65742F6973696C6475722F6C6469736B2F6461696C795F6275696C642F6F74705F7072656275696C645F7231336230322E323030392D30392D32315F31312F6F74705F7372635F5231334230322F6C69622F6B65726E656C2F7372632F2E2E2F696E636C75646564000A64656275675F696E666F6A680264000776657273696F6E6B0005342E362E33680264000474696D65680662000007D961096115610A612F61116802640006736F757263656B006B2F6E65742F6973696C6475722F6C6469736B2F6461696C795F6275696C642F6F74705F7072656275696C645F7231336230322E323030392D30392D32315F31312F6F74705F7372635F5231334230322F6C69622F6B65726E656C2F7372632F65726C5F65706D642E65726C6A +=mod:auth +Current size: 12159 +Current attributes: 836C00000003680264000376736E6C000000016E10002929A4040E8520372F1309DDA4035DD96A68026400096265686176696F75726C0000000164000A67656E5F7365727665726A680264000A646570726563617465646C00000003680264000769735F6175746861016802640006636F6F6B69656400015F680264000B6E6F64655F636F6F6B69656400015F6A6A +Current compilation info: 836C0000000468026400076F7074696F6E736C0000000468026400036377646B005E2F6E65742F6973696C6475722F6C6469736B2F6461696C795F6275696C642F6F74705F7072656275696C645F7231336230322E323030392D30392D32315F31312F6F74705F7372635F5231334230322F6C69622F6B65726E656C2F73726368026400066F75746469726B00662F6E65742F6973696C6475722F6C6469736B2F6461696C795F6275696C642F6F74705F7072656275696C645F7231336230322E323030392D30392D32315F31312F6F74705F7372635F5231334230322F6C69622F6B65726E656C2F7372632F2E2E2F6562696E6802640001696B00692F6E65742F6973696C6475722F6C6469736B2F6461696C795F6275696C642F6F74705F7072656275696C645F7231336230322E323030392D30392D32315F31312F6F74705F7372635F5231334230322F6C69622F6B65726E656C2F7372632F2E2E2F696E636C75646564000A64656275675F696E666F6A680264000776657273696F6E6B0005342E362E33680264000474696D65680662000007D961096115610A612F610D6802640006736F757263656B00672F6E65742F6973696C6475722F6C6469736B2F6461696C795F6275696C642F6F74705F7072656275696C645F7231336230322E323030392D30392D32315F31312F6F74705F7372635F5231334230322F6C69622F6B65726E656C2F7372632F617574682E65726C6A +=mod:file +Current size: 20472 +Current attributes: 836C00000001680264000376736E6C000000016E1000F098A9E1D749FBBAD7557402AA4041FC6A6A +Current compilation info: 836C0000000468026400076F7074696F6E736C0000000468026400036377646B005E2F6E65742F6973696C6475722F6C6469736B2F6461696C795F6275696C642F6F74705F7072656275696C645F7231336230322E323030392D30392D32315F31312F6F74705F7372635F5231334230322F6C69622F6B65726E656C2F73726368026400066F75746469726B00662F6E65742F6973696C6475722F6C6469736B2F6461696C795F6275696C642F6F74705F7072656275696C645F7231336230322E323030392D30392D32315F31312F6F74705F7372635F5231334230322F6C69622F6B65726E656C2F7372632F2E2E2F6562696E6802640001696B00692F6E65742F6973696C6475722F6C6469736B2F6461696C795F6275696C642F6F74705F7072656275696C645F7231336230322E323030392D30392D32315F31312F6F74705F7372635F5231334230322F6C69622F6B65726E656C2F7372632F2E2E2F696E636C75646564000A64656275675F696E666F6A680264000776657273696F6E6B0005342E362E33680264000474696D65680662000007D961096115610A612F61116802640006736F757263656B00672F6E65742F6973696C6475722F6C6469736B2F6461696C795F6275696C642F6F74705F7072656275696C645F7231336230322E323030392D30392D32315F31312F6F74705F7372635F5231334230322F6C69622F6B65726E656C2F7372632F66696C652E65726C6A +=mod:inet_tcp +Current size: 3396 +Current attributes: 836C00000001680264000376736E6C000000016E1000320AD835042696799AD4B092D5BBCE8F6A6A +Current compilation info: 836C0000000468026400076F7074696F6E736C0000000468026400036377646B005E2F6E65742F6973696C6475722F6C6469736B2F6461696C795F6275696C642F6F74705F7072656275696C645F7231336230322E323030392D30392D32315F31312F6F74705F7372635F5231334230322F6C69622F6B65726E656C2F73726368026400066F75746469726B00662F6E65742F6973696C6475722F6C6469736B2F6461696C795F6275696C642F6F74705F7072656275696C645F7231336230322E323030392D30392D32315F31312F6F74705F7372635F5231334230322F6C69622F6B65726E656C2F7372632F2E2E2F6562696E6802640001696B00692F6E65742F6973696C6475722F6C6469736B2F6461696C795F6275696C642F6F74705F7072656275696C645F7231336230322E323030392D30392D32315F31312F6F74705F7372635F5231334230322F6C69622F6B65726E656C2F7372632F2E2E2F696E636C75646564000A64656275675F696E666F6A680264000776657273696F6E6B0005342E362E33680264000474696D65680662000007D961096115610A612F61176802640006736F757263656B006B2F6E65742F6973696C6475722F6C6469736B2F6461696C795F6275696C642F6F74705F7072656275696C645F7231336230322E323030392D30392D32315F31312F6F74705F7372635F5231334230322F6C69622F6B65726E656C2F7372632F696E65745F7463702E65726C6A +=mod:gen_tcp +Current size: 3635 +Current attributes: 836C00000001680264000376736E6C000000016E1000132A2451489621933F4E3C2BBBF9EC726A6A +Current compilation info: 836C0000000468026400076F7074696F6E736C0000000468026400036377646B005E2F6E65742F6973696C6475722F6C6469736B2F6461696C795F6275696C642F6F74705F7072656275696C645F7231336230322E323030392D30392D32315F31312F6F74705F7372635F5231334230322F6C69622F6B65726E656C2F73726368026400066F75746469726B00662F6E65742F6973696C6475722F6C6469736B2F6461696C795F6275696C642F6F74705F7072656275696C645F7231336230322E323030392D30392D32315F31312F6F74705F7372635F5231334230322F6C69622F6B65726E656C2F7372632F2E2E2F6562696E6802640001696B00692F6E65742F6973696C6475722F6C6469736B2F6461696C795F6275696C642F6F74705F7072656275696C645F7231336230322E323030392D30392D32315F31312F6F74705F7372635F5231334230322F6C69622F6B65726E656C2F7372632F2E2E2F696E636C75646564000A64656275675F696E666F6A680264000776657273696F6E6B0005342E362E33680264000474696D65680662000007D961096115610A612F61126802640006736F757263656B006A2F6E65742F6973696C6475722F6C6469736B2F6461696C795F6275696C642F6F74705F7072656275696C645F7231336230322E323030392D30392D32315F31312F6F74705F7372635F5231334230322F6C69622F6B65726E656C2F7372632F67656E5F7463702E65726C6A +=mod:io_lib +Current size: 14951 +Current attributes: 836C00000001680264000376736E6C000000016E1000529F49CCB3FD275042BAECD4BA7C818D6A6A +Current compilation info: 836C0000000468026400076F7074696F6E736C0000000568026400036377646B005E2F6E65742F6973696C6475722F6C6469736B2F6461696C795F6275696C642F6F74705F7072656275696C645F7231336230322E323030392D30392D32315F31312F6F74705F7372635F5231334230322F6C69622F7374646C69622F73726368026400066F75746469726B00662F6E65742F6973696C6475722F6C6469736B2F6461696C795F6275696C642F6F74705F7072656275696C645F7231336230322E323030392D30392D32315F31312F6F74705F7372635F5231334230322F6C69622F7374646C69622F7372632F2E2E2F6562696E6802640001696B00692F6E65742F6973696C6475722F6C6469736B2F6461696C795F6275696C642F6F74705F7072656275696C645F7231336230322E323030392D30392D32315F31312F6F74705F7372635F5231334230322F6C69622F7374646C69622F7372632F2E2E2F696E636C7564656802640001696B00732F6E65742F6973696C6475722F6C6469736B2F6461696C795F6275696C642F6F74705F7072656275696C645F7231336230322E323030392D30392D32315F31312F6F74705F7372635F5231334230322F6C69622F7374646C69622F7372632F2E2E2F2E2E2F6B65726E656C2F696E636C75646564000A64656275675F696E666F6A680264000776657273696F6E6B0005342E362E33680264000474696D65680662000007D961096115610A612F61036802640006736F757263656B00692F6E65742F6973696C6475722F6C6469736B2F6461696C795F6275696C642F6F74705F7072656275696C645F7231336230322E323030392D30392D32315F31312F6F74705F7372635F5231334230322F6C69622F7374646C69622F7372632F696F5F6C69622E65726C6A +=mod:erl_scan +Current size: 66226 +Current attributes: 836C00000003680264000376736E6C000000016E100068BE7C492759DAEC0A47792463C5FEA16A68026400066F70617175656C00000001680364000F617474726962757465735F646174616804640004747970656157640005756E696F6E6C0000000268046400047479706561576400046C6973746C000000016804640004747970656157640005756E696F6E6C0000000368046400047479706561576400057475706C656C00000002680364000461746F6D6157640006636F6C756D6E6804640004747970656157640006636F6C756D6E6A6A68046400047479706561576400057475706C656C00000002680364000461746F6D61576400046C696E656804640004747970656157640009696E666F5F6C696E656A6A68046400047479706561576400057475706C656C00000002680364000461746F6D6157640004746578746804640004747970656157640006737472696E676A6A6A6A68046400047479706561586400057475706C656C0000000268046400047479706561586400046C696E656A6804640004747970656158640006636F6C756D6E6A6A6A6A6A68026400066F70617175656C00000001680364000B72657475726E5F636F6E7468046400047479706561876400057475706C656C000000076804640004747970656187640006737472696E676A6804640004747970656187640006636F6C756D6E6A6804640004747970656187640006746F6B656E736A68046400047479706561876400046C696E656A68046400047479706561886400067265636F72646C00000001680364000461746F6D618864000865726C5F7363616E6A6804640004747970656188640008636F6E745F66756E6A6804640004747970656188640003616E796A6A6A6A6A +Current compilation info: 836C0000000468026400076F7074696F6E736C0000000B6802640006696E6C696E656C00000001680264000F77686974655F73706163655F656E6461076A6802640006696E6C696E656C00000001680264000A6174747269627574657361046A6802640006696E6C696E656C00000002680264000B696E63725F636F6C756D6E6102680264000A6E65775F636F6C756D6E61026A68026400036377646B005E2F6E65742F6973696C6475722F6C6469736B2F6461696C795F6275696C642F6F74705F7072656275696C645F7231336230322E323030392D30392D32315F31312F6F74705F7372635F5231334230322F6C69622F7374646C69622F73726368026400066F75746469726B00662F6E65742F6973696C6475722F6C6469736B2F6461696C795F6275696C642F6F74705F7072656275696C645F7231336230322E323030392D30392D32315F31312F6F74705F7372635F5231334230322F6C69622F7374646C69622F7372632F2E2E2F6562696E6802640001696B00692F6E65742F6973696C6475722F6C6469736B2F6461696C795F6275696C642F6F74705F7072656275696C645F7231336230322E323030392D30392D32315F31312F6F74705F7372635F5231334230322F6C69622F7374646C69622F7372632F2E2E2F696E636C7564656802640001696B00732F6E65742F6973696C6475722F6C6469736B2F6461696C795F6275696C642F6F74705F7072656275696C645F7231336230322E323030392D30392D32315F31312F6F74705F7372635F5231334230322F6C69622F7374646C69622F7372632F2E2E2F2E2E2F6B65726E656C2F696E636C75646564000A64656275675F696E666F6802640006696E6C696E656C00000001680264000F77686974655F73706163655F656E6461076A6802640006696E6C696E656C00000001680264000A6174747269627574657361046A6802640006696E6C696E656C00000002680264000B696E63725F636F6C756D6E6102680264000A6E65775F636F6C756D6E61026A6A680264000776657273696F6E6B0005342E362E33680264000474696D65680662000007D961096115610A612E61366802640006736F757263656B006B2F6E65742F6973696C6475722F6C6469736B2F6461696C795F6275696C642F6F74705F7072656275696C645F7231336230322E323030392D30392D32315F31312F6F74705F7372635F5231334230322F6C69622F7374646C69622F7372632F65726C5F7363616E2E65726C6A +=fun +Module: net_kernel +Uniq: 7335464 +Index: 2 +Address: 0xb598cd9c +Native_address: 0x08185384 +Refc: 1 +=fun +Module: inet_parse +Uniq: 33089816 +Index: 11 +Address: 0xb7d595e4 +Native_address: 0x08185384 +Refc: 1 +=fun +Module: inet_tcp_dist +Uniq: 12202156 +Index: 4 +Address: 0xb596435c +Native_address: 0x08185384 +Refc: 1 +=fun +Module: inet_config +Uniq: 104182465 +Index: 4 +Address: 0xb7d48384 +Native_address: 0x08185384 +Refc: 1 +=fun +Module: global +Uniq: 62203979 +Index: 22 +Address: 0xb7d43068 +Native_address: 0x08185384 +Refc: 1 +=fun +Module: erl_prim_loader +Uniq: 87386555 +Index: 18 +Address: 0xb7cd9a90 +Native_address: 0x0818537c +Refc: 1 +=fun +Module: ets +Uniq: 93898976 +Index: 4 +Address: 0xb5970b20 +Native_address: 0x0818538c +Refc: 1 +=fun +Module: erl_scan +Uniq: 13867962 +Index: 14 +Address: 0xb59b9c10 +Native_address: 0x08185374 +Refc: 1 +=fun +Module: inet +Uniq: 119811797 +Index: 3 +Address: 0xb7d508bc +Native_address: 0x08185384 +Refc: 1 +=fun +Module: dict +Uniq: 89486578 +Index: 4 +Address: 0xb7d153ac +Native_address: 0x08185384 +Refc: 1 +=fun +Module: sys +Uniq: 127526891 +Index: 0 +Address: 0xb7d059f4 +Native_address: 0x08185384 +Refc: 1 +=fun +Module: inet_parse +Uniq: 60134508 +Index: 13 +Address: 0xb7d595ac +Native_address: 0x08185384 +Refc: 1 +=fun +Module: ets +Uniq: 50416600 +Index: 3 +Address: 0xb5970c20 +Native_address: 0x08185384 +Refc: 1 +=fun +Module: application_controller +Uniq: 14886695 +Index: 6 +Address: 0xb7d013cc +Native_address: 0x08185384 +Refc: 1 +=fun +Module: prim_zip +Uniq: 48567280 +Index: 0 +Address: 0xb7cb0b88 +Native_address: 0x0818537c +Refc: 1 +=fun +Module: erl_scan +Uniq: 132577084 +Index: 29 +Address: 0xb59b9954 +Native_address: 0x08185374 +Refc: 1 +=fun +Module: inet_parse +Uniq: 97775021 +Index: 1 +Address: 0xb7d59d78 +Native_address: 0x08185384 +Refc: 1 +=fun +Module: inet +Uniq: 121533714 +Index: 0 +Address: 0xb7d50940 +Native_address: 0x08185384 +Refc: 1 +=fun +Module: ets +Uniq: 108071367 +Index: 1 +Address: 0xb5970ef8 +Native_address: 0x0818537c +Refc: 1 +=fun +Module: ets +Uniq: 101208419 +Index: 0 +Address: 0xb5970f30 +Native_address: 0x0818537c +Refc: 1 +=fun +Module: gen_server +Uniq: 91316170 +Index: 0 +Address: 0xb7d0dd84 +Native_address: 0x0818538c +Refc: 1 +=fun +Module: erl_prim_loader +Uniq: 31841620 +Index: 15 +Address: 0xb7cd9e0c +Native_address: 0x0818537c +Refc: 1 +=fun +Module: ets +Uniq: 85134466 +Index: 2 +Address: 0xb5970d88 +Native_address: 0x08185384 +Refc: 1 +=fun +Module: global +Uniq: 114663564 +Index: 1 +Address: 0xb7d44120 +Native_address: 0x08185384 +Refc: 1 +=fun +Module: inet +Uniq: 61003236 +Index: 2 +Address: 0xb7d508d8 +Native_address: 0x08185384 +Refc: 1 +=fun +Module: global +Uniq: 39261695 +Index: 24 +Address: 0xb7d43014 +Native_address: 0x0818538c +Refc: 1 +=fun +Module: inet_tcp_dist +Uniq: 132089710 +Index: 8 +Address: 0xb59641f8 +Native_address: 0x0818537c +Refc: 1 +=fun +Module: inet_parse +Uniq: 97930656 +Index: 5 +Address: 0xb7d5992c +Native_address: 0x08185384 +Refc: 1 +=fun +Module: os +Uniq: 11535056 +Index: 1 +Address: 0xb7d34ea8 +Native_address: 0x0818538c +Refc: 1 +=fun +Module: ets +Uniq: 27791349 +Index: 7 +Address: 0xb5970b50 +Native_address: 0x08185384 +Refc: 1 +=fun +Module: inet +Uniq: 30921023 +Index: 6 +Address: 0xb7d505c0 +Native_address: 0x0818537c +Refc: 1 +=fun +Module: inet_db +Uniq: 55350251 +Index: 7 +Address: 0xb7d330b4 +Native_address: 0x08185384 +Refc: 1 +=fun +Module: rpc +Uniq: 20606123 +Index: 4 +Address: 0xb7d25c8c +Native_address: 0x08185384 +Refc: 1 +=fun +Module: erl_scan +Uniq: 50599472 +Index: 20 +Address: 0xb59b9a50 +Native_address: 0x08185374 +Refc: 1 +=fun +Module: inet_tcp_dist +Uniq: 72907366 +Index: 7 +Address: 0xb596425c +Native_address: 0x08185384 +Refc: 1 +=fun +Module: application_controller +Uniq: 126397741 +Index: 15 +Address: 0xb7d00e48 +Native_address: 0x08185384 +Refc: 1 +=fun +Module: application_controller +Uniq: 7161020 +Index: 14 +Address: 0xb7d00ed8 +Native_address: 0x0818537c +Refc: 1 +=fun +Module: ets +Uniq: 69761134 +Index: 8 +Address: 0xb5970ae0 +Native_address: 0x08185384 +Refc: 1 +=fun +Module: dict +Uniq: 14151155 +Index: 11 +Address: 0xb7d15124 +Native_address: 0x08185374 +Refc: 1 +=fun +Module: gen_event +Uniq: 110575489 +Index: 2 +Address: 0xb7ccf94c +Native_address: 0x08185384 +Refc: 1 +=fun +Module: dict +Uniq: 81389057 +Index: 1 +Address: 0xb7d15438 +Native_address: 0x0818537c +Refc: 1 +=fun +Module: erl_scan +Uniq: 1479109 +Index: 30 +Address: 0xb59b991c +Native_address: 0x08185374 +Refc: 1 +=fun +Module: inet_tcp_dist +Uniq: 94764167 +Index: 6 +Address: 0xb59642d4 +Native_address: 0x08185384 +Refc: 1 +=fun +Module: inet +Uniq: 42373228 +Index: 5 +Address: 0xb7d506b8 +Native_address: 0x08185384 +Refc: 1 +=fun +Module: os +Uniq: 21594516 +Index: 0 +Address: 0xb7d34ee4 +Native_address: 0x0818538c +Refc: 1 +=fun +Module: global +Uniq: 100913830 +Index: 15 +Address: 0xb7d436f4 +Native_address: 0x0818537c +Refc: 1 +=fun +Module: net_kernel +Uniq: 104470939 +Index: 0 +Address: 0xb598ce9c +Native_address: 0x08185384 +Refc: 1 +=fun +Module: inet_parse +Uniq: 93670768 +Index: 14 +Address: 0xb7d59590 +Native_address: 0x08185384 +Refc: 1 +=fun +Module: erl_prim_loader +Uniq: 99263058 +Index: 16 +Address: 0xb7cd9c84 +Native_address: 0x0818537c +Refc: 1 +=fun +Module: inet_db +Uniq: 66083174 +Index: 11 +Address: 0xb7d32788 +Native_address: 0x0818537c +Refc: 1 +=fun +Module: lists +Uniq: 101351085 +Index: 0 +Address: 0xb7cf1d60 +Native_address: 0x08185384 +Refc: 1 +=fun +Module: inet_parse +Uniq: 22402890 +Index: 10 +Address: 0xb7d59670 +Native_address: 0x08185384 +Refc: 1 +=fun +Module: rpc +Uniq: 69165610 +Index: 0 +Address: 0xb7d25df4 +Native_address: 0x0818538c +Refc: 1 +=fun +Module: erl_prim_loader +Uniq: 91790143 +Index: 17 +Address: 0xb7cd9b50 +Native_address: 0x0818537c +Refc: 1 +=fun +Module: inet_config +Uniq: 78756860 +Index: 2 +Address: 0xb7d4842c +Native_address: 0x08185384 +Refc: 1 +=fun +Module: erl_prim_loader +Uniq: 4823423 +Index: 4 +Address: 0xb7cda870 +Native_address: 0x0818538c +Refc: 1 +=fun +Module: dict +Uniq: 65221643 +Index: 13 +Address: 0xb7d15254 +Native_address: 0x08185384 +Refc: 1 +=fun +Module: erl_prim_loader +Uniq: 86224164 +Index: 6 +Address: 0xb7cda700 +Native_address: 0x0818538c +Refc: 1 +=fun +Module: inet_config +Uniq: 482064 +Index: 0 +Address: 0xb7d484bc +Native_address: 0x08185384 +Refc: 1 +=fun +Module: inet_tcp_dist +Uniq: 39127961 +Index: 2 +Address: 0xb59643f0 +Native_address: 0x08185384 +Refc: 1 +=fun +Module: rpc +Uniq: 103889561 +Index: 2 +Address: 0xb7d25d84 +Native_address: 0x0818538c +Refc: 1 +=fun +Module: erl_scan +Uniq: 124437465 +Index: 23 +Address: 0xb59b99fc +Native_address: 0x08185374 +Refc: 1 +=fun +Module: application_controller +Uniq: 28106645 +Index: 1 +Address: 0xb7d01774 +Native_address: 0x08185384 +Refc: 1 +=fun +Module: erl_prim_loader +Uniq: 50133685 +Index: 12 +Address: 0xb7cda2dc +Native_address: 0x0818538c +Refc: 1 +=fun +Module: erl_prim_loader +Uniq: 11086051 +Index: 14 +Address: 0xb7cda0fc +Native_address: 0x0818537c +Refc: 1 +=fun +Module: global +Uniq: 17842198 +Index: 0 +Address: 0xb7d44204 +Native_address: 0x08185374 +Refc: 1 +=fun +Module: erl_scan +Uniq: 126558884 +Index: 15 +Address: 0xb59b9adc +Native_address: 0x08185374 +Refc: 1 +=fun +Module: erl_scan +Uniq: 9897799 +Index: 18 +Address: 0xb59b9a88 +Native_address: 0x08185374 +Refc: 1 +=fun +Module: erl_scan +Uniq: 120081942 +Index: 22 +Address: 0xb59b9a18 +Native_address: 0x08185374 +Refc: 1 +=fun +Module: erl_scan +Uniq: 100293911 +Index: 13 +Address: 0xb59b9af8 +Native_address: 0x08185374 +Refc: 1 +=fun +Module: erl_scan +Uniq: 115285334 +Index: 6 +Address: 0xb59b9ba0 +Native_address: 0x08185374 +Refc: 1 +=fun +Module: global +Uniq: 102835157 +Index: 7 +Address: 0xb7d43e78 +Native_address: 0x08185384 +Refc: 1 +=fun +Module: erl_prim_loader +Uniq: 12673501 +Index: 13 +Address: 0xb7cda1c0 +Native_address: 0x0818537c +Refc: 1 +=fun +Module: global +Uniq: 18939484 +Index: 25 +Address: 0xb7d42f4c +Native_address: 0x08185384 +Refc: 1 +=fun +Module: erl_scan +Uniq: 35460975 +Index: 10 +Address: 0xb59b9b4c +Native_address: 0x08185374 +Refc: 1 +=fun +Module: application_controller +Uniq: 83020074 +Index: 12 +Address: 0xb7d01004 +Native_address: 0x08185384 +Refc: 1 +=fun +Module: inet_parse +Uniq: 16087369 +Index: 6 +Address: 0xb7d598f4 +Native_address: 0x08185384 +Refc: 1 +=fun +Module: rpc +Uniq: 108480693 +Index: 5 +Address: 0xb7d25bec +Native_address: 0x0818538c +Refc: 1 +=fun +Module: application_controller +Uniq: 30656608 +Index: 4 +Address: 0xb7d01494 +Native_address: 0x08185384 +Refc: 1 +=fun +Module: prim_zip +Uniq: 119495432 +Index: 3 +Address: 0xb7cb0b14 +Native_address: 0x0818538c +Refc: 1 +=fun +Module: inet +Uniq: 34536943 +Index: 1 +Address: 0xb7d5090c +Native_address: 0x08185384 +Refc: 1 +=fun +Module: erl_scan +Uniq: 46254756 +Index: 34 +Address: 0xb59b98c8 +Native_address: 0x08185374 +Refc: 1 +=fun +Module: erl_scan +Uniq: 123404702 +Index: 0 +Address: 0xb59b9c80 +Native_address: 0x08185374 +Refc: 1 +=fun +Module: erl_prim_loader +Uniq: 117475929 +Index: 11 +Address: 0xb7cda32c +Native_address: 0x0818538c +Refc: 1 +=fun +Module: inet_db +Uniq: 8016715 +Index: 3 +Address: 0xb7d33124 +Native_address: 0x08185384 +Refc: 1 +=fun +Module: init +Uniq: 71194557 +Index: 4 +Address: 0xb7ca8b80 +Native_address: 0x0818538c +Refc: 1 +=fun +Module: init +Uniq: 120821421 +Index: 3 +Address: 0xb7ca8c60 +Native_address: 0x08185384 +Refc: 1 +=fun +Module: global +Uniq: 63862060 +Index: 27 +Address: 0xb7d42e64 +Native_address: 0x0818538c +Refc: 1 +=fun +Module: erl_scan +Uniq: 68725036 +Index: 16 +Address: 0xb59b9ac0 +Native_address: 0x08185374 +Refc: 1 +=fun +Module: global +Uniq: 52825859 +Index: 28 +Address: 0xb7d42f90 +Native_address: 0x08185384 +Refc: 1 +=fun +Module: inet_db +Uniq: 102636764 +Index: 9 +Address: 0xb7d32ae8 +Native_address: 0x08185384 +Refc: 1 +=fun +Module: erl_scan +Uniq: 67525631 +Index: 24 +Address: 0xb59b99e0 +Native_address: 0x08185374 +Refc: 1 +=fun +Module: inet_db +Uniq: 28404792 +Index: 0 +Address: 0xb7d33178 +Native_address: 0x08185384 +Refc: 1 +=fun +Module: erl_scan +Uniq: 124423443 +Index: 17 +Address: 0xb59b9aa4 +Native_address: 0x08185374 +Refc: 1 +=fun +Module: application_controller +Uniq: 58156454 +Index: 3 +Address: 0xb7d01538 +Native_address: 0x08185384 +Refc: 1 +=fun +Module: global +Uniq: 67899720 +Index: 17 +Address: 0xb7d43440 +Native_address: 0x0818538c +Refc: 2 +=fun +Module: erl_scan +Uniq: 129876024 +Index: 7 +Address: 0xb59b9bd8 +Native_address: 0x08185374 +Refc: 1 +=fun +Module: init +Uniq: 69742193 +Index: 0 +Address: 0xb7ca8ccc +Native_address: 0x08185384 +Refc: 1 +=fun +Module: erl_prim_loader +Uniq: 84727123 +Index: 3 +Address: 0xb7cda934 +Native_address: 0x0818538c +Refc: 1 +=fun +Module: inet_config +Uniq: 101215808 +Index: 1 +Address: 0xb7d4848c +Native_address: 0x08185384 +Refc: 1 +=fun +Module: dict +Uniq: 30564473 +Index: 9 +Address: 0xb7d152a4 +Native_address: 0x08185384 +Refc: 1 +=fun +Module: global +Uniq: 23788340 +Index: 5 +Address: 0xb7d43f94 +Native_address: 0x08185374 +Refc: 1 +=fun +Module: gen_event +Uniq: 102717583 +Index: 1 +Address: 0xb7ccfa9c +Native_address: 0x08185384 +Refc: 1 +=fun +Module: ets +Uniq: 108704202 +Index: 13 +Address: 0xb59705b0 +Native_address: 0x0818538c +Refc: 1 +=fun +Module: rpc +Uniq: 42921910 +Index: 1 +Address: 0xb7d25da0 +Native_address: 0x0818538c +Refc: 1 +=fun +Module: inet_gethost_native +Uniq: 132518442 +Index: 0 +Address: 0xb595ab48 +Native_address: 0x08185384 +Refc: 1 +=fun +Module: inet_db +Uniq: 19722950 +Index: 4 +Address: 0xb7d33108 +Native_address: 0x08185384 +Refc: 1 +=fun +Module: inet_parse +Uniq: 53954651 +Index: 9 +Address: 0xb7d59764 +Native_address: 0x08185384 +Refc: 1 +=fun +Module: global +Uniq: 8906517 +Index: 10 +Address: 0xb7d43c80 +Native_address: 0x0818538c +Refc: 1 +=fun +Module: application_controller +Uniq: 95831077 +Index: 10 +Address: 0xb7d010fc +Native_address: 0x08185384 +Refc: 1 +=fun +Module: ets +Uniq: 44629514 +Index: 9 +Address: 0xb5970aa8 +Native_address: 0x0818538c +Refc: 1 +=fun +Module: inet_config +Uniq: 113855098 +Index: 5 +Address: 0xb7d48324 +Native_address: 0x08185384 +Refc: 1 +=fun +Module: global +Uniq: 111125279 +Index: 29 +Address: 0xb7d436c4 +Native_address: 0x0818538c +Refc: 1 +=fun +Module: inet +Uniq: 17471892 +Index: 8 +Address: 0xb7d5028c +Native_address: 0x08185384 +Refc: 1 +=fun +Module: inet_db +Uniq: 129209946 +Index: 6 +Address: 0xb7d330d0 +Native_address: 0x08185384 +Refc: 1 +=fun +Module: application_controller +Uniq: 36891727 +Index: 16 +Address: 0xb7d00e04 +Native_address: 0x0818537c +Refc: 1 +=fun +Module: init +Uniq: 133016334 +Index: 5 +Address: 0xb7ca8960 +Native_address: 0x08185374 +Refc: 1 +=fun +Module: erl_scan +Uniq: 23550004 +Index: 31 +Address: 0xb59b9938 +Native_address: 0x08185374 +Refc: 1 +=fun +Module: inet_db +Uniq: 45465551 +Index: 12 +Address: 0xb7d326e0 +Native_address: 0x0818537c +Refc: 1 +=fun +Module: inet +Uniq: 84626036 +Index: 4 +Address: 0xb7d506e8 +Native_address: 0x0818537c +Refc: 1 +=fun +Module: gen_event +Uniq: 38922863 +Index: 0 +Address: 0xb7ccfbdc +Native_address: 0x08185384 +Refc: 1 +=fun +Module: application_master +Uniq: 129796060 +Index: 0 +Address: 0xb7d114b0 +Native_address: 0x08185384 +Refc: 1 +=fun +Module: application_controller +Uniq: 52249318 +Index: 8 +Address: 0xb7d0125c +Native_address: 0x0818537c +Refc: 1 +=fun +Module: init +Uniq: 85706310 +Index: 6 +Address: 0xb7ca87d0 +Native_address: 0x0818538c +Refc: 1 +=fun +Module: erl_scan +Uniq: 18971321 +Index: 32 +Address: 0xb59b9900 +Native_address: 0x08185374 +Refc: 1 +=fun +Module: inet_tcp_dist +Uniq: 14696506 +Index: 3 +Address: 0xb5964378 +Native_address: 0x08185384 +Refc: 1 +=fun +Module: inet_db +Uniq: 70952805 +Index: 13 +Address: 0xb7d326c4 +Native_address: 0x0818537c +Refc: 1 +=fun +Module: dict +Uniq: 3030396 +Index: 10 +Address: 0xb7d151e0 +Native_address: 0x08185374 +Refc: 1 +=fun +Module: supervisor +Uniq: 130140974 +Index: 0 +Address: 0xb7d1e020 +Native_address: 0x08185384 +Refc: 1 +=fun +Module: erl_prim_loader +Uniq: 41119430 +Index: 5 +Address: 0xb7cda7c4 +Native_address: 0x0818538c +Refc: 1 +=fun +Module: ets +Uniq: 118219916 +Index: 11 +Address: 0xb59709a0 +Native_address: 0x0818537c +Refc: 1 +=fun +Module: prim_zip +Uniq: 86382196 +Index: 4 +Address: 0xb7cb0ae4 +Native_address: 0x0818538c +Refc: 1 +=fun +Module: dict +Uniq: 116002459 +Index: 2 +Address: 0xb7d1540c +Native_address: 0x08185374 +Refc: 1 +=fun +Module: global +Uniq: 74480140 +Index: 18 +Address: 0xb7d43238 +Native_address: 0x0818537c +Refc: 1 +=fun +Module: global +Uniq: 128409020 +Index: 8 +Address: 0xb7d43d90 +Native_address: 0x08185384 +Refc: 1 +=fun +Module: global +Uniq: 121748382 +Index: 19 +Address: 0xb7d431ec +Native_address: 0x08185384 +Refc: 1 +=fun +Module: ets +Uniq: 80678962 +Index: 16 +Address: 0xb5970508 +Native_address: 0x08185384 +Refc: 1 +=fun +Module: application_controller +Uniq: 17241178 +Index: 17 +Address: 0xb7d00b40 +Native_address: 0x0818537c +Refc: 1 +=fun +Module: inet_db +Uniq: 11918804 +Index: 2 +Address: 0xb7d33140 +Native_address: 0x08185384 +Refc: 1 +=fun +Module: inet_config +Uniq: 9252528 +Index: 3 +Address: 0xb7d483a0 +Native_address: 0x08185384 +Refc: 1 +=fun +Module: ets +Uniq: 34024230 +Index: 5 +Address: 0xb5970bdc +Native_address: 0x0818538c +Refc: 1 +=fun +Module: application_controller +Uniq: 115433269 +Index: 2 +Address: 0xb7d0170c +Native_address: 0x08185384 +Refc: 1 +=fun +Module: inet_parse +Uniq: 34997756 +Index: 7 +Address: 0xb7d59894 +Native_address: 0x08185384 +Refc: 1 +=fun +Module: erl_scan +Uniq: 69182734 +Index: 21 +Address: 0xb59b9a34 +Native_address: 0x08185374 +Refc: 1 +=fun +Module: erl_scan +Uniq: 94759123 +Index: 36 +Address: 0xb59b9890 +Native_address: 0x08185374 +Refc: 1 +=fun +Module: application_controller +Uniq: 81956923 +Index: 18 +Address: 0xb7d00ac0 +Native_address: 0x0818537c +Refc: 1 +=fun +Module: global +Uniq: 54986005 +Index: 21 +Address: 0xb7d430a0 +Native_address: 0x0818538c +Refc: 1 +=fun +Module: application_controller +Uniq: 43155715 +Index: 11 +Address: 0xb7d010bc +Native_address: 0x08185384 +Refc: 1 +=fun +Module: erl_prim_loader +Uniq: 71285252 +Index: 9 +Address: 0xb7cda4b4 +Native_address: 0x0818538c +Refc: 1 +=fun +Module: application_controller +Uniq: 101429404 +Index: 7 +Address: 0xb7d012f4 +Native_address: 0x0818537c +Refc: 1 +=fun +Module: erl_scan +Uniq: 80476629 +Index: 35 +Address: 0xb59b98ac +Native_address: 0x08185374 +Refc: 1 +=fun +Module: dict +Uniq: 130844104 +Index: 7 +Address: 0xb7d1531c +Native_address: 0x08185384 +Refc: 1 +=fun +Module: global +Uniq: 70707981 +Index: 12 +Address: 0xb7d43c10 +Native_address: 0x0818538c +Refc: 1 +=fun +Module: inet_db +Uniq: 44790908 +Index: 1 +Address: 0xb7d3315c +Native_address: 0x08185384 +Refc: 1 +=fun +Module: global +Uniq: 51407555 +Index: 11 +Address: 0xb7d43c40 +Native_address: 0x08185384 +Refc: 1 +=fun +Module: dict +Uniq: 44420904 +Index: 3 +Address: 0xb7d153dc +Native_address: 0x08185384 +Refc: 1 +=fun +Module: global +Uniq: 114622525 +Index: 23 +Address: 0xb7d43030 +Native_address: 0x08185384 +Refc: 1 +=fun +Module: proc_lib +Uniq: 73600383 +Index: 0 +Address: 0xb7ce3568 +Native_address: 0x08185374 +Refc: 1 +=fun +Module: erl_scan +Uniq: 92959357 +Index: 33 +Address: 0xb59b98e4 +Native_address: 0x08185374 +Refc: 1 +=fun +Module: erl_scan +Uniq: 84957484 +Index: 1 +Address: 0xb59b9c64 +Native_address: 0x08185374 +Refc: 1 +=fun +Module: global +Uniq: 5043898 +Index: 14 +Address: 0xb7d437a8 +Native_address: 0x0818537c +Refc: 1 +=fun +Module: erl_prim_loader +Uniq: 97618852 +Index: 2 +Address: 0xb7cdaa00 +Native_address: 0x0818538c +Refc: 1 +=fun +Module: application_controller +Uniq: 27729446 +Index: 0 +Address: 0xb7d0186c +Native_address: 0x0818538c +Refc: 1 +=fun +Module: dict +Uniq: 14654623 +Index: 8 +Address: 0xb7d152d4 +Native_address: 0x08185384 +Refc: 1 +=fun +Module: ets +Uniq: 41458308 +Index: 14 +Address: 0xb5970554 +Native_address: 0x0818538c +Refc: 1 +=fun +Module: inet_parse +Uniq: 117938597 +Index: 8 +Address: 0xb7d59830 +Native_address: 0x08185384 +Refc: 1 +=fun +Module: supervisor +Uniq: 21363331 +Index: 2 +Address: 0xb7d1de48 +Native_address: 0x08185384 +Refc: 1 +=fun +Module: global +Uniq: 14238417 +Index: 13 +Address: 0xb7d43a0c +Native_address: 0x0818537c +Refc: 1 +=fun +Module: supervisor +Uniq: 111989307 +Index: 1 +Address: 0xb7d1dfa0 +Native_address: 0x08185384 +Refc: 1 +=fun +Module: application_controller +Uniq: 68378279 +Index: 13 +Address: 0xb7d00f1c +Native_address: 0x08185384 +Refc: 1 +=fun +Module: dict +Uniq: 33146997 +Index: 5 +Address: 0xb7d1537c +Native_address: 0x08185384 +Refc: 1 +=fun +Module: net_kernel +Uniq: 94578372 +Index: 6 +Address: 0xb598cc5c +Native_address: 0x08185384 +Refc: 1 +=fun +Module: inet_db +Uniq: 79415526 +Index: 8 +Address: 0xb7d33098 +Native_address: 0x08185384 +Refc: 1 +=fun +Module: inet_db +Uniq: 5499393 +Index: 10 +Address: 0xb7d32a30 +Native_address: 0x08185384 +Refc: 1 +=fun +Module: erl_scan +Uniq: 49600587 +Index: 9 +Address: 0xb59b9b68 +Native_address: 0x08185374 +Refc: 1 +=fun +Module: ets +Uniq: 30269907 +Index: 17 +Address: 0xb5970a34 +Native_address: 0x08185384 +Refc: 1 +=fun +Module: erl_scan +Uniq: 22626449 +Index: 5 +Address: 0xb59b9bbc +Native_address: 0x08185374 +Refc: 1 +=fun +Module: erl_scan +Uniq: 100174013 +Index: 4 +Address: 0xb59b9bf4 +Native_address: 0x08185374 +Refc: 1 +=fun +Module: net_kernel +Uniq: 131488678 +Index: 1 +Address: 0xb598ce28 +Native_address: 0x08185384 +Refc: 1 +=fun +Module: application_controller +Uniq: 125750610 +Index: 5 +Address: 0xb7d015d4 +Native_address: 0x08185384 +Refc: 1 +=fun +Module: erl_prim_loader +Uniq: 59207092 +Index: 10 +Address: 0xb7cda3f0 +Native_address: 0x0818538c +Refc: 1 +=fun +Module: global +Uniq: 125901911 +Index: 30 +Address: 0xb7d43d60 +Native_address: 0x08185384 +Refc: 1 +=fun +Module: init +Uniq: 7412831 +Index: 2 +Address: 0xb7ca8c94 +Native_address: 0x08185384 +Refc: 1 +=fun +Module: global +Uniq: 13074530 +Index: 3 +Address: 0xb7d44024 +Native_address: 0x08185374 +Refc: 1 +=fun +Module: erl_scan +Uniq: 41961618 +Index: 2 +Address: 0xb59b9c48 +Native_address: 0x0818537c +Refc: 1 +=fun +Module: global +Uniq: 8275055 +Index: 16 +Address: 0xb7d43684 +Native_address: 0x08185384 +Refc: 1 +=fun +Module: proc_lib +Uniq: 130404962 +Index: 1 +Address: 0xb7ce34e8 +Native_address: 0x0818537c +Refc: 1 +=fun +Module: inet_parse +Uniq: 42797026 +Index: 3 +Address: 0xb7d59b80 +Native_address: 0x08185384 +Refc: 1 +=fun +Module: inet_tcp_dist +Uniq: 7041088 +Index: 0 +Address: 0xb5964478 +Native_address: 0x0818537c +Refc: 1 +=fun +Module: global +Uniq: 116105487 +Index: 2 +Address: 0xb7d44040 +Native_address: 0x08185384 +Refc: 1 +=fun +Module: erl_scan +Uniq: 114798877 +Index: 37 +Address: 0xb59b9874 +Native_address: 0x08185374 +Refc: 1 +=fun +Module: erl_prim_loader +Uniq: 59294489 +Index: 1 +Address: 0xb7cdaacc +Native_address: 0x0818538c +Refc: 1 +=fun +Module: global +Uniq: 58108697 +Index: 26 +Address: 0xb7d42f30 +Native_address: 0x0818538c +Refc: 1 +=fun +Module: rpc +Uniq: 11877839 +Index: 3 +Address: 0xb7d25cb8 +Native_address: 0x0818538c +Refc: 1 +=fun +Module: erl_scan +Uniq: 43514321 +Index: 8 +Address: 0xb59b9b84 +Native_address: 0x08185374 +Refc: 1 +=fun +Module: erl_scan +Uniq: 104688400 +Index: 3 +Address: 0xb59b9c2c +Native_address: 0x08185384 +Refc: 1 +=fun +Module: ets +Uniq: 119734853 +Index: 12 +Address: 0xb597060c +Native_address: 0x08185384 +Refc: 1 +=fun +Module: global +Uniq: 93193124 +Index: 4 +Address: 0xb7d43fb0 +Native_address: 0x08185384 +Refc: 1 +=fun +Module: prim_zip +Uniq: 10697549 +Index: 1 +Address: 0xb7cb0b6c +Native_address: 0x0818537c +Refc: 1 +=fun +Module: global +Uniq: 130341580 +Index: 20 +Address: 0xb7d430d0 +Native_address: 0x0818538c +Refc: 1 +=fun +Module: erl_scan +Uniq: 30789222 +Index: 11 +Address: 0xb59b9b30 +Native_address: 0x08185374 +Refc: 1 +=fun +Module: inet_parse +Uniq: 99910734 +Index: 4 +Address: 0xb7d599ac +Native_address: 0x08185384 +Refc: 1 +=fun +Module: ets +Uniq: 116997047 +Index: 15 +Address: 0xb5970524 +Native_address: 0x0818538c +Refc: 1 +=fun +Module: net_kernel +Uniq: 95824103 +Index: 3 +Address: 0xb598cd2c +Native_address: 0x08185384 +Refc: 1 +=fun +Module: dict +Uniq: 112608439 +Index: 0 +Address: 0xb7d15480 +Native_address: 0x08185374 +Refc: 1 +=fun +Module: init +Uniq: 68514384 +Index: 1 +Address: 0xb7ca8cb0 +Native_address: 0x08185384 +Refc: 1 +=fun +Module: application_controller +Uniq: 76395899 +Index: 9 +Address: 0xb7d011ac +Native_address: 0x08185384 +Refc: 1 +=fun +Module: erl_prim_loader +Uniq: 73519217 +Index: 0 +Address: 0xb7cdaba8 +Native_address: 0x0818538c +Refc: 1 +=fun +Module: erl_scan +Uniq: 97143895 +Index: 19 +Address: 0xb59b9a6c +Native_address: 0x08185374 +Refc: 1 +=fun +Module: erl_scan +Uniq: 65981056 +Index: 27 +Address: 0xb59b99a8 +Native_address: 0x08185374 +Refc: 1 +=fun +Module: ets +Uniq: 65652154 +Index: 6 +Address: 0xb5970b98 +Native_address: 0x0818538c +Refc: 1 +=fun +Module: dict +Uniq: 110279503 +Index: 12 +Address: 0xb7d15198 +Native_address: 0x08185384 +Refc: 1 +=fun +Module: erl_prim_loader +Uniq: 15196858 +Index: 8 +Address: 0xb7cda578 +Native_address: 0x0818538c +Refc: 1 +=fun +Module: net_kernel +Uniq: 114066757 +Index: 4 +Address: 0xb598ccbc +Native_address: 0x08185384 +Refc: 1 +=fun +Module: net_kernel +Uniq: 126210494 +Index: 7 +Address: 0xb598cc40 +Native_address: 0x0818537c +Refc: 1 +=fun +Module: erl_scan +Uniq: 35656457 +Index: 26 +Address: 0xb59b998c +Native_address: 0x08185374 +Refc: 1 +=fun +Module: erl_prim_loader +Uniq: 14509926 +Index: 7 +Address: 0xb7cda63c +Native_address: 0x0818538c +Refc: 1 +=fun +Module: global +Uniq: 55254700 +Index: 6 +Address: 0xb7d43ec8 +Native_address: 0x08185384 +Refc: 1 +=fun +Module: dict +Uniq: 37088176 +Index: 6 +Address: 0xb7d1534c +Native_address: 0x08185384 +Refc: 1 +=fun +Module: ets +Uniq: 103868544 +Index: 10 +Address: 0xb5970a78 +Native_address: 0x08185384 +Refc: 1 +=fun +Module: erl_scan +Uniq: 131416581 +Index: 25 +Address: 0xb59b99c4 +Native_address: 0x08185374 +Refc: 1 +=fun +Module: inet_tcp_dist +Uniq: 86432818 +Index: 5 +Address: 0xb5964340 +Native_address: 0x0818537c +Refc: 1 +=fun +Module: inet_parse +Uniq: 101474882 +Index: 12 +Address: 0xb7d595c8 +Native_address: 0x08185384 +Refc: 1 +=fun +Module: net_kernel +Uniq: 105366315 +Index: 5 +Address: 0xb598cc8c +Native_address: 0x08185384 +Refc: 1 +=fun +Module: inet_db +Uniq: 15820861 +Index: 5 +Address: 0xb7d330ec +Native_address: 0x08185384 +Refc: 1 +=fun +Module: erlang +Uniq: 110526956 +Index: 0 +Address: 0xb7cb4c54 +Native_address: 0x0818537c +Refc: 1 +=fun +Module: erl_scan +Uniq: 44968448 +Index: 28 +Address: 0xb59b9970 +Native_address: 0x08185374 +Refc: 1 +=fun +Module: erl_scan +Uniq: 94397494 +Index: 12 +Address: 0xb59b9b14 +Native_address: 0x08185374 +Refc: 1 +=fun +Module: inet_gethost_native +Uniq: 48733929 +Index: 1 +Address: 0xb595a98c +Native_address: 0x08185384 +Refc: 1 +=fun +Module: inet +Uniq: 110685557 +Index: 7 +Address: 0xb7d504e8 +Native_address: 0x08185384 +Refc: 1 +=fun +Module: prim_zip +Uniq: 56267849 +Index: 2 +Address: 0xb7cb0b50 +Native_address: 0x0818537c +Refc: 1 +=fun +Module: global +Uniq: 65323168 +Index: 9 +Address: 0xb7d43d00 +Native_address: 0x0818537c +Refc: 1 +=fun +Module: supervisor +Uniq: 126835098 +Index: 3 +Address: 0xb7d1ddf4 +Native_address: 0x08185384 +Refc: 1 +=fun +Module: inet_tcp_dist +Uniq: 73314166 +Index: 1 +Address: 0xb596445c +Native_address: 0x08185374 +Refc: 1 +=fun +Module: inet_parse +Uniq: 60713317 +Index: 2 +Address: 0xb7d59c2c +Native_address: 0x08185384 +Refc: 1 +=fun +Module: inet_parse +Uniq: 41624009 +Index: 0 +Address: 0xb7d59df8 +Native_address: 0x08185384 +Refc: 1 +=proc_messages:<0.0.0> +HB58DAAD8:N +=proc_stack:<0.0.0> +0xb58dd9b8:SReturn addr 0xB7CA28B8 (init:boot_loop/2 + 1976) +0xb58dd9bc:SReturn addr 0x8248774 () +y0:N +y1:HB58DA850 +y2:P<0.1.0> +=proc_heap:<0.0.0> +B58DA850:tA:A5:state,HB7B464C8,N,HB7B464D0,HB7B464D8,P<0.1.0>,HB7B464E0,HB7B464EC,HB58DA830,N +B58DA830:lHB58DA824|HB58DA4F0 +B58DA824:t2:A8:erl_scan,HB58DA7E0 +B58DA7E0:lI47|HB58DA7D8 +B58DA7D8:lI117|HB58DA7D0 +B58DA7D0:lI115|HB58DA7C8 +B58DA7C8:lI114|HB58DA7C0 +B58DA7C0:lI47|HB58DA7B8 +B58DA7B8:lI108|HB58DA7B0 +B58DA7B0:lI111|HB58DA7A8 +B58DA7A8:lI99|HB58DA7A0 +B58DA7A0:lI97|HB58DA798 +B58DA798:lI108|HB58DA790 +B58DA790:lI47|HB58DA788 +B58DA788:lI108|HB58DA780 +B58DA780:lI105|HB58DA778 +B58DA778:lI98|HB58DA770 +B58DA770:lI47|HB58DA768 +B58DA768:lI101|HB58DA760 +B58DA760:lI114|HB58DA758 +B58DA758:lI108|HB58DA750 +B58DA750:lI97|HB58DA748 +B58DA748:lI110|HB58DA740 +B58DA740:lI103|HB58DA738 +B58DA738:lI47|HB58DA730 +B58DA730:lI108|HB58DA728 +B58DA728:lI105|HB58DA720 +B58DA720:lI98|HB58DA718 +B58DA718:lI47|HB58DA710 +B58DA710:lI115|HB58DA708 +B58DA708:lI116|HB58DA700 +B58DA700:lI100|HB58DA6F8 +B58DA6F8:lI108|HB58DA6F0 +B58DA6F0:lI105|HB58DA6E8 +B58DA6E8:lI98|HB58DA6E0 +B58DA6E0:lI45|HB58DA6D8 +B58DA6D8:lI49|HB58DA6D0 +B58DA6D0:lI46|HB58DA6C8 +B58DA6C8:lI49|HB58DA6C0 +B58DA6C0:lI54|HB58DA6B8 +B58DA6B8:lI46|HB58DA6B0 +B58DA6B0:lI51|HB58DA6A8 +B58DA6A8:lI47|HB58DA6A0 +B58DA6A0:lI101|HB58DA698 +B58DA698:lI98|HB58DA690 +B58DA690:lI105|HB58DA688 +B58DA688:lI110|HB58DA680 +B58DA680:lI47|HB58DA678 +B58DA678:lI101|HB58DA670 +B58DA670:lI114|HB58DA668 +B58DA668:lI108|HB58DA660 +B58DA660:lI95|HB58DA658 +B58DA658:lI115|HB58DA650 +B58DA650:lI99|HB58DA648 +B58DA648:lI97|HB58DA640 +B58DA640:lI110|HB58DA638 +B58DA638:lI46|HB58DA630 +B58DA630:lI98|HB58DA628 +B58DA628:lI101|HB58DA620 +B58DA620:lI97|HB58DA618 +B58DA618:lI109|N +B58DA4F0:lHB58DA4E4|HB58DA1E0 +B58DA4E4:t2:A6:io_lib,HB58DA4A0 +B58DA4A0:lI47|HB58DA498 +B58DA498:lI117|HB58DA490 +B58DA490:lI115|HB58DA488 +B58DA488:lI114|HB58DA480 +B58DA480:lI47|HB58DA478 +B58DA478:lI108|HB58DA470 +B58DA470:lI111|HB58DA468 +B58DA468:lI99|HB58DA460 +B58DA460:lI97|HB58DA458 +B58DA458:lI108|HB58DA450 +B58DA450:lI47|HB58DA448 +B58DA448:lI108|HB58DA440 +B58DA440:lI105|HB58DA438 +B58DA438:lI98|HB58DA430 +B58DA430:lI47|HB58DA428 +B58DA428:lI101|HB58DA420 +B58DA420:lI114|HB58DA418 +B58DA418:lI108|HB58DA410 +B58DA410:lI97|HB58DA408 +B58DA408:lI110|HB58DA400 +B58DA400:lI103|HB58DA3F8 +B58DA3F8:lI47|HB58DA3F0 +B58DA3F0:lI108|HB58DA3E8 +B58DA3E8:lI105|HB58DA3E0 +B58DA3E0:lI98|HB58DA3D8 +B58DA3D8:lI47|HB58DA3D0 +B58DA3D0:lI115|HB58DA3C8 +B58DA3C8:lI116|HB58DA3C0 +B58DA3C0:lI100|HB58DA3B8 +B58DA3B8:lI108|HB58DA3B0 +B58DA3B0:lI105|HB58DA3A8 +B58DA3A8:lI98|HB58DA3A0 +B58DA3A0:lI45|HB58DA398 +B58DA398:lI49|HB58DA390 +B58DA390:lI46|HB58DA388 +B58DA388:lI49|HB58DA380 +B58DA380:lI54|HB58DA378 +B58DA378:lI46|HB58DA370 +B58DA370:lI51|HB58DA368 +B58DA368:lI47|HB58DA360 +B58DA360:lI101|HB58DA358 +B58DA358:lI98|HB58DA350 +B58DA350:lI105|HB58DA348 +B58DA348:lI110|HB58DA340 +B58DA340:lI47|HB58DA338 +B58DA338:lI105|HB58DA330 +B58DA330:lI111|HB58DA328 +B58DA328:lI95|HB58DA320 +B58DA320:lI108|HB58DA318 +B58DA318:lI105|HB58DA310 +B58DA310:lI98|HB58DA308 +B58DA308:lI46|HB58DA300 +B58DA300:lI98|HB58DA2F8 +B58DA2F8:lI101|HB58DA2F0 +B58DA2F0:lI97|HB58DA2E8 +B58DA2E8:lI109|N +B58DA1E0:lHB58DA1D4|HB58D9648 +B58DA1D4:t2:A7:gen_tcp,HB58DA190 +B58DA190:lI47|HB58DA188 +B58DA188:lI117|HB58DA180 +B58DA180:lI115|HB58DA178 +B58DA178:lI114|HB58DA170 +B58DA170:lI47|HB58DA168 +B58DA168:lI108|HB58DA160 +B58DA160:lI111|HB58DA158 +B58DA158:lI99|HB58DA150 +B58DA150:lI97|HB58DA148 +B58DA148:lI108|HB58DA140 +B58DA140:lI47|HB58DA138 +B58DA138:lI108|HB58DA130 +B58DA130:lI105|HB58DA128 +B58DA128:lI98|HB58DA120 +B58DA120:lI47|HB58DA118 +B58DA118:lI101|HB58DA110 +B58DA110:lI114|HB58DA108 +B58DA108:lI108|HB58DA100 +B58DA100:lI97|HB58DA0F8 +B58DA0F8:lI110|HB58DA0F0 +B58DA0F0:lI103|HB58DA0E8 +B58DA0E8:lI47|HB58DA0E0 +B58DA0E0:lI108|HB58DA0D8 +B58DA0D8:lI105|HB58DA0D0 +B58DA0D0:lI98|HB58DA0C8 +B58DA0C8:lI47|HB58DA0C0 +B58DA0C0:lI107|HB58DA0B8 +B58DA0B8:lI101|HB58DA0B0 +B58DA0B0:lI114|HB58DA0A8 +B58DA0A8:lI110|HB58DA0A0 +B58DA0A0:lI101|HB58DA098 +B58DA098:lI108|HB58DA090 +B58DA090:lI45|HB58DA088 +B58DA088:lI50|HB58DA080 +B58DA080:lI46|HB58DA078 +B58DA078:lI49|HB58DA070 +B58DA070:lI51|HB58DA068 +B58DA068:lI46|HB58DA060 +B58DA060:lI51|HB58DA058 +B58DA058:lI47|HB58DA050 +B58DA050:lI101|HB58DA048 +B58DA048:lI98|HB58DA040 +B58DA040:lI105|HB58DA038 +B58DA038:lI110|HB58DA030 +B58DA030:lI47|HB58DA028 +B58DA028:lI103|HB58DA020 +B58DA020:lI101|HB58DA018 +B58DA018:lI110|HB58DA010 +B58DA010:lI95|HB58DA008 +B58DA008:lI116|HB58DA000 +B58DA000:lI99|HB58D9FF8 +B58D9FF8:lI112|HB58D9FF0 +B58D9FF0:lI46|HB58D9FE8 +B58D9FE8:lI98|HB58D9FE0 +B58D9FE0:lI101|HB58D9FD8 +B58D9FD8:lI97|HB58D9FD0 +B58D9FD0:lI109|N +B58D9648:lHB58D963C|HB58D71BC +B58D963C:t2:A8:inet_tcp,HB58D95F8 +B58D95F8:lI47|HB58D95F0 +B58D95F0:lI117|HB58D95E8 +B58D95E8:lI115|HB58D95E0 +B58D95E0:lI114|HB58D95D8 +B58D95D8:lI47|HB58D95D0 +B58D95D0:lI108|HB58D95C8 +B58D95C8:lI111|HB58D95C0 +B58D95C0:lI99|HB58D95B8 +B58D95B8:lI97|HB58D95B0 +B58D95B0:lI108|HB58D95A8 +B58D95A8:lI47|HB58D95A0 +B58D95A0:lI108|HB58D9598 +B58D9598:lI105|HB58D9590 +B58D9590:lI98|HB58D9588 +B58D9588:lI47|HB58D9580 +B58D9580:lI101|HB58D9578 +B58D9578:lI114|HB58D9570 +B58D9570:lI108|HB58D9568 +B58D9568:lI97|HB58D9560 +B58D9560:lI110|HB58D9558 +B58D9558:lI103|HB58D9550 +B58D9550:lI47|HB58D9548 +B58D9548:lI108|HB58D9540 +B58D9540:lI105|HB58D9538 +B58D9538:lI98|HB58D9530 +B58D9530:lI47|HB58D9528 +B58D9528:lI107|HB58D9520 +B58D9520:lI101|HB58D9518 +B58D9518:lI114|HB58D9510 +B58D9510:lI110|HB58D9508 +B58D9508:lI101|HB58D9500 +B58D9500:lI108|HB58D94F8 +B58D94F8:lI45|HB58D94F0 +B58D94F0:lI50|HB58D94E8 +B58D94E8:lI46|HB58D94E0 +B58D94E0:lI49|HB58D94D8 +B58D94D8:lI51|HB58D94D0 +B58D94D0:lI46|HB58D94C8 +B58D94C8:lI51|HB58D94C0 +B58D94C0:lI47|HB58D94B8 +B58D94B8:lI101|HB58D94B0 +B58D94B0:lI98|HB58D94A8 +B58D94A8:lI105|HB58D94A0 +B58D94A0:lI110|HB58D9498 +B58D9498:lI47|HB58D9490 +B58D9490:lI105|HB58D9488 +B58D9488:lI110|HB58D9480 +B58D9480:lI101|HB58D9478 +B58D9478:lI116|HB58D9470 +B58D9470:lI95|HB58D9468 +B58D9468:lI116|HB58D9460 +B58D9460:lI99|HB58D9458 +B58D9458:lI112|HB58D9450 +B58D9450:lI46|HB58D9448 +B58D9448:lI98|HB58D9440 +B58D9440:lI101|HB58D9438 +B58D9438:lI97|HB58D9430 +B58D9430:lI109|N +B58D71BC:lHB58D71EC|HB58D71F8 +B58D71EC:t2:A4:file,HB58D7228 +B58D7228:lI47|HB58D7264 +B58D7264:lI117|HB58D72A0 +B58D72A0:lI115|HB58D72E4 +B58D72E4:lI114|HB58D7330 +B58D7330:lI47|HB58D7384 +B58D7384:lI108|HB58D73E0 +B58D73E0:lI111|HB58D7444 +B58D7444:lI99|HB58D74B0 +B58D74B0:lI97|HB58D751C +B58D751C:lI108|HB58D7584 +B58D7584:lI47|HB58D75EC +B58D75EC:lI108|HB58D7644 +B58D7644:lI105|HB58D769C +B58D769C:lI98|HB58D76F4 +B58D76F4:lI47|HB58D774C +B58D774C:lI101|HB58D77A4 +B58D77A4:lI114|HB58D77FC +B58D77FC:lI108|HB58D7854 +B58D7854:lI97|HB58D78A4 +B58D78A4:lI110|HB58D78F4 +B58D78F4:lI103|HB58D7944 +B58D7944:lI47|HB58D7994 +B58D7994:lI108|HB58D79E4 +B58D79E4:lI105|HB58D7A34 +B58D7A34:lI98|HB58D7A84 +B58D7A84:lI47|HB58D7AD4 +B58D7AD4:lI107|HB58D7B24 +B58D7B24:lI101|HB58D7B74 +B58D7B74:lI114|HB58D7BC4 +B58D7BC4:lI110|HB58D7C14 +B58D7C14:lI101|HB58D7C64 +B58D7C64:lI108|HB58D7CB4 +B58D7CB4:lI45|HB58D7D04 +B58D7D04:lI50|HB58D7D54 +B58D7D54:lI46|HB58D7DA4 +B58D7DA4:lI49|HB58D7DF4 +B58D7DF4:lI51|HB58D7E44 +B58D7E44:lI46|HB58D7E94 +B58D7E94:lI51|HB58D7EE4 +B58D7EE4:lI47|HB58D7F34 +B58D7F34:lI101|HB58D7F84 +B58D7F84:lI98|HB58D7FD4 +B58D7FD4:lI105|HB58D8024 +B58D8024:lI110|HB58D8074 +B58D8074:lI47|HB58D80C4 +B58D80C4:lI102|HB58D8114 +B58D8114:lI105|HB58D8164 +B58D8164:lI108|HB58D81B4 +B58D81B4:lI101|HB58D8204 +B58D8204:lI46|HB58D8254 +B58D8254:lI98|HB58D82A4 +B58D82A4:lI101|HB58D82F4 +B58D82F4:lI97|HB58D8344 +B58D8344:lI109|N +B58D71F8:lHB58D7230|HB58D723C +B58D7230:t2:A4:auth,HB58D726C +B58D726C:lI47|HB58D72A8 +B58D72A8:lI117|HB58D72EC +B58D72EC:lI115|HB58D7338 +B58D7338:lI114|HB58D738C +B58D738C:lI47|HB58D73E8 +B58D73E8:lI108|HB58D744C +B58D744C:lI111|HB58D74B8 +B58D74B8:lI99|HB58D7524 +B58D7524:lI97|HB58D758C +B58D758C:lI108|HB58D75F4 +B58D75F4:lI47|HB58D764C +B58D764C:lI108|HB58D76A4 +B58D76A4:lI105|HB58D76FC +B58D76FC:lI98|HB58D7754 +B58D7754:lI47|HB58D77AC +B58D77AC:lI101|HB58D7804 +B58D7804:lI114|HB58D785C +B58D785C:lI108|HB58D78AC +B58D78AC:lI97|HB58D78FC +B58D78FC:lI110|HB58D794C +B58D794C:lI103|HB58D799C +B58D799C:lI47|HB58D79EC +B58D79EC:lI108|HB58D7A3C +B58D7A3C:lI105|HB58D7A8C +B58D7A8C:lI98|HB58D7ADC +B58D7ADC:lI47|HB58D7B2C +B58D7B2C:lI107|HB58D7B7C +B58D7B7C:lI101|HB58D7BCC +B58D7BCC:lI114|HB58D7C1C +B58D7C1C:lI110|HB58D7C6C +B58D7C6C:lI101|HB58D7CBC +B58D7CBC:lI108|HB58D7D0C +B58D7D0C:lI45|HB58D7D5C +B58D7D5C:lI50|HB58D7DAC +B58D7DAC:lI46|HB58D7DFC +B58D7DFC:lI49|HB58D7E4C +B58D7E4C:lI51|HB58D7E9C +B58D7E9C:lI46|HB58D7EEC +B58D7EEC:lI51|HB58D7F3C +B58D7F3C:lI47|HB58D7F8C +B58D7F8C:lI101|HB58D7FDC +B58D7FDC:lI98|HB58D802C +B58D802C:lI105|HB58D807C +B58D807C:lI110|HB58D80CC +B58D80CC:lI47|HB58D811C +B58D811C:lI97|HB58D816C +B58D816C:lI117|HB58D81BC +B58D81BC:lI116|HB58D820C +B58D820C:lI104|HB58D825C +B58D825C:lI46|HB58D82AC +B58D82AC:lI98|HB58D82FC +B58D82FC:lI101|HB58D834C +B58D834C:lI97|HB58D8394 +B58D8394:lI109|N +B58D723C:lHB58D7274|HB58D7280 +B58D7274:t2:A8:erl_epmd,HB58D72B0 +B58D72B0:lI47|HB58D72F4 +B58D72F4:lI117|HB58D7340 +B58D7340:lI115|HB58D7394 +B58D7394:lI114|HB58D73F0 +B58D73F0:lI47|HB58D7454 +B58D7454:lI108|HB58D74C0 +B58D74C0:lI111|HB58D752C +B58D752C:lI99|HB58D7594 +B58D7594:lI97|HB58D75FC +B58D75FC:lI108|HB58D7654 +B58D7654:lI47|HB58D76AC +B58D76AC:lI108|HB58D7704 +B58D7704:lI105|HB58D775C +B58D775C:lI98|HB58D77B4 +B58D77B4:lI47|HB58D780C +B58D780C:lI101|HB58D7864 +B58D7864:lI114|HB58D78B4 +B58D78B4:lI108|HB58D7904 +B58D7904:lI97|HB58D7954 +B58D7954:lI110|HB58D79A4 +B58D79A4:lI103|HB58D79F4 +B58D79F4:lI47|HB58D7A44 +B58D7A44:lI108|HB58D7A94 +B58D7A94:lI105|HB58D7AE4 +B58D7AE4:lI98|HB58D7B34 +B58D7B34:lI47|HB58D7B84 +B58D7B84:lI107|HB58D7BD4 +B58D7BD4:lI101|HB58D7C24 +B58D7C24:lI114|HB58D7C74 +B58D7C74:lI110|HB58D7CC4 +B58D7CC4:lI101|HB58D7D14 +B58D7D14:lI108|HB58D7D64 +B58D7D64:lI45|HB58D7DB4 +B58D7DB4:lI50|HB58D7E04 +B58D7E04:lI46|HB58D7E54 +B58D7E54:lI49|HB58D7EA4 +B58D7EA4:lI51|HB58D7EF4 +B58D7EF4:lI46|HB58D7F44 +B58D7F44:lI51|HB58D7F94 +B58D7F94:lI47|HB58D7FE4 +B58D7FE4:lI101|HB58D8034 +B58D8034:lI98|HB58D8084 +B58D8084:lI105|HB58D80D4 +B58D80D4:lI110|HB58D8124 +B58D8124:lI47|HB58D8174 +B58D8174:lI101|HB58D81C4 +B58D81C4:lI114|HB58D8214 +B58D8214:lI108|HB58D8264 +B58D8264:lI95|HB58D82B4 +B58D82B4:lI101|HB58D8304 +B58D8304:lI112|HB58D8354 +B58D8354:lI109|HB58D839C +B58D839C:lI100|HB58D83DC +B58D83DC:lI46|HB58D841C +B58D841C:lI98|HB58D845C +B58D845C:lI101|HB58D849C +B58D849C:lI97|HB58D84DC +B58D84DC:lI109|N +B58D7280:lHB58D72B8|HB58D72C4 +B58D72B8:t2:AD:inet_tcp_dist,HB58D72FC +B58D72FC:lI47|HB58D7348 +B58D7348:lI117|HB58D739C +B58D739C:lI115|HB58D73F8 +B58D73F8:lI114|HB58D745C +B58D745C:lI47|HB58D74C8 +B58D74C8:lI108|HB58D7534 +B58D7534:lI111|HB58D759C +B58D759C:lI99|HB58D7604 +B58D7604:lI97|HB58D765C +B58D765C:lI108|HB58D76B4 +B58D76B4:lI47|HB58D770C +B58D770C:lI108|HB58D7764 +B58D7764:lI105|HB58D77BC +B58D77BC:lI98|HB58D7814 +B58D7814:lI47|HB58D786C +B58D786C:lI101|HB58D78BC +B58D78BC:lI114|HB58D790C +B58D790C:lI108|HB58D795C +B58D795C:lI97|HB58D79AC +B58D79AC:lI110|HB58D79FC +B58D79FC:lI103|HB58D7A4C +B58D7A4C:lI47|HB58D7A9C +B58D7A9C:lI108|HB58D7AEC +B58D7AEC:lI105|HB58D7B3C +B58D7B3C:lI98|HB58D7B8C +B58D7B8C:lI47|HB58D7BDC +B58D7BDC:lI107|HB58D7C2C +B58D7C2C:lI101|HB58D7C7C +B58D7C7C:lI114|HB58D7CCC +B58D7CCC:lI110|HB58D7D1C +B58D7D1C:lI101|HB58D7D6C +B58D7D6C:lI108|HB58D7DBC +B58D7DBC:lI45|HB58D7E0C +B58D7E0C:lI50|HB58D7E5C +B58D7E5C:lI46|HB58D7EAC +B58D7EAC:lI49|HB58D7EFC +B58D7EFC:lI51|HB58D7F4C +B58D7F4C:lI46|HB58D7F9C +B58D7F9C:lI51|HB58D7FEC +B58D7FEC:lI47|HB58D803C +B58D803C:lI101|HB58D808C +B58D808C:lI98|HB58D80DC +B58D80DC:lI105|HB58D812C +B58D812C:lI110|HB58D817C +B58D817C:lI47|HB58D81CC +B58D81CC:lI105|HB58D821C +B58D821C:lI110|HB58D826C +B58D826C:lI101|HB58D82BC +B58D82BC:lI116|HB58D830C +B58D830C:lI95|HB58D835C +B58D835C:lI116|HB58D83A4 +B58D83A4:lI99|HB58D83E4 +B58D83E4:lI112|HB58D8424 +B58D8424:lI95|HB58D8464 +B58D8464:lI100|HB58D84A4 +B58D84A4:lI105|HB58D84E4 +B58D84E4:lI115|HB58D851C +B58D851C:lI116|HB58D854C +B58D854C:lI46|HB58D857C +B58D857C:lI98|HB58D85A4 +B58D85A4:lI101|HB58D85CC +B58D85CC:lI97|HB58D85EC +B58D85EC:lI109|N +B58D72C4:lHB58D7304|HB58D7310 +B58D7304:t2:AA:net_kernel,HB58D7350 +B58D7350:lI47|HB58D73A4 +B58D73A4:lI117|HB58D7400 +B58D7400:lI115|HB58D7464 +B58D7464:lI114|HB58D74D0 +B58D74D0:lI47|HB58D753C +B58D753C:lI108|HB58D75A4 +B58D75A4:lI111|HB58D760C +B58D760C:lI99|HB58D7664 +B58D7664:lI97|HB58D76BC +B58D76BC:lI108|HB58D7714 +B58D7714:lI47|HB58D776C +B58D776C:lI108|HB58D77C4 +B58D77C4:lI105|HB58D781C +B58D781C:lI98|HB58D7874 +B58D7874:lI47|HB58D78C4 +B58D78C4:lI101|HB58D7914 +B58D7914:lI114|HB58D7964 +B58D7964:lI108|HB58D79B4 +B58D79B4:lI97|HB58D7A04 +B58D7A04:lI110|HB58D7A54 +B58D7A54:lI103|HB58D7AA4 +B58D7AA4:lI47|HB58D7AF4 +B58D7AF4:lI108|HB58D7B44 +B58D7B44:lI105|HB58D7B94 +B58D7B94:lI98|HB58D7BE4 +B58D7BE4:lI47|HB58D7C34 +B58D7C34:lI107|HB58D7C84 +B58D7C84:lI101|HB58D7CD4 +B58D7CD4:lI114|HB58D7D24 +B58D7D24:lI110|HB58D7D74 +B58D7D74:lI101|HB58D7DC4 +B58D7DC4:lI108|HB58D7E14 +B58D7E14:lI45|HB58D7E64 +B58D7E64:lI50|HB58D7EB4 +B58D7EB4:lI46|HB58D7F04 +B58D7F04:lI49|HB58D7F54 +B58D7F54:lI51|HB58D7FA4 +B58D7FA4:lI46|HB58D7FF4 +B58D7FF4:lI51|HB58D8044 +B58D8044:lI47|HB58D8094 +B58D8094:lI101|HB58D80E4 +B58D80E4:lI98|HB58D8134 +B58D8134:lI105|HB58D8184 +B58D8184:lI110|HB58D81D4 +B58D81D4:lI47|HB58D8224 +B58D8224:lI110|HB58D8274 +B58D8274:lI101|HB58D82C4 +B58D82C4:lI116|HB58D8314 +B58D8314:lI95|HB58D8364 +B58D8364:lI107|HB58D83AC +B58D83AC:lI101|HB58D83EC +B58D83EC:lI114|HB58D842C +B58D842C:lI110|HB58D846C +B58D846C:lI101|HB58D84AC +B58D84AC:lI108|HB58D84EC +B58D84EC:lI46|HB58D8524 +B58D8524:lI98|HB58D8554 +B58D8554:lI101|HB58D8584 +B58D8584:lI97|HB58D85AC +B58D85AC:lI109|N +B58D7310:lHB58D7358|HB58D7364 +B58D7358:t2:A10:erl_distribution,HB58D73AC +B58D73AC:lI47|HB58D7408 +B58D7408:lI117|HB58D746C +B58D746C:lI115|HB58D74D8 +B58D74D8:lI114|HB58D7544 +B58D7544:lI47|HB58D75AC +B58D75AC:lI108|HB58D7614 +B58D7614:lI111|HB58D766C +B58D766C:lI99|HB58D76C4 +B58D76C4:lI97|HB58D771C +B58D771C:lI108|HB58D7774 +B58D7774:lI47|HB58D77CC +B58D77CC:lI108|HB58D7824 +B58D7824:lI105|HB58D787C +B58D787C:lI98|HB58D78CC +B58D78CC:lI47|HB58D791C +B58D791C:lI101|HB58D796C +B58D796C:lI114|HB58D79BC +B58D79BC:lI108|HB58D7A0C +B58D7A0C:lI97|HB58D7A5C +B58D7A5C:lI110|HB58D7AAC +B58D7AAC:lI103|HB58D7AFC +B58D7AFC:lI47|HB58D7B4C +B58D7B4C:lI108|HB58D7B9C +B58D7B9C:lI105|HB58D7BEC +B58D7BEC:lI98|HB58D7C3C +B58D7C3C:lI47|HB58D7C8C +B58D7C8C:lI107|HB58D7CDC +B58D7CDC:lI101|HB58D7D2C +B58D7D2C:lI114|HB58D7D7C +B58D7D7C:lI110|HB58D7DCC +B58D7DCC:lI101|HB58D7E1C +B58D7E1C:lI108|HB58D7E6C +B58D7E6C:lI45|HB58D7EBC +B58D7EBC:lI50|HB58D7F0C +B58D7F0C:lI46|HB58D7F5C +B58D7F5C:lI49|HB58D7FAC +B58D7FAC:lI51|HB58D7FFC +B58D7FFC:lI46|HB58D804C +B58D804C:lI51|HB58D809C +B58D809C:lI47|HB58D80EC +B58D80EC:lI101|HB58D813C +B58D813C:lI98|HB58D818C +B58D818C:lI105|HB58D81DC +B58D81DC:lI110|HB58D822C +B58D822C:lI47|HB58D827C +B58D827C:lI101|HB58D82CC +B58D82CC:lI114|HB58D831C +B58D831C:lI108|HB58D836C +B58D836C:lI95|HB58D83B4 +B58D83B4:lI100|HB58D83F4 +B58D83F4:lI105|HB58D8434 +B58D8434:lI115|HB58D8474 +B58D8474:lI116|HB58D84B4 +B58D84B4:lI114|HB58D84F4 +B58D84F4:lI105|HB58D852C +B58D852C:lI98|HB58D855C +B58D855C:lI117|HB58D858C +B58D858C:lI116|HB58D85B4 +B58D85B4:lI105|HB58D85D4 +B58D85D4:lI111|HB58D85F4 +B58D85F4:lI110|HB58D860C +B58D860C:lI46|HB58D8624 +B58D8624:lI98|HB58D8634 +B58D8634:lI101|HB58D8644 +B58D8644:lI97|HB58D8654 +B58D8654:lI109|N +B58D7364:lHB58D73B4|HB58D73C0 +B58D73B4:t2:A6:string,HB58D7410 +B58D7410:lI47|HB58D7474 +B58D7474:lI117|HB58D74E0 +B58D74E0:lI115|HB58D754C +B58D754C:lI114|HB58D75B4 +B58D75B4:lI47|HB58D761C +B58D761C:lI108|HB58D7674 +B58D7674:lI111|HB58D76CC +B58D76CC:lI99|HB58D7724 +B58D7724:lI97|HB58D777C +B58D777C:lI108|HB58D77D4 +B58D77D4:lI47|HB58D782C +B58D782C:lI108|HB58D7884 +B58D7884:lI105|HB58D78D4 +B58D78D4:lI98|HB58D7924 +B58D7924:lI47|HB58D7974 +B58D7974:lI101|HB58D79C4 +B58D79C4:lI114|HB58D7A14 +B58D7A14:lI108|HB58D7A64 +B58D7A64:lI97|HB58D7AB4 +B58D7AB4:lI110|HB58D7B04 +B58D7B04:lI103|HB58D7B54 +B58D7B54:lI47|HB58D7BA4 +B58D7BA4:lI108|HB58D7BF4 +B58D7BF4:lI105|HB58D7C44 +B58D7C44:lI98|HB58D7C94 +B58D7C94:lI47|HB58D7CE4 +B58D7CE4:lI115|HB58D7D34 +B58D7D34:lI116|HB58D7D84 +B58D7D84:lI100|HB58D7DD4 +B58D7DD4:lI108|HB58D7E24 +B58D7E24:lI105|HB58D7E74 +B58D7E74:lI98|HB58D7EC4 +B58D7EC4:lI45|HB58D7F14 +B58D7F14:lI49|HB58D7F64 +B58D7F64:lI46|HB58D7FB4 +B58D7FB4:lI49|HB58D8004 +B58D8004:lI54|HB58D8054 +B58D8054:lI46|HB58D80A4 +B58D80A4:lI51|HB58D80F4 +B58D80F4:lI47|HB58D8144 +B58D8144:lI101|HB58D8194 +B58D8194:lI98|HB58D81E4 +B58D81E4:lI105|HB58D8234 +B58D8234:lI110|HB58D8284 +B58D8284:lI47|HB58D82D4 +B58D82D4:lI115|HB58D8324 +B58D8324:lI116|HB58D8374 +B58D8374:lI114|HB58D83BC +B58D83BC:lI105|HB58D83FC +B58D83FC:lI110|HB58D843C +B58D843C:lI103|HB58D847C +B58D847C:lI46|HB58D84BC +B58D84BC:lI98|HB58D84FC +B58D84FC:lI101|HB58D8534 +B58D8534:lI97|HB58D8564 +B58D8564:lI109|N +B58D73C0:lHB58D7418|HB58D7424 +B58D7418:t2:A3:ets,HB58D747C +B58D747C:lI47|HB58D74E8 +B58D74E8:lI117|HB58D7554 +B58D7554:lI115|HB58D75BC +B58D75BC:lI114|HB58D7624 +B58D7624:lI47|HB58D767C +B58D767C:lI108|HB58D76D4 +B58D76D4:lI111|HB58D772C +B58D772C:lI99|HB58D7784 +B58D7784:lI97|HB58D77DC +B58D77DC:lI108|HB58D7834 +B58D7834:lI47|HB58D788C +B58D788C:lI108|HB58D78DC +B58D78DC:lI105|HB58D792C +B58D792C:lI98|HB58D797C +B58D797C:lI47|HB58D79CC +B58D79CC:lI101|HB58D7A1C +B58D7A1C:lI114|HB58D7A6C +B58D7A6C:lI108|HB58D7ABC +B58D7ABC:lI97|HB58D7B0C +B58D7B0C:lI110|HB58D7B5C +B58D7B5C:lI103|HB58D7BAC +B58D7BAC:lI47|HB58D7BFC +B58D7BFC:lI108|HB58D7C4C +B58D7C4C:lI105|HB58D7C9C +B58D7C9C:lI98|HB58D7CEC +B58D7CEC:lI47|HB58D7D3C +B58D7D3C:lI115|HB58D7D8C +B58D7D8C:lI116|HB58D7DDC +B58D7DDC:lI100|HB58D7E2C +B58D7E2C:lI108|HB58D7E7C +B58D7E7C:lI105|HB58D7ECC +B58D7ECC:lI98|HB58D7F1C +B58D7F1C:lI45|HB58D7F6C +B58D7F6C:lI49|HB58D7FBC +B58D7FBC:lI46|HB58D800C +B58D800C:lI49|HB58D805C +B58D805C:lI54|HB58D80AC +B58D80AC:lI46|HB58D80FC +B58D80FC:lI51|HB58D814C +B58D814C:lI47|HB58D819C +B58D819C:lI101|HB58D81EC +B58D81EC:lI98|HB58D823C +B58D823C:lI105|HB58D828C +B58D828C:lI110|HB58D82DC +B58D82DC:lI47|HB58D832C +B58D832C:lI101|HB58D837C +B58D837C:lI116|HB58D83C4 +B58D83C4:lI115|HB58D8404 +B58D8404:lI46|HB58D8444 +B58D8444:lI98|HB58D8484 +B58D8484:lI101|HB58D84C4 +B58D84C4:lI97|HB58D8504 +B58D8504:lI109|N +B58D7424:lHB58D7484|HB58D7490 +B58D7484:t2:A13:inet_gethost_native,HB58D74F0 +B58D74F0:lI47|HB58D755C +B58D755C:lI117|HB58D75C4 +B58D75C4:lI115|HB58D762C +B58D762C:lI114|HB58D7684 +B58D7684:lI47|HB58D76DC +B58D76DC:lI108|HB58D7734 +B58D7734:lI111|HB58D778C +B58D778C:lI99|HB58D77E4 +B58D77E4:lI97|HB58D783C +B58D783C:lI108|HB58D7894 +B58D7894:lI47|HB58D78E4 +B58D78E4:lI108|HB58D7934 +B58D7934:lI105|HB58D7984 +B58D7984:lI98|HB58D79D4 +B58D79D4:lI47|HB58D7A24 +B58D7A24:lI101|HB58D7A74 +B58D7A74:lI114|HB58D7AC4 +B58D7AC4:lI108|HB58D7B14 +B58D7B14:lI97|HB58D7B64 +B58D7B64:lI110|HB58D7BB4 +B58D7BB4:lI103|HB58D7C04 +B58D7C04:lI47|HB58D7C54 +B58D7C54:lI108|HB58D7CA4 +B58D7CA4:lI105|HB58D7CF4 +B58D7CF4:lI98|HB58D7D44 +B58D7D44:lI47|HB58D7D94 +B58D7D94:lI107|HB58D7DE4 +B58D7DE4:lI101|HB58D7E34 +B58D7E34:lI114|HB58D7E84 +B58D7E84:lI110|HB58D7ED4 +B58D7ED4:lI101|HB58D7F24 +B58D7F24:lI108|HB58D7F74 +B58D7F74:lI45|HB58D7FC4 +B58D7FC4:lI50|HB58D8014 +B58D8014:lI46|HB58D8064 +B58D8064:lI49|HB58D80B4 +B58D80B4:lI51|HB58D8104 +B58D8104:lI46|HB58D8154 +B58D8154:lI51|HB58D81A4 +B58D81A4:lI47|HB58D81F4 +B58D81F4:lI101|HB58D8244 +B58D8244:lI98|HB58D8294 +B58D8294:lI105|HB58D82E4 +B58D82E4:lI110|HB58D8334 +B58D8334:lI47|HB58D8384 +B58D8384:lI105|HB58D83CC +B58D83CC:lI110|HB58D840C +B58D840C:lI101|HB58D844C +B58D844C:lI116|HB58D848C +B58D848C:lI95|HB58D84CC +B58D84CC:lI103|HB58D850C +B58D850C:lI101|HB58D853C +B58D853C:lI116|HB58D856C +B58D856C:lI104|HB58D8594 +B58D8594:lI111|HB58D85BC +B58D85BC:lI115|HB58D85DC +B58D85DC:lI116|HB58D85FC +B58D85FC:lI95|HB58D8614 +B58D8614:lI110|HB58D862C +B58D862C:lI97|HB58D863C +B58D863C:lI116|HB58D864C +B58D864C:lI105|HB58D865C +B58D865C:lI118|HB58D8664 +B58D8664:lI101|HB58D866C +B58D866C:lI46|HB58D8674 +B58D8674:lI98|HB58D867C +B58D867C:lI101|HB58D8684 +B58D8684:lI97|HB58D868C +B58D868C:lI109|N +B58D7490:lHB58D74F8|HB7B4831C +B58D74F8:t2:A8:filename,HB58D7564 +B58D7564:lI47|HB58D75CC +B58D75CC:lI117|HB58D7634 +B58D7634:lI115|HB58D768C +B58D768C:lI114|HB58D76E4 +B58D76E4:lI47|HB58D773C +B58D773C:lI108|HB58D7794 +B58D7794:lI111|HB58D77EC +B58D77EC:lI99|HB58D7844 +B58D7844:lI97|HB58D789C +B58D789C:lI108|HB58D78EC +B58D78EC:lI47|HB58D793C +B58D793C:lI108|HB58D798C +B58D798C:lI105|HB58D79DC +B58D79DC:lI98|HB58D7A2C +B58D7A2C:lI47|HB58D7A7C +B58D7A7C:lI101|HB58D7ACC +B58D7ACC:lI114|HB58D7B1C +B58D7B1C:lI108|HB58D7B6C +B58D7B6C:lI97|HB58D7BBC +B58D7BBC:lI110|HB58D7C0C +B58D7C0C:lI103|HB58D7C5C +B58D7C5C:lI47|HB58D7CAC +B58D7CAC:lI108|HB58D7CFC +B58D7CFC:lI105|HB58D7D4C +B58D7D4C:lI98|HB58D7D9C +B58D7D9C:lI47|HB58D7DEC +B58D7DEC:lI115|HB58D7E3C +B58D7E3C:lI116|HB58D7E8C +B58D7E8C:lI100|HB58D7EDC +B58D7EDC:lI108|HB58D7F2C +B58D7F2C:lI105|HB58D7F7C +B58D7F7C:lI98|HB58D7FCC +B58D7FCC:lI45|HB58D801C +B58D801C:lI49|HB58D806C +B58D806C:lI46|HB58D80BC +B58D80BC:lI49|HB58D810C +B58D810C:lI54|HB58D815C +B58D815C:lI46|HB58D81AC +B58D81AC:lI51|HB58D81FC +B58D81FC:lI47|HB58D824C +B58D824C:lI101|HB58D829C +B58D829C:lI98|HB58D82EC +B58D82EC:lI105|HB58D833C +B58D833C:lI110|HB58D838C +B58D838C:lI47|HB58D83D4 +B58D83D4:lI102|HB58D8414 +B58D8414:lI105|HB58D8454 +B58D8454:lI108|HB58D8494 +B58D8494:lI101|HB58D84D4 +B58D84D4:lI110|HB58D8514 +B58D8514:lI97|HB58D8544 +B58D8544:lI109|HB58D8574 +B58D8574:lI101|HB58D859C +B58D859C:lI46|HB58D85C4 +B58D85C4:lI98|HB58D85E4 +B58D85E4:lI101|HB58D8604 +B58D8604:lI97|HB58D861C +B58D861C:lI109|N +B7B4831C:lHB7B48324|HB7B48330 +B7B48324:t2:AA:inet_parse,HB7B48338 +B7B48338:lI47|HB7B48354 +B7B48354:lI117|HB7B48378 +B7B48378:lI115|HB7B483A4 +B7B483A4:lI114|HB7B483D8 +B7B483D8:lI47|HB7B48414 +B7B48414:lI108|HB7B48458 +B7B48458:lI111|HB7B484A4 +B7B484A4:lI99|HB7B484F8 +B7B484F8:lI97|HB7B4854C +B7B4854C:lI108|HB7B4859C +B7B4859C:lI47|HB7B485EC +B7B485EC:lI108|HB7B4863C +B7B4863C:lI105|HB7B4868C +B7B4868C:lI98|HB7B486DC +B7B486DC:lI47|HB7B4872C +B7B4872C:lI101|HB7B4877C +B7B4877C:lI114|HB7B487CC +B7B487CC:lI108|HB7B4881C +B7B4881C:lI97|HB7B4886C +B7B4886C:lI110|HB7B488BC +B7B488BC:lI103|HB7B4890C +B7B4890C:lI47|HB7B4895C +B7B4895C:lI108|HB7B489AC +B7B489AC:lI105|HB7B489FC +B7B489FC:lI98|HB7B48A4C +B7B48A4C:lI47|HB7B48A9C +B7B48A9C:lI107|HB7B48AEC +B7B48AEC:lI101|HB7B48B3C +B7B48B3C:lI114|HB7B48B8C +B7B48B8C:lI110|HB7B48BDC +B7B48BDC:lI101|HB7B48C2C +B7B48C2C:lI108|HB7B48C7C +B7B48C7C:lI45|HB7B48CCC +B7B48CCC:lI50|HB7B48D1C +B7B48D1C:lI46|HB7B48D6C +B7B48D6C:lI49|HB7B48DBC +B7B48DBC:lI51|HB7B48E0C +B7B48E0C:lI46|HB7B48E5C +B7B48E5C:lI51|HB7B48EAC +B7B48EAC:lI47|HB7B48EFC +B7B48EFC:lI101|HB7B48F4C +B7B48F4C:lI98|HB7B48F9C +B7B48F9C:lI105|HB7B48FEC +B7B48FEC:lI110|HB7B4903C +B7B4903C:lI47|HB7B4908C +B7B4908C:lI105|HB7B490DC +B7B490DC:lI110|HB7B4912C +B7B4912C:lI101|HB7B4917C +B7B4917C:lI116|HB7B491CC +B7B491CC:lI95|HB7B4921C +B7B4921C:lI112|HB7B4926C +B7B4926C:lI97|HB7B492BC +B7B492BC:lI114|HB7B4930C +B7B4930C:lI115|HB7B4935C +B7B4935C:lI101|HB7B493AC +B7B493AC:lI46|HB7B493EC +B7B493EC:lI98|HB7B4942C +B7B4942C:lI101|HB7B4946C +B7B4946C:lI97|HB7B494AC +B7B494AC:lI109|N +B7B48330:lHB7B48340|HB7B4834C +B7B48340:t2:A4:inet,HB7B4835C +B7B4835C:lI47|HB7B48380 +B7B48380:lI117|HB7B483AC +B7B483AC:lI115|HB7B483E0 +B7B483E0:lI114|HB7B4841C +B7B4841C:lI47|HB7B48460 +B7B48460:lI108|HB7B484AC +B7B484AC:lI111|HB7B48500 +B7B48500:lI99|HB7B48554 +B7B48554:lI97|HB7B485A4 +B7B485A4:lI108|HB7B485F4 +B7B485F4:lI47|HB7B48644 +B7B48644:lI108|HB7B48694 +B7B48694:lI105|HB7B486E4 +B7B486E4:lI98|HB7B48734 +B7B48734:lI47|HB7B48784 +B7B48784:lI101|HB7B487D4 +B7B487D4:lI114|HB7B48824 +B7B48824:lI108|HB7B48874 +B7B48874:lI97|HB7B488C4 +B7B488C4:lI110|HB7B48914 +B7B48914:lI103|HB7B48964 +B7B48964:lI47|HB7B489B4 +B7B489B4:lI108|HB7B48A04 +B7B48A04:lI105|HB7B48A54 +B7B48A54:lI98|HB7B48AA4 +B7B48AA4:lI47|HB7B48AF4 +B7B48AF4:lI107|HB7B48B44 +B7B48B44:lI101|HB7B48B94 +B7B48B94:lI114|HB7B48BE4 +B7B48BE4:lI110|HB7B48C34 +B7B48C34:lI101|HB7B48C84 +B7B48C84:lI108|HB7B48CD4 +B7B48CD4:lI45|HB7B48D24 +B7B48D24:lI50|HB7B48D74 +B7B48D74:lI46|HB7B48DC4 +B7B48DC4:lI49|HB7B48E14 +B7B48E14:lI51|HB7B48E64 +B7B48E64:lI46|HB7B48EB4 +B7B48EB4:lI51|HB7B48F04 +B7B48F04:lI47|HB7B48F54 +B7B48F54:lI101|HB7B48FA4 +B7B48FA4:lI98|HB7B48FF4 +B7B48FF4:lI105|HB7B49044 +B7B49044:lI110|HB7B49094 +B7B49094:lI47|HB7B490E4 +B7B490E4:lI105|HB7B49134 +B7B49134:lI110|HB7B49184 +B7B49184:lI101|HB7B491D4 +B7B491D4:lI116|HB7B49224 +B7B49224:lI46|HB7B49274 +B7B49274:lI98|HB7B492C4 +B7B492C4:lI101|HB7B49314 +B7B49314:lI97|HB7B49364 +B7B49364:lI109|N +B7B4834C:lHB7B48364|HB7B48370 +B7B48364:t2:A8:inet_udp,HB7B48388 +B7B48388:lI47|HB7B483B4 +B7B483B4:lI117|HB7B483E8 +B7B483E8:lI115|HB7B48424 +B7B48424:lI114|HB7B48468 +B7B48468:lI47|HB7B484B4 +B7B484B4:lI108|HB7B48508 +B7B48508:lI111|HB7B4855C +B7B4855C:lI99|HB7B485AC +B7B485AC:lI97|HB7B485FC +B7B485FC:lI108|HB7B4864C +B7B4864C:lI47|HB7B4869C +B7B4869C:lI108|HB7B486EC +B7B486EC:lI105|HB7B4873C +B7B4873C:lI98|HB7B4878C +B7B4878C:lI47|HB7B487DC +B7B487DC:lI101|HB7B4882C +B7B4882C:lI114|HB7B4887C +B7B4887C:lI108|HB7B488CC +B7B488CC:lI97|HB7B4891C +B7B4891C:lI110|HB7B4896C +B7B4896C:lI103|HB7B489BC +B7B489BC:lI47|HB7B48A0C +B7B48A0C:lI108|HB7B48A5C +B7B48A5C:lI105|HB7B48AAC +B7B48AAC:lI98|HB7B48AFC +B7B48AFC:lI47|HB7B48B4C +B7B48B4C:lI107|HB7B48B9C +B7B48B9C:lI101|HB7B48BEC +B7B48BEC:lI114|HB7B48C3C +B7B48C3C:lI110|HB7B48C8C +B7B48C8C:lI101|HB7B48CDC +B7B48CDC:lI108|HB7B48D2C +B7B48D2C:lI45|HB7B48D7C +B7B48D7C:lI50|HB7B48DCC +B7B48DCC:lI46|HB7B48E1C +B7B48E1C:lI49|HB7B48E6C +B7B48E6C:lI51|HB7B48EBC +B7B48EBC:lI46|HB7B48F0C +B7B48F0C:lI51|HB7B48F5C +B7B48F5C:lI47|HB7B48FAC +B7B48FAC:lI101|HB7B48FFC +B7B48FFC:lI98|HB7B4904C +B7B4904C:lI105|HB7B4909C +B7B4909C:lI110|HB7B490EC +B7B490EC:lI47|HB7B4913C +B7B4913C:lI105|HB7B4918C +B7B4918C:lI110|HB7B491DC +B7B491DC:lI101|HB7B4922C +B7B4922C:lI116|HB7B4927C +B7B4927C:lI95|HB7B492CC +B7B492CC:lI117|HB7B4931C +B7B4931C:lI100|HB7B4936C +B7B4936C:lI112|HB7B493B4 +B7B493B4:lI46|HB7B493F4 +B7B493F4:lI98|HB7B49434 +B7B49434:lI101|HB7B49474 +B7B49474:lI97|HB7B494B4 +B7B494B4:lI109|N +B7B48370:lHB7B48390|HB7B4839C +B7B48390:t2:A2:os,HB7B483BC +B7B483BC:lI47|HB7B483F0 +B7B483F0:lI117|HB7B4842C +B7B4842C:lI115|HB7B48470 +B7B48470:lI114|HB7B484BC +B7B484BC:lI47|HB7B48510 +B7B48510:lI108|HB7B48564 +B7B48564:lI111|HB7B485B4 +B7B485B4:lI99|HB7B48604 +B7B48604:lI97|HB7B48654 +B7B48654:lI108|HB7B486A4 +B7B486A4:lI47|HB7B486F4 +B7B486F4:lI108|HB7B48744 +B7B48744:lI105|HB7B48794 +B7B48794:lI98|HB7B487E4 +B7B487E4:lI47|HB7B48834 +B7B48834:lI101|HB7B48884 +B7B48884:lI114|HB7B488D4 +B7B488D4:lI108|HB7B48924 +B7B48924:lI97|HB7B48974 +B7B48974:lI110|HB7B489C4 +B7B489C4:lI103|HB7B48A14 +B7B48A14:lI47|HB7B48A64 +B7B48A64:lI108|HB7B48AB4 +B7B48AB4:lI105|HB7B48B04 +B7B48B04:lI98|HB7B48B54 +B7B48B54:lI47|HB7B48BA4 +B7B48BA4:lI107|HB7B48BF4 +B7B48BF4:lI101|HB7B48C44 +B7B48C44:lI114|HB7B48C94 +B7B48C94:lI110|HB7B48CE4 +B7B48CE4:lI101|HB7B48D34 +B7B48D34:lI108|HB7B48D84 +B7B48D84:lI45|HB7B48DD4 +B7B48DD4:lI50|HB7B48E24 +B7B48E24:lI46|HB7B48E74 +B7B48E74:lI49|HB7B48EC4 +B7B48EC4:lI51|HB7B48F14 +B7B48F14:lI46|HB7B48F64 +B7B48F64:lI51|HB7B48FB4 +B7B48FB4:lI47|HB7B49004 +B7B49004:lI101|HB7B49054 +B7B49054:lI98|HB7B490A4 +B7B490A4:lI105|HB7B490F4 +B7B490F4:lI110|HB7B49144 +B7B49144:lI47|HB7B49194 +B7B49194:lI111|HB7B491E4 +B7B491E4:lI115|HB7B49234 +B7B49234:lI46|HB7B49284 +B7B49284:lI98|HB7B492D4 +B7B492D4:lI101|HB7B49324 +B7B49324:lI97|HB7B49374 +B7B49374:lI109|N +B7B4839C:lHB7B483C4|HB7B483D0 +B7B483C4:t2:AB:inet_config,HB7B483F8 +B7B483F8:lI47|HB7B48434 +B7B48434:lI117|HB7B48478 +B7B48478:lI115|HB7B484C4 +B7B484C4:lI114|HB7B48518 +B7B48518:lI47|HB7B4856C +B7B4856C:lI108|HB7B485BC +B7B485BC:lI111|HB7B4860C +B7B4860C:lI99|HB7B4865C +B7B4865C:lI97|HB7B486AC +B7B486AC:lI108|HB7B486FC +B7B486FC:lI47|HB7B4874C +B7B4874C:lI108|HB7B4879C +B7B4879C:lI105|HB7B487EC +B7B487EC:lI98|HB7B4883C +B7B4883C:lI47|HB7B4888C +B7B4888C:lI101|HB7B488DC +B7B488DC:lI114|HB7B4892C +B7B4892C:lI108|HB7B4897C +B7B4897C:lI97|HB7B489CC +B7B489CC:lI110|HB7B48A1C +B7B48A1C:lI103|HB7B48A6C +B7B48A6C:lI47|HB7B48ABC +B7B48ABC:lI108|HB7B48B0C +B7B48B0C:lI105|HB7B48B5C +B7B48B5C:lI98|HB7B48BAC +B7B48BAC:lI47|HB7B48BFC +B7B48BFC:lI107|HB7B48C4C +B7B48C4C:lI101|HB7B48C9C +B7B48C9C:lI114|HB7B48CEC +B7B48CEC:lI110|HB7B48D3C +B7B48D3C:lI101|HB7B48D8C +B7B48D8C:lI108|HB7B48DDC +B7B48DDC:lI45|HB7B48E2C +B7B48E2C:lI50|HB7B48E7C +B7B48E7C:lI46|HB7B48ECC +B7B48ECC:lI49|HB7B48F1C +B7B48F1C:lI51|HB7B48F6C +B7B48F6C:lI46|HB7B48FBC +B7B48FBC:lI51|HB7B4900C +B7B4900C:lI47|HB7B4905C +B7B4905C:lI101|HB7B490AC +B7B490AC:lI98|HB7B490FC +B7B490FC:lI105|HB7B4914C +B7B4914C:lI110|HB7B4919C +B7B4919C:lI47|HB7B491EC +B7B491EC:lI105|HB7B4923C +B7B4923C:lI110|HB7B4928C +B7B4928C:lI101|HB7B492DC +B7B492DC:lI116|HB7B4932C +B7B4932C:lI95|HB7B4937C +B7B4937C:lI99|HB7B493BC +B7B493BC:lI111|HB7B493FC +B7B493FC:lI110|HB7B4943C +B7B4943C:lI102|HB7B4947C +B7B4947C:lI105|HB7B494BC +B7B494BC:lI103|HB7B494EC +B7B494EC:lI46|HB7B4951C +B7B4951C:lI98|HB7B49544 +B7B49544:lI101|HB7B4955C +B7B4955C:lI97|HB7B4956C +B7B4956C:lI109|N +B7B483D0:lHB7B48400|HB7B4840C +B7B48400:t2:A7:inet_db,HB7B4843C +B7B4843C:lI47|HB7B48480 +B7B48480:lI117|HB7B484CC +B7B484CC:lI115|HB7B48520 +B7B48520:lI114|HB7B48574 +B7B48574:lI47|HB7B485C4 +B7B485C4:lI108|HB7B48614 +B7B48614:lI111|HB7B48664 +B7B48664:lI99|HB7B486B4 +B7B486B4:lI97|HB7B48704 +B7B48704:lI108|HB7B48754 +B7B48754:lI47|HB7B487A4 +B7B487A4:lI108|HB7B487F4 +B7B487F4:lI105|HB7B48844 +B7B48844:lI98|HB7B48894 +B7B48894:lI47|HB7B488E4 +B7B488E4:lI101|HB7B48934 +B7B48934:lI114|HB7B48984 +B7B48984:lI108|HB7B489D4 +B7B489D4:lI97|HB7B48A24 +B7B48A24:lI110|HB7B48A74 +B7B48A74:lI103|HB7B48AC4 +B7B48AC4:lI47|HB7B48B14 +B7B48B14:lI108|HB7B48B64 +B7B48B64:lI105|HB7B48BB4 +B7B48BB4:lI98|HB7B48C04 +B7B48C04:lI47|HB7B48C54 +B7B48C54:lI107|HB7B48CA4 +B7B48CA4:lI101|HB7B48CF4 +B7B48CF4:lI114|HB7B48D44 +B7B48D44:lI110|HB7B48D94 +B7B48D94:lI101|HB7B48DE4 +B7B48DE4:lI108|HB7B48E34 +B7B48E34:lI45|HB7B48E84 +B7B48E84:lI50|HB7B48ED4 +B7B48ED4:lI46|HB7B48F24 +B7B48F24:lI49|HB7B48F74 +B7B48F74:lI51|HB7B48FC4 +B7B48FC4:lI46|HB7B49014 +B7B49014:lI51|HB7B49064 +B7B49064:lI47|HB7B490B4 +B7B490B4:lI101|HB7B49104 +B7B49104:lI98|HB7B49154 +B7B49154:lI105|HB7B491A4 +B7B491A4:lI110|HB7B491F4 +B7B491F4:lI47|HB7B49244 +B7B49244:lI105|HB7B49294 +B7B49294:lI110|HB7B492E4 +B7B492E4:lI101|HB7B49334 +B7B49334:lI116|HB7B49384 +B7B49384:lI95|HB7B493C4 +B7B493C4:lI100|HB7B49404 +B7B49404:lI98|HB7B49444 +B7B49444:lI46|HB7B49484 +B7B49484:lI98|HB7B494C4 +B7B494C4:lI101|HB7B494F4 +B7B494F4:lI97|HB7B49524 +B7B49524:lI109|N +B7B4840C:lHB7B48444|HB7B48450 +B7B48444:t2:A6:global,HB7B48488 +B7B48488:lI47|HB7B484D4 +B7B484D4:lI117|HB7B48528 +B7B48528:lI115|HB7B4857C +B7B4857C:lI114|HB7B485CC +B7B485CC:lI47|HB7B4861C +B7B4861C:lI108|HB7B4866C +B7B4866C:lI111|HB7B486BC +B7B486BC:lI99|HB7B4870C +B7B4870C:lI97|HB7B4875C +B7B4875C:lI108|HB7B487AC +B7B487AC:lI47|HB7B487FC +B7B487FC:lI108|HB7B4884C +B7B4884C:lI105|HB7B4889C +B7B4889C:lI98|HB7B488EC +B7B488EC:lI47|HB7B4893C +B7B4893C:lI101|HB7B4898C +B7B4898C:lI114|HB7B489DC +B7B489DC:lI108|HB7B48A2C +B7B48A2C:lI97|HB7B48A7C +B7B48A7C:lI110|HB7B48ACC +B7B48ACC:lI103|HB7B48B1C +B7B48B1C:lI47|HB7B48B6C +B7B48B6C:lI108|HB7B48BBC +B7B48BBC:lI105|HB7B48C0C +B7B48C0C:lI98|HB7B48C5C +B7B48C5C:lI47|HB7B48CAC +B7B48CAC:lI107|HB7B48CFC +B7B48CFC:lI101|HB7B48D4C +B7B48D4C:lI114|HB7B48D9C +B7B48D9C:lI110|HB7B48DEC +B7B48DEC:lI101|HB7B48E3C +B7B48E3C:lI108|HB7B48E8C +B7B48E8C:lI45|HB7B48EDC +B7B48EDC:lI50|HB7B48F2C +B7B48F2C:lI46|HB7B48F7C +B7B48F7C:lI49|HB7B48FCC +B7B48FCC:lI51|HB7B4901C +B7B4901C:lI46|HB7B4906C +B7B4906C:lI51|HB7B490BC +B7B490BC:lI47|HB7B4910C +B7B4910C:lI101|HB7B4915C +B7B4915C:lI98|HB7B491AC +B7B491AC:lI105|HB7B491FC +B7B491FC:lI110|HB7B4924C +B7B4924C:lI47|HB7B4929C +B7B4929C:lI103|HB7B492EC +B7B492EC:lI108|HB7B4933C +B7B4933C:lI111|HB7B4938C +B7B4938C:lI98|HB7B493CC +B7B493CC:lI97|HB7B4940C +B7B4940C:lI108|HB7B4944C +B7B4944C:lI46|HB7B4948C +B7B4948C:lI98|HB7B494CC +B7B494CC:lI101|HB7B494FC +B7B494FC:lI97|HB7B4952C +B7B4952C:lI109|N +B7B48450:lHB7B48490|HB7B4849C +B7B48490:t2:A8:gb_trees,HB7B484DC +B7B484DC:lI47|HB7B48530 +B7B48530:lI117|HB7B48584 +B7B48584:lI115|HB7B485D4 +B7B485D4:lI114|HB7B48624 +B7B48624:lI47|HB7B48674 +B7B48674:lI108|HB7B486C4 +B7B486C4:lI111|HB7B48714 +B7B48714:lI99|HB7B48764 +B7B48764:lI97|HB7B487B4 +B7B487B4:lI108|HB7B48804 +B7B48804:lI47|HB7B48854 +B7B48854:lI108|HB7B488A4 +B7B488A4:lI105|HB7B488F4 +B7B488F4:lI98|HB7B48944 +B7B48944:lI47|HB7B48994 +B7B48994:lI101|HB7B489E4 +B7B489E4:lI114|HB7B48A34 +B7B48A34:lI108|HB7B48A84 +B7B48A84:lI97|HB7B48AD4 +B7B48AD4:lI110|HB7B48B24 +B7B48B24:lI103|HB7B48B74 +B7B48B74:lI47|HB7B48BC4 +B7B48BC4:lI108|HB7B48C14 +B7B48C14:lI105|HB7B48C64 +B7B48C64:lI98|HB7B48CB4 +B7B48CB4:lI47|HB7B48D04 +B7B48D04:lI115|HB7B48D54 +B7B48D54:lI116|HB7B48DA4 +B7B48DA4:lI100|HB7B48DF4 +B7B48DF4:lI108|HB7B48E44 +B7B48E44:lI105|HB7B48E94 +B7B48E94:lI98|HB7B48EE4 +B7B48EE4:lI45|HB7B48F34 +B7B48F34:lI49|HB7B48F84 +B7B48F84:lI46|HB7B48FD4 +B7B48FD4:lI49|HB7B49024 +B7B49024:lI54|HB7B49074 +B7B49074:lI46|HB7B490C4 +B7B490C4:lI51|HB7B49114 +B7B49114:lI47|HB7B49164 +B7B49164:lI101|HB7B491B4 +B7B491B4:lI98|HB7B49204 +B7B49204:lI105|HB7B49254 +B7B49254:lI110|HB7B492A4 +B7B492A4:lI47|HB7B492F4 +B7B492F4:lI103|HB7B49344 +B7B49344:lI98|HB7B49394 +B7B49394:lI95|HB7B493D4 +B7B493D4:lI116|HB7B49414 +B7B49414:lI114|HB7B49454 +B7B49454:lI101|HB7B49494 +B7B49494:lI101|HB7B494D4 +B7B494D4:lI115|HB7B49504 +B7B49504:lI46|HB7B49534 +B7B49534:lI98|HB7B4954C +B7B4954C:lI101|HB7B49564 +B7B49564:lI97|HB7B49574 +B7B49574:lI109|N +B7B4849C:lHB7B484E4|HB7B484F0 +B7B484E4:t2:A3:rpc,HB7B48538 +B7B48538:lI47|HB7B4858C +B7B4858C:lI117|HB7B485DC +B7B485DC:lI115|HB7B4862C +B7B4862C:lI114|HB7B4867C +B7B4867C:lI47|HB7B486CC +B7B486CC:lI108|HB7B4871C +B7B4871C:lI111|HB7B4876C +B7B4876C:lI99|HB7B487BC +B7B487BC:lI97|HB7B4880C +B7B4880C:lI108|HB7B4885C +B7B4885C:lI47|HB7B488AC +B7B488AC:lI108|HB7B488FC +B7B488FC:lI105|HB7B4894C +B7B4894C:lI98|HB7B4899C +B7B4899C:lI47|HB7B489EC +B7B489EC:lI101|HB7B48A3C +B7B48A3C:lI114|HB7B48A8C +B7B48A8C:lI108|HB7B48ADC +B7B48ADC:lI97|HB7B48B2C +B7B48B2C:lI110|HB7B48B7C +B7B48B7C:lI103|HB7B48BCC +B7B48BCC:lI47|HB7B48C1C +B7B48C1C:lI108|HB7B48C6C +B7B48C6C:lI105|HB7B48CBC +B7B48CBC:lI98|HB7B48D0C +B7B48D0C:lI47|HB7B48D5C +B7B48D5C:lI107|HB7B48DAC +B7B48DAC:lI101|HB7B48DFC +B7B48DFC:lI114|HB7B48E4C +B7B48E4C:lI110|HB7B48E9C +B7B48E9C:lI101|HB7B48EEC +B7B48EEC:lI108|HB7B48F3C +B7B48F3C:lI45|HB7B48F8C +B7B48F8C:lI50|HB7B48FDC +B7B48FDC:lI46|HB7B4902C +B7B4902C:lI49|HB7B4907C +B7B4907C:lI51|HB7B490CC +B7B490CC:lI46|HB7B4911C +B7B4911C:lI51|HB7B4916C +B7B4916C:lI47|HB7B491BC +B7B491BC:lI101|HB7B4920C +B7B4920C:lI98|HB7B4925C +B7B4925C:lI105|HB7B492AC +B7B492AC:lI110|HB7B492FC +B7B492FC:lI47|HB7B4934C +B7B4934C:lI114|HB7B4939C +B7B4939C:lI112|HB7B493DC +B7B493DC:lI99|HB7B4941C +B7B4941C:lI46|HB7B4945C +B7B4945C:lI98|HB7B4949C +B7B4949C:lI101|HB7B494DC +B7B494DC:lI97|HB7B4950C +B7B4950C:lI109|N +B7B484F0:lHB7B48540|HB7B464F8 +B7B48540:t2:A4:dict,HB7B48594 +B7B48594:lI47|HB7B485E4 +B7B485E4:lI117|HB7B48634 +B7B48634:lI115|HB7B48684 +B7B48684:lI114|HB7B486D4 +B7B486D4:lI47|HB7B48724 +B7B48724:lI108|HB7B48774 +B7B48774:lI111|HB7B487C4 +B7B487C4:lI99|HB7B48814 +B7B48814:lI97|HB7B48864 +B7B48864:lI108|HB7B488B4 +B7B488B4:lI47|HB7B48904 +B7B48904:lI108|HB7B48954 +B7B48954:lI105|HB7B489A4 +B7B489A4:lI98|HB7B489F4 +B7B489F4:lI47|HB7B48A44 +B7B48A44:lI101|HB7B48A94 +B7B48A94:lI114|HB7B48AE4 +B7B48AE4:lI108|HB7B48B34 +B7B48B34:lI97|HB7B48B84 +B7B48B84:lI110|HB7B48BD4 +B7B48BD4:lI103|HB7B48C24 +B7B48C24:lI47|HB7B48C74 +B7B48C74:lI108|HB7B48CC4 +B7B48CC4:lI105|HB7B48D14 +B7B48D14:lI98|HB7B48D64 +B7B48D64:lI47|HB7B48DB4 +B7B48DB4:lI115|HB7B48E04 +B7B48E04:lI116|HB7B48E54 +B7B48E54:lI100|HB7B48EA4 +B7B48EA4:lI108|HB7B48EF4 +B7B48EF4:lI105|HB7B48F44 +B7B48F44:lI98|HB7B48F94 +B7B48F94:lI45|HB7B48FE4 +B7B48FE4:lI49|HB7B49034 +B7B49034:lI46|HB7B49084 +B7B49084:lI49|HB7B490D4 +B7B490D4:lI54|HB7B49124 +B7B49124:lI46|HB7B49174 +B7B49174:lI51|HB7B491C4 +B7B491C4:lI47|HB7B49214 +B7B49214:lI101|HB7B49264 +B7B49264:lI98|HB7B492B4 +B7B492B4:lI105|HB7B49304 +B7B49304:lI110|HB7B49354 +B7B49354:lI47|HB7B493A4 +B7B493A4:lI100|HB7B493E4 +B7B493E4:lI105|HB7B49424 +B7B49424:lI99|HB7B49464 +B7B49464:lI116|HB7B494A4 +B7B494A4:lI46|HB7B494E4 +B7B494E4:lI98|HB7B49514 +B7B49514:lI101|HB7B4953C +B7B4953C:lI97|HB7B49554 +B7B49554:lI109|N +B7B464F8:lHB7B46548|HB7B46554 +B7B46548:t2:AA:supervisor,HB7B465AC +B7B465AC:lI47|HB7B46628 +B7B46628:lI117|HB7B46684 +B7B46684:lI115|HB7B466F4 +B7B466F4:lI114|HB7B46760 +B7B46760:lI47|HB7B467D0 +B7B467D0:lI108|HB7B4685C +B7B4685C:lI111|HB7B46908 +B7B46908:lI99|HB7B469A0 +B7B469A0:lI97|HB7B46A04 +B7B46A04:lI108|HB7B46A70 +B7B46A70:lI47|HB7B46AE4 +B7B46AE4:lI108|HB7B46B60 +B7B46B60:lI105|HB7B46BDC +B7B46BDC:lI98|HB7B46C4C +B7B46C4C:lI47|HB7B46CBC +B7B46CBC:lI101|HB7B46D2C +B7B46D2C:lI114|HB7B46D9C +B7B46D9C:lI108|HB7B46E0C +B7B46E0C:lI97|HB7B46E7C +B7B46E7C:lI110|HB7B46EEC +B7B46EEC:lI103|HB7B46F5C +B7B46F5C:lI47|HB7B46FCC +B7B46FCC:lI108|HB7B4703C +B7B4703C:lI105|HB7B470AC +B7B470AC:lI98|HB7B4711C +B7B4711C:lI47|HB7B4718C +B7B4718C:lI115|HB7B471FC +B7B471FC:lI116|HB7B4726C +B7B4726C:lI100|HB7B472DC +B7B472DC:lI108|HB7B4734C +B7B4734C:lI105|HB7B473BC +B7B473BC:lI98|HB7B4742C +B7B4742C:lI45|HB7B4749C +B7B4749C:lI49|HB7B4750C +B7B4750C:lI46|HB7B4757C +B7B4757C:lI49|HB7B475EC +B7B475EC:lI54|HB7B4765C +B7B4765C:lI46|HB7B476CC +B7B476CC:lI51|HB7B4773C +B7B4773C:lI47|HB7B477AC +B7B477AC:lI101|HB7B4781C +B7B4781C:lI98|HB7B4788C +B7B4788C:lI105|HB7B478FC +B7B478FC:lI110|HB7B4796C +B7B4796C:lI47|HB7B479DC +B7B479DC:lI115|HB7B47A4C +B7B47A4C:lI117|HB7B47ABC +B7B47ABC:lI112|HB7B47B2C +B7B47B2C:lI101|HB7B47B9C +B7B47B9C:lI114|HB7B47C0C +B7B47C0C:lI118|HB7B47C7C +B7B47C7C:lI105|HB7B47CEC +B7B47CEC:lI115|HB7B47D5C +B7B47D5C:lI111|HB7B47DCC +B7B47DCC:lI114|HB7B47E3C +B7B47E3C:lI46|HB7B47EAC +B7B47EAC:lI98|HB7B47F14 +B7B47F14:lI101|HB7B47F74 +B7B47F74:lI97|HB7B47FD4 +B7B47FD4:lI109|N +B7B46554:lHB7B465B4|HB7B465C0 +B7B465B4:t2:A6:kernel,HB7B46630 +B7B46630:lI47|HB7B4668C +B7B4668C:lI117|HB7B466FC +B7B466FC:lI115|HB7B46768 +B7B46768:lI114|HB7B467D8 +B7B467D8:lI47|HB7B46864 +B7B46864:lI108|HB7B46910 +B7B46910:lI111|HB7B469A8 +B7B469A8:lI99|HB7B46A0C +B7B46A0C:lI97|HB7B46A78 +B7B46A78:lI108|HB7B46AEC +B7B46AEC:lI47|HB7B46B68 +B7B46B68:lI108|HB7B46BE4 +B7B46BE4:lI105|HB7B46C54 +B7B46C54:lI98|HB7B46CC4 +B7B46CC4:lI47|HB7B46D34 +B7B46D34:lI101|HB7B46DA4 +B7B46DA4:lI114|HB7B46E14 +B7B46E14:lI108|HB7B46E84 +B7B46E84:lI97|HB7B46EF4 +B7B46EF4:lI110|HB7B46F64 +B7B46F64:lI103|HB7B46FD4 +B7B46FD4:lI47|HB7B47044 +B7B47044:lI108|HB7B470B4 +B7B470B4:lI105|HB7B47124 +B7B47124:lI98|HB7B47194 +B7B47194:lI47|HB7B47204 +B7B47204:lI107|HB7B47274 +B7B47274:lI101|HB7B472E4 +B7B472E4:lI114|HB7B47354 +B7B47354:lI110|HB7B473C4 +B7B473C4:lI101|HB7B47434 +B7B47434:lI108|HB7B474A4 +B7B474A4:lI45|HB7B47514 +B7B47514:lI50|HB7B47584 +B7B47584:lI46|HB7B475F4 +B7B475F4:lI49|HB7B47664 +B7B47664:lI51|HB7B476D4 +B7B476D4:lI46|HB7B47744 +B7B47744:lI51|HB7B477B4 +B7B477B4:lI47|HB7B47824 +B7B47824:lI101|HB7B47894 +B7B47894:lI98|HB7B47904 +B7B47904:lI105|HB7B47974 +B7B47974:lI110|HB7B479E4 +B7B479E4:lI47|HB7B47A54 +B7B47A54:lI107|HB7B47AC4 +B7B47AC4:lI101|HB7B47B34 +B7B47B34:lI114|HB7B47BA4 +B7B47BA4:lI110|HB7B47C14 +B7B47C14:lI101|HB7B47C84 +B7B47C84:lI108|HB7B47CF4 +B7B47CF4:lI46|HB7B47D64 +B7B47D64:lI98|HB7B47DD4 +B7B47DD4:lI101|HB7B47E44 +B7B47E44:lI97|HB7B47EB4 +B7B47EB4:lI109|N +B7B465C0:lHB7B46638|HB7B46644 +B7B46638:t2:A12:application_master,HB7B46694 +B7B46694:lI47|HB7B46704 +B7B46704:lI117|HB7B46770 +B7B46770:lI115|HB7B467E0 +B7B467E0:lI114|HB7B4686C +B7B4686C:lI47|HB7B46918 +B7B46918:lI108|HB7B469B0 +B7B469B0:lI111|HB7B46A14 +B7B46A14:lI99|HB7B46A80 +B7B46A80:lI97|HB7B46AF4 +B7B46AF4:lI108|HB7B46B70 +B7B46B70:lI47|HB7B46BEC +B7B46BEC:lI108|HB7B46C5C +B7B46C5C:lI105|HB7B46CCC +B7B46CCC:lI98|HB7B46D3C +B7B46D3C:lI47|HB7B46DAC +B7B46DAC:lI101|HB7B46E1C +B7B46E1C:lI114|HB7B46E8C +B7B46E8C:lI108|HB7B46EFC +B7B46EFC:lI97|HB7B46F6C +B7B46F6C:lI110|HB7B46FDC +B7B46FDC:lI103|HB7B4704C +B7B4704C:lI47|HB7B470BC +B7B470BC:lI108|HB7B4712C +B7B4712C:lI105|HB7B4719C +B7B4719C:lI98|HB7B4720C +B7B4720C:lI47|HB7B4727C +B7B4727C:lI107|HB7B472EC +B7B472EC:lI101|HB7B4735C +B7B4735C:lI114|HB7B473CC +B7B473CC:lI110|HB7B4743C +B7B4743C:lI101|HB7B474AC +B7B474AC:lI108|HB7B4751C +B7B4751C:lI45|HB7B4758C +B7B4758C:lI50|HB7B475FC +B7B475FC:lI46|HB7B4766C +B7B4766C:lI49|HB7B476DC +B7B476DC:lI51|HB7B4774C +B7B4774C:lI46|HB7B477BC +B7B477BC:lI51|HB7B4782C +B7B4782C:lI47|HB7B4789C +B7B4789C:lI101|HB7B4790C +B7B4790C:lI98|HB7B4797C +B7B4797C:lI105|HB7B479EC +B7B479EC:lI110|HB7B47A5C +B7B47A5C:lI47|HB7B47ACC +B7B47ACC:lI97|HB7B47B3C +B7B47B3C:lI112|HB7B47BAC +B7B47BAC:lI112|HB7B47C1C +B7B47C1C:lI108|HB7B47C8C +B7B47C8C:lI105|HB7B47CFC +B7B47CFC:lI99|HB7B47D6C +B7B47D6C:lI97|HB7B47DDC +B7B47DDC:lI116|HB7B47E4C +B7B47E4C:lI105|HB7B47EBC +B7B47EBC:lI111|HB7B47F1C +B7B47F1C:lI110|HB7B47F7C +B7B47F7C:lI95|HB7B47FDC +B7B47FDC:lI109|HB7B48034 +B7B48034:lI97|HB7B4808C +B7B4808C:lI115|HB7B480DC +B7B480DC:lI116|HB7B48124 +B7B48124:lI101|HB7B4816C +B7B4816C:lI114|HB7B481B4 +B7B481B4:lI46|HB7B481EC +B7B481EC:lI98|HB7B4821C +B7B4821C:lI101|HB7B48244 +B7B48244:lI97|HB7B4826C +B7B4826C:lI109|N +B7B46644:lHB7B4669C|HB7B466A8 +B7B4669C:t2:A3:sys,HB7B4670C +B7B4670C:lI47|HB7B46778 +B7B46778:lI117|HB7B467E8 +B7B467E8:lI115|HB7B46874 +B7B46874:lI114|HB7B46920 +B7B46920:lI47|HB7B469B8 +B7B469B8:lI108|HB7B46A1C +B7B46A1C:lI111|HB7B46A88 +B7B46A88:lI99|HB7B46AFC +B7B46AFC:lI97|HB7B46B78 +B7B46B78:lI108|HB7B46BF4 +B7B46BF4:lI47|HB7B46C64 +B7B46C64:lI108|HB7B46CD4 +B7B46CD4:lI105|HB7B46D44 +B7B46D44:lI98|HB7B46DB4 +B7B46DB4:lI47|HB7B46E24 +B7B46E24:lI101|HB7B46E94 +B7B46E94:lI114|HB7B46F04 +B7B46F04:lI108|HB7B46F74 +B7B46F74:lI97|HB7B46FE4 +B7B46FE4:lI110|HB7B47054 +B7B47054:lI103|HB7B470C4 +B7B470C4:lI47|HB7B47134 +B7B47134:lI108|HB7B471A4 +B7B471A4:lI105|HB7B47214 +B7B47214:lI98|HB7B47284 +B7B47284:lI47|HB7B472F4 +B7B472F4:lI115|HB7B47364 +B7B47364:lI116|HB7B473D4 +B7B473D4:lI100|HB7B47444 +B7B47444:lI108|HB7B474B4 +B7B474B4:lI105|HB7B47524 +B7B47524:lI98|HB7B47594 +B7B47594:lI45|HB7B47604 +B7B47604:lI49|HB7B47674 +B7B47674:lI46|HB7B476E4 +B7B476E4:lI49|HB7B47754 +B7B47754:lI54|HB7B477C4 +B7B477C4:lI46|HB7B47834 +B7B47834:lI51|HB7B478A4 +B7B478A4:lI47|HB7B47914 +B7B47914:lI101|HB7B47984 +B7B47984:lI98|HB7B479F4 +B7B479F4:lI105|HB7B47A64 +B7B47A64:lI110|HB7B47AD4 +B7B47AD4:lI47|HB7B47B44 +B7B47B44:lI115|HB7B47BB4 +B7B47BB4:lI121|HB7B47C24 +B7B47C24:lI115|HB7B47C94 +B7B47C94:lI46|HB7B47D04 +B7B47D04:lI98|HB7B47D74 +B7B47D74:lI101|HB7B47DE4 +B7B47DE4:lI97|HB7B47E54 +B7B47E54:lI109|N +B7B466A8:lHB7B46714|HB7B46720 +B7B46714:t2:AB:application,HB7B46780 +B7B46780:lI47|HB7B467F0 +B7B467F0:lI117|HB7B4687C +B7B4687C:lI115|HB7B46928 +B7B46928:lI114|HB7B469C0 +B7B469C0:lI47|HB7B46A24 +B7B46A24:lI108|HB7B46A90 +B7B46A90:lI111|HB7B46B04 +B7B46B04:lI99|HB7B46B80 +B7B46B80:lI97|HB7B46BFC +B7B46BFC:lI108|HB7B46C6C +B7B46C6C:lI47|HB7B46CDC +B7B46CDC:lI108|HB7B46D4C +B7B46D4C:lI105|HB7B46DBC +B7B46DBC:lI98|HB7B46E2C +B7B46E2C:lI47|HB7B46E9C +B7B46E9C:lI101|HB7B46F0C +B7B46F0C:lI114|HB7B46F7C +B7B46F7C:lI108|HB7B46FEC +B7B46FEC:lI97|HB7B4705C +B7B4705C:lI110|HB7B470CC +B7B470CC:lI103|HB7B4713C +B7B4713C:lI47|HB7B471AC +B7B471AC:lI108|HB7B4721C +B7B4721C:lI105|HB7B4728C +B7B4728C:lI98|HB7B472FC +B7B472FC:lI47|HB7B4736C +B7B4736C:lI107|HB7B473DC +B7B473DC:lI101|HB7B4744C +B7B4744C:lI114|HB7B474BC +B7B474BC:lI110|HB7B4752C +B7B4752C:lI101|HB7B4759C +B7B4759C:lI108|HB7B4760C +B7B4760C:lI45|HB7B4767C +B7B4767C:lI50|HB7B476EC +B7B476EC:lI46|HB7B4775C +B7B4775C:lI49|HB7B477CC +B7B477CC:lI51|HB7B4783C +B7B4783C:lI46|HB7B478AC +B7B478AC:lI51|HB7B4791C +B7B4791C:lI47|HB7B4798C +B7B4798C:lI101|HB7B479FC +B7B479FC:lI98|HB7B47A6C +B7B47A6C:lI105|HB7B47ADC +B7B47ADC:lI110|HB7B47B4C +B7B47B4C:lI47|HB7B47BBC +B7B47BBC:lI97|HB7B47C2C +B7B47C2C:lI112|HB7B47C9C +B7B47C9C:lI112|HB7B47D0C +B7B47D0C:lI108|HB7B47D7C +B7B47D7C:lI105|HB7B47DEC +B7B47DEC:lI99|HB7B47E5C +B7B47E5C:lI97|HB7B47EC4 +B7B47EC4:lI116|HB7B47F24 +B7B47F24:lI105|HB7B47F84 +B7B47F84:lI111|HB7B47FE4 +B7B47FE4:lI110|HB7B4803C +B7B4803C:lI46|HB7B48094 +B7B48094:lI98|HB7B480E4 +B7B480E4:lI101|HB7B4812C +B7B4812C:lI97|HB7B48174 +B7B48174:lI109|N +B7B46720:lHB7B46788|HB7B46794 +B7B46788:t2:AA:gen_server,HB7B467F8 +B7B467F8:lI47|HB7B46884 +B7B46884:lI117|HB7B46930 +B7B46930:lI115|HB7B469C8 +B7B469C8:lI114|HB7B46A2C +B7B46A2C:lI47|HB7B46A98 +B7B46A98:lI108|HB7B46B0C +B7B46B0C:lI111|HB7B46B88 +B7B46B88:lI99|HB7B46C04 +B7B46C04:lI97|HB7B46C74 +B7B46C74:lI108|HB7B46CE4 +B7B46CE4:lI47|HB7B46D54 +B7B46D54:lI108|HB7B46DC4 +B7B46DC4:lI105|HB7B46E34 +B7B46E34:lI98|HB7B46EA4 +B7B46EA4:lI47|HB7B46F14 +B7B46F14:lI101|HB7B46F84 +B7B46F84:lI114|HB7B46FF4 +B7B46FF4:lI108|HB7B47064 +B7B47064:lI97|HB7B470D4 +B7B470D4:lI110|HB7B47144 +B7B47144:lI103|HB7B471B4 +B7B471B4:lI47|HB7B47224 +B7B47224:lI108|HB7B47294 +B7B47294:lI105|HB7B47304 +B7B47304:lI98|HB7B47374 +B7B47374:lI47|HB7B473E4 +B7B473E4:lI115|HB7B47454 +B7B47454:lI116|HB7B474C4 +B7B474C4:lI100|HB7B47534 +B7B47534:lI108|HB7B475A4 +B7B475A4:lI105|HB7B47614 +B7B47614:lI98|HB7B47684 +B7B47684:lI45|HB7B476F4 +B7B476F4:lI49|HB7B47764 +B7B47764:lI46|HB7B477D4 +B7B477D4:lI49|HB7B47844 +B7B47844:lI54|HB7B478B4 +B7B478B4:lI46|HB7B47924 +B7B47924:lI51|HB7B47994 +B7B47994:lI47|HB7B47A04 +B7B47A04:lI101|HB7B47A74 +B7B47A74:lI98|HB7B47AE4 +B7B47AE4:lI105|HB7B47B54 +B7B47B54:lI110|HB7B47BC4 +B7B47BC4:lI47|HB7B47C34 +B7B47C34:lI103|HB7B47CA4 +B7B47CA4:lI101|HB7B47D14 +B7B47D14:lI110|HB7B47D84 +B7B47D84:lI95|HB7B47DF4 +B7B47DF4:lI115|HB7B47E64 +B7B47E64:lI101|HB7B47ECC +B7B47ECC:lI114|HB7B47F2C +B7B47F2C:lI118|HB7B47F8C +B7B47F8C:lI101|HB7B47FEC +B7B47FEC:lI114|HB7B48044 +B7B48044:lI46|HB7B4809C +B7B4809C:lI98|HB7B480EC +B7B480EC:lI101|HB7B48134 +B7B48134:lI97|HB7B4817C +B7B4817C:lI109|N +B7B46794:lHB7B46800|HB7B4680C +B7B46800:t2:A5:lists,HB7B4688C +B7B4688C:lI47|HB7B46938 +B7B46938:lI117|HB7B469D0 +B7B469D0:lI115|HB7B46A34 +B7B46A34:lI114|HB7B46AA0 +B7B46AA0:lI47|HB7B46B14 +B7B46B14:lI108|HB7B46B90 +B7B46B90:lI111|HB7B46C0C +B7B46C0C:lI99|HB7B46C7C +B7B46C7C:lI97|HB7B46CEC +B7B46CEC:lI108|HB7B46D5C +B7B46D5C:lI47|HB7B46DCC +B7B46DCC:lI108|HB7B46E3C +B7B46E3C:lI105|HB7B46EAC +B7B46EAC:lI98|HB7B46F1C +B7B46F1C:lI47|HB7B46F8C +B7B46F8C:lI101|HB7B46FFC +B7B46FFC:lI114|HB7B4706C +B7B4706C:lI108|HB7B470DC +B7B470DC:lI97|HB7B4714C +B7B4714C:lI110|HB7B471BC +B7B471BC:lI103|HB7B4722C +B7B4722C:lI47|HB7B4729C +B7B4729C:lI108|HB7B4730C +B7B4730C:lI105|HB7B4737C +B7B4737C:lI98|HB7B473EC +B7B473EC:lI47|HB7B4745C +B7B4745C:lI115|HB7B474CC +B7B474CC:lI116|HB7B4753C +B7B4753C:lI100|HB7B475AC +B7B475AC:lI108|HB7B4761C +B7B4761C:lI105|HB7B4768C +B7B4768C:lI98|HB7B476FC +B7B476FC:lI45|HB7B4776C +B7B4776C:lI49|HB7B477DC +B7B477DC:lI46|HB7B4784C +B7B4784C:lI49|HB7B478BC +B7B478BC:lI54|HB7B4792C +B7B4792C:lI46|HB7B4799C +B7B4799C:lI51|HB7B47A0C +B7B47A0C:lI47|HB7B47A7C +B7B47A7C:lI101|HB7B47AEC +B7B47AEC:lI98|HB7B47B5C +B7B47B5C:lI105|HB7B47BCC +B7B47BCC:lI110|HB7B47C3C +B7B47C3C:lI47|HB7B47CAC +B7B47CAC:lI108|HB7B47D1C +B7B47D1C:lI105|HB7B47D8C +B7B47D8C:lI115|HB7B47DFC +B7B47DFC:lI116|HB7B47E6C +B7B47E6C:lI115|HB7B47ED4 +B7B47ED4:lI46|HB7B47F34 +B7B47F34:lI98|HB7B47F94 +B7B47F94:lI101|HB7B47FF4 +B7B47FF4:lI97|HB7B4804C +B7B4804C:lI109|N +B7B4680C:lHB7B46894|HB7B468A0 +B7B46894:t2:A16:application_controller,HB7B46940 +B7B46940:lI47|HB7B469D8 +B7B469D8:lI117|HB7B46A3C +B7B46A3C:lI115|HB7B46AA8 +B7B46AA8:lI114|HB7B46B1C +B7B46B1C:lI47|HB7B46B98 +B7B46B98:lI108|HB7B46C14 +B7B46C14:lI111|HB7B46C84 +B7B46C84:lI99|HB7B46CF4 +B7B46CF4:lI97|HB7B46D64 +B7B46D64:lI108|HB7B46DD4 +B7B46DD4:lI47|HB7B46E44 +B7B46E44:lI108|HB7B46EB4 +B7B46EB4:lI105|HB7B46F24 +B7B46F24:lI98|HB7B46F94 +B7B46F94:lI47|HB7B47004 +B7B47004:lI101|HB7B47074 +B7B47074:lI114|HB7B470E4 +B7B470E4:lI108|HB7B47154 +B7B47154:lI97|HB7B471C4 +B7B471C4:lI110|HB7B47234 +B7B47234:lI103|HB7B472A4 +B7B472A4:lI47|HB7B47314 +B7B47314:lI108|HB7B47384 +B7B47384:lI105|HB7B473F4 +B7B473F4:lI98|HB7B47464 +B7B47464:lI47|HB7B474D4 +B7B474D4:lI107|HB7B47544 +B7B47544:lI101|HB7B475B4 +B7B475B4:lI114|HB7B47624 +B7B47624:lI110|HB7B47694 +B7B47694:lI101|HB7B47704 +B7B47704:lI108|HB7B47774 +B7B47774:lI45|HB7B477E4 +B7B477E4:lI50|HB7B47854 +B7B47854:lI46|HB7B478C4 +B7B478C4:lI49|HB7B47934 +B7B47934:lI51|HB7B479A4 +B7B479A4:lI46|HB7B47A14 +B7B47A14:lI51|HB7B47A84 +B7B47A84:lI47|HB7B47AF4 +B7B47AF4:lI101|HB7B47B64 +B7B47B64:lI98|HB7B47BD4 +B7B47BD4:lI105|HB7B47C44 +B7B47C44:lI110|HB7B47CB4 +B7B47CB4:lI47|HB7B47D24 +B7B47D24:lI97|HB7B47D94 +B7B47D94:lI112|HB7B47E04 +B7B47E04:lI112|HB7B47E74 +B7B47E74:lI108|HB7B47EDC +B7B47EDC:lI105|HB7B47F3C +B7B47F3C:lI99|HB7B47F9C +B7B47F9C:lI97|HB7B47FFC +B7B47FFC:lI116|HB7B48054 +B7B48054:lI105|HB7B480A4 +B7B480A4:lI111|HB7B480F4 +B7B480F4:lI110|HB7B4813C +B7B4813C:lI95|HB7B48184 +B7B48184:lI99|HB7B481BC +B7B481BC:lI111|HB7B481F4 +B7B481F4:lI110|HB7B48224 +B7B48224:lI116|HB7B4824C +B7B4824C:lI114|HB7B48274 +B7B48274:lI111|HB7B4828C +B7B4828C:lI108|HB7B482A4 +B7B482A4:lI108|HB7B482BC +B7B482BC:lI101|HB7B482D4 +B7B482D4:lI114|HB7B482E4 +B7B482E4:lI46|HB7B482F4 +B7B482F4:lI98|HB7B48304 +B7B48304:lI101|HB7B4830C +B7B4830C:lI97|HB7B48314 +B7B48314:lI109|N +B7B468A0:lHB7B46948|HB7B46954 +B7B46948:t2:A8:proc_lib,HB7B469E0 +B7B469E0:lI47|HB7B46A44 +B7B46A44:lI117|HB7B46AB0 +B7B46AB0:lI115|HB7B46B24 +B7B46B24:lI114|HB7B46BA0 +B7B46BA0:lI47|HB7B46C1C +B7B46C1C:lI108|HB7B46C8C +B7B46C8C:lI111|HB7B46CFC +B7B46CFC:lI99|HB7B46D6C +B7B46D6C:lI97|HB7B46DDC +B7B46DDC:lI108|HB7B46E4C +B7B46E4C:lI47|HB7B46EBC +B7B46EBC:lI108|HB7B46F2C +B7B46F2C:lI105|HB7B46F9C +B7B46F9C:lI98|HB7B4700C +B7B4700C:lI47|HB7B4707C +B7B4707C:lI101|HB7B470EC +B7B470EC:lI114|HB7B4715C +B7B4715C:lI108|HB7B471CC +B7B471CC:lI97|HB7B4723C +B7B4723C:lI110|HB7B472AC +B7B472AC:lI103|HB7B4731C +B7B4731C:lI47|HB7B4738C +B7B4738C:lI108|HB7B473FC +B7B473FC:lI105|HB7B4746C +B7B4746C:lI98|HB7B474DC +B7B474DC:lI47|HB7B4754C +B7B4754C:lI115|HB7B475BC +B7B475BC:lI116|HB7B4762C +B7B4762C:lI100|HB7B4769C +B7B4769C:lI108|HB7B4770C +B7B4770C:lI105|HB7B4777C +B7B4777C:lI98|HB7B477EC +B7B477EC:lI45|HB7B4785C +B7B4785C:lI49|HB7B478CC +B7B478CC:lI46|HB7B4793C +B7B4793C:lI49|HB7B479AC +B7B479AC:lI54|HB7B47A1C +B7B47A1C:lI46|HB7B47A8C +B7B47A8C:lI51|HB7B47AFC +B7B47AFC:lI47|HB7B47B6C +B7B47B6C:lI101|HB7B47BDC +B7B47BDC:lI98|HB7B47C4C +B7B47C4C:lI105|HB7B47CBC +B7B47CBC:lI110|HB7B47D2C +B7B47D2C:lI47|HB7B47D9C +B7B47D9C:lI112|HB7B47E0C +B7B47E0C:lI114|HB7B47E7C +B7B47E7C:lI111|HB7B47EE4 +B7B47EE4:lI99|HB7B47F44 +B7B47F44:lI95|HB7B47FA4 +B7B47FA4:lI108|HB7B48004 +B7B48004:lI105|HB7B4805C +B7B4805C:lI98|HB7B480AC +B7B480AC:lI46|HB7B480FC +B7B480FC:lI98|HB7B48144 +B7B48144:lI101|HB7B4818C +B7B4818C:lI97|HB7B481C4 +B7B481C4:lI109|N +B7B46954:lHB7B469E8|HB7B469F4 +B7B469E8:t2:A3:gen,HB7B46A4C +B7B46A4C:lI47|HB7B46AB8 +B7B46AB8:lI117|HB7B46B2C +B7B46B2C:lI115|HB7B46BA8 +B7B46BA8:lI114|HB7B46C24 +B7B46C24:lI47|HB7B46C94 +B7B46C94:lI108|HB7B46D04 +B7B46D04:lI111|HB7B46D74 +B7B46D74:lI99|HB7B46DE4 +B7B46DE4:lI97|HB7B46E54 +B7B46E54:lI108|HB7B46EC4 +B7B46EC4:lI47|HB7B46F34 +B7B46F34:lI108|HB7B46FA4 +B7B46FA4:lI105|HB7B47014 +B7B47014:lI98|HB7B47084 +B7B47084:lI47|HB7B470F4 +B7B470F4:lI101|HB7B47164 +B7B47164:lI114|HB7B471D4 +B7B471D4:lI108|HB7B47244 +B7B47244:lI97|HB7B472B4 +B7B472B4:lI110|HB7B47324 +B7B47324:lI103|HB7B47394 +B7B47394:lI47|HB7B47404 +B7B47404:lI108|HB7B47474 +B7B47474:lI105|HB7B474E4 +B7B474E4:lI98|HB7B47554 +B7B47554:lI47|HB7B475C4 +B7B475C4:lI115|HB7B47634 +B7B47634:lI116|HB7B476A4 +B7B476A4:lI100|HB7B47714 +B7B47714:lI108|HB7B47784 +B7B47784:lI105|HB7B477F4 +B7B477F4:lI98|HB7B47864 +B7B47864:lI45|HB7B478D4 +B7B478D4:lI49|HB7B47944 +B7B47944:lI46|HB7B479B4 +B7B479B4:lI49|HB7B47A24 +B7B47A24:lI54|HB7B47A94 +B7B47A94:lI46|HB7B47B04 +B7B47B04:lI51|HB7B47B74 +B7B47B74:lI47|HB7B47BE4 +B7B47BE4:lI101|HB7B47C54 +B7B47C54:lI98|HB7B47CC4 +B7B47CC4:lI105|HB7B47D34 +B7B47D34:lI110|HB7B47DA4 +B7B47DA4:lI47|HB7B47E14 +B7B47E14:lI103|HB7B47E84 +B7B47E84:lI101|HB7B47EEC +B7B47EEC:lI110|HB7B47F4C +B7B47F4C:lI46|HB7B47FAC +B7B47FAC:lI98|HB7B4800C +B7B4800C:lI101|HB7B48064 +B7B48064:lI97|HB7B480B4 +B7B480B4:lI109|N +B7B469F4:lHB7B46A54|HB7B46A60 +B7B46A54:t2:A9:gen_event,HB7B46AC0 +B7B46AC0:lI47|HB7B46B34 +B7B46B34:lI117|HB7B46BB0 +B7B46BB0:lI115|HB7B46C2C +B7B46C2C:lI114|HB7B46C9C +B7B46C9C:lI47|HB7B46D0C +B7B46D0C:lI108|HB7B46D7C +B7B46D7C:lI111|HB7B46DEC +B7B46DEC:lI99|HB7B46E5C +B7B46E5C:lI97|HB7B46ECC +B7B46ECC:lI108|HB7B46F3C +B7B46F3C:lI47|HB7B46FAC +B7B46FAC:lI108|HB7B4701C +B7B4701C:lI105|HB7B4708C +B7B4708C:lI98|HB7B470FC +B7B470FC:lI47|HB7B4716C +B7B4716C:lI101|HB7B471DC +B7B471DC:lI114|HB7B4724C +B7B4724C:lI108|HB7B472BC +B7B472BC:lI97|HB7B4732C +B7B4732C:lI110|HB7B4739C +B7B4739C:lI103|HB7B4740C +B7B4740C:lI47|HB7B4747C +B7B4747C:lI108|HB7B474EC +B7B474EC:lI105|HB7B4755C +B7B4755C:lI98|HB7B475CC +B7B475CC:lI47|HB7B4763C +B7B4763C:lI115|HB7B476AC +B7B476AC:lI116|HB7B4771C +B7B4771C:lI100|HB7B4778C +B7B4778C:lI108|HB7B477FC +B7B477FC:lI105|HB7B4786C +B7B4786C:lI98|HB7B478DC +B7B478DC:lI45|HB7B4794C +B7B4794C:lI49|HB7B479BC +B7B479BC:lI46|HB7B47A2C +B7B47A2C:lI49|HB7B47A9C +B7B47A9C:lI54|HB7B47B0C +B7B47B0C:lI46|HB7B47B7C +B7B47B7C:lI51|HB7B47BEC +B7B47BEC:lI47|HB7B47C5C +B7B47C5C:lI101|HB7B47CCC +B7B47CCC:lI98|HB7B47D3C +B7B47D3C:lI105|HB7B47DAC +B7B47DAC:lI110|HB7B47E1C +B7B47E1C:lI47|HB7B47E8C +B7B47E8C:lI103|HB7B47EF4 +B7B47EF4:lI101|HB7B47F54 +B7B47F54:lI110|HB7B47FB4 +B7B47FB4:lI95|HB7B48014 +B7B48014:lI101|HB7B4806C +B7B4806C:lI118|HB7B480BC +B7B480BC:lI101|HB7B48104 +B7B48104:lI110|HB7B4814C +B7B4814C:lI116|HB7B48194 +B7B48194:lI46|HB7B481CC +B7B481CC:lI98|HB7B481FC +B7B481FC:lI101|HB7B4822C +B7B4822C:lI97|HB7B48254 +B7B48254:lI109|N +B7B46A60:lHB7B46AC8|HB7B46AD4 +B7B46AC8:t2:AC:error_logger,HB7B46B3C +B7B46B3C:lI47|HB7B46BB8 +B7B46BB8:lI117|HB7B46C34 +B7B46C34:lI115|HB7B46CA4 +B7B46CA4:lI114|HB7B46D14 +B7B46D14:lI47|HB7B46D84 +B7B46D84:lI108|HB7B46DF4 +B7B46DF4:lI111|HB7B46E64 +B7B46E64:lI99|HB7B46ED4 +B7B46ED4:lI97|HB7B46F44 +B7B46F44:lI108|HB7B46FB4 +B7B46FB4:lI47|HB7B47024 +B7B47024:lI108|HB7B47094 +B7B47094:lI105|HB7B47104 +B7B47104:lI98|HB7B47174 +B7B47174:lI47|HB7B471E4 +B7B471E4:lI101|HB7B47254 +B7B47254:lI114|HB7B472C4 +B7B472C4:lI108|HB7B47334 +B7B47334:lI97|HB7B473A4 +B7B473A4:lI110|HB7B47414 +B7B47414:lI103|HB7B47484 +B7B47484:lI47|HB7B474F4 +B7B474F4:lI108|HB7B47564 +B7B47564:lI105|HB7B475D4 +B7B475D4:lI98|HB7B47644 +B7B47644:lI47|HB7B476B4 +B7B476B4:lI107|HB7B47724 +B7B47724:lI101|HB7B47794 +B7B47794:lI114|HB7B47804 +B7B47804:lI110|HB7B47874 +B7B47874:lI101|HB7B478E4 +B7B478E4:lI108|HB7B47954 +B7B47954:lI45|HB7B479C4 +B7B479C4:lI50|HB7B47A34 +B7B47A34:lI46|HB7B47AA4 +B7B47AA4:lI49|HB7B47B14 +B7B47B14:lI51|HB7B47B84 +B7B47B84:lI46|HB7B47BF4 +B7B47BF4:lI51|HB7B47C64 +B7B47C64:lI47|HB7B47CD4 +B7B47CD4:lI101|HB7B47D44 +B7B47D44:lI98|HB7B47DB4 +B7B47DB4:lI105|HB7B47E24 +B7B47E24:lI110|HB7B47E94 +B7B47E94:lI47|HB7B47EFC +B7B47EFC:lI101|HB7B47F5C +B7B47F5C:lI114|HB7B47FBC +B7B47FBC:lI114|HB7B4801C +B7B4801C:lI111|HB7B48074 +B7B48074:lI114|HB7B480C4 +B7B480C4:lI95|HB7B4810C +B7B4810C:lI108|HB7B48154 +B7B48154:lI111|HB7B4819C +B7B4819C:lI103|HB7B481D4 +B7B481D4:lI103|HB7B48204 +B7B48204:lI101|HB7B48234 +B7B48234:lI114|HB7B4825C +B7B4825C:lI46|HB7B4827C +B7B4827C:lI98|HB7B48294 +B7B48294:lI101|HB7B482AC +B7B482AC:lI97|HB7B482C4 +B7B482C4:lI109|N +B7B46AD4:lHB7B46B44|HB7B46B50 +B7B46B44:t2:A5:heart,HB7B46BC0 +B7B46BC0:lI47|HB7B46C3C +B7B46C3C:lI117|HB7B46CAC +B7B46CAC:lI115|HB7B46D1C +B7B46D1C:lI114|HB7B46D8C +B7B46D8C:lI47|HB7B46DFC +B7B46DFC:lI108|HB7B46E6C +B7B46E6C:lI111|HB7B46EDC +B7B46EDC:lI99|HB7B46F4C +B7B46F4C:lI97|HB7B46FBC +B7B46FBC:lI108|HB7B4702C +B7B4702C:lI47|HB7B4709C +B7B4709C:lI108|HB7B4710C +B7B4710C:lI105|HB7B4717C +B7B4717C:lI98|HB7B471EC +B7B471EC:lI47|HB7B4725C +B7B4725C:lI101|HB7B472CC +B7B472CC:lI114|HB7B4733C +B7B4733C:lI108|HB7B473AC +B7B473AC:lI97|HB7B4741C +B7B4741C:lI110|HB7B4748C +B7B4748C:lI103|HB7B474FC +B7B474FC:lI47|HB7B4756C +B7B4756C:lI108|HB7B475DC +B7B475DC:lI105|HB7B4764C +B7B4764C:lI98|HB7B476BC +B7B476BC:lI47|HB7B4772C +B7B4772C:lI107|HB7B4779C +B7B4779C:lI101|HB7B4780C +B7B4780C:lI114|HB7B4787C +B7B4787C:lI110|HB7B478EC +B7B478EC:lI101|HB7B4795C +B7B4795C:lI108|HB7B479CC +B7B479CC:lI45|HB7B47A3C +B7B47A3C:lI50|HB7B47AAC +B7B47AAC:lI46|HB7B47B1C +B7B47B1C:lI49|HB7B47B8C +B7B47B8C:lI51|HB7B47BFC +B7B47BFC:lI46|HB7B47C6C +B7B47C6C:lI51|HB7B47CDC +B7B47CDC:lI47|HB7B47D4C +B7B47D4C:lI101|HB7B47DBC +B7B47DBC:lI98|HB7B47E2C +B7B47E2C:lI105|HB7B47E9C +B7B47E9C:lI110|HB7B47F04 +B7B47F04:lI47|HB7B47F64 +B7B47F64:lI104|HB7B47FC4 +B7B47FC4:lI101|HB7B48024 +B7B48024:lI97|HB7B4807C +B7B4807C:lI114|HB7B480CC +B7B480CC:lI116|HB7B48114 +B7B48114:lI46|HB7B4815C +B7B4815C:lI98|HB7B481A4 +B7B481A4:lI101|HB7B481DC +B7B481DC:lI97|HB7B4820C +B7B4820C:lI109|N +B7B46B50:lHB7B46BC8|N +B7B46BC8:t2:AD:error_handler,HB7B46C44 +B7B46C44:lI47|HB7B46CB4 +B7B46CB4:lI117|HB7B46D24 +B7B46D24:lI115|HB7B46D94 +B7B46D94:lI114|HB7B46E04 +B7B46E04:lI47|HB7B46E74 +B7B46E74:lI108|HB7B46EE4 +B7B46EE4:lI111|HB7B46F54 +B7B46F54:lI99|HB7B46FC4 +B7B46FC4:lI97|HB7B47034 +B7B47034:lI108|HB7B470A4 +B7B470A4:lI47|HB7B47114 +B7B47114:lI108|HB7B47184 +B7B47184:lI105|HB7B471F4 +B7B471F4:lI98|HB7B47264 +B7B47264:lI47|HB7B472D4 +B7B472D4:lI101|HB7B47344 +B7B47344:lI114|HB7B473B4 +B7B473B4:lI108|HB7B47424 +B7B47424:lI97|HB7B47494 +B7B47494:lI110|HB7B47504 +B7B47504:lI103|HB7B47574 +B7B47574:lI47|HB7B475E4 +B7B475E4:lI108|HB7B47654 +B7B47654:lI105|HB7B476C4 +B7B476C4:lI98|HB7B47734 +B7B47734:lI47|HB7B477A4 +B7B477A4:lI107|HB7B47814 +B7B47814:lI101|HB7B47884 +B7B47884:lI114|HB7B478F4 +B7B478F4:lI110|HB7B47964 +B7B47964:lI101|HB7B479D4 +B7B479D4:lI108|HB7B47A44 +B7B47A44:lI45|HB7B47AB4 +B7B47AB4:lI50|HB7B47B24 +B7B47B24:lI46|HB7B47B94 +B7B47B94:lI49|HB7B47C04 +B7B47C04:lI51|HB7B47C74 +B7B47C74:lI46|HB7B47CE4 +B7B47CE4:lI51|HB7B47D54 +B7B47D54:lI47|HB7B47DC4 +B7B47DC4:lI101|HB7B47E34 +B7B47E34:lI98|HB7B47EA4 +B7B47EA4:lI105|HB7B47F0C +B7B47F0C:lI110|HB7B47F6C +B7B47F6C:lI47|HB7B47FCC +B7B47FCC:lI101|HB7B4802C +B7B4802C:lI114|HB7B48084 +B7B48084:lI114|HB7B480D4 +B7B480D4:lI111|HB7B4811C +B7B4811C:lI114|HB7B48164 +B7B48164:lI95|HB7B481AC +B7B481AC:lI104|HB7B481E4 +B7B481E4:lI97|HB7B48214 +B7B48214:lI110|HB7B4823C +B7B4823C:lI100|HB7B48264 +B7B48264:lI108|HB7B48284 +B7B48284:lI101|HB7B4829C +B7B4829C:lI114|HB7B482B4 +B7B482B4:lI46|HB7B482CC +B7B482CC:lI98|HB7B482DC +B7B482DC:lI101|HB7B482EC +B7B482EC:lI97|HB7B482FC +B7B482FC:lI109|N +B7B464EC:t2:HB7B46538,HB7B46540 +B7B46540:lI82|HB7B465A4 +B7B465A4:lI49|HB7B46620 +B7B46620:lI51|HB7B4667C +B7B4667C:lI66|HB7B466EC +B7B466EC:lI48|HB7B46758 +B7B46758:lI50|N +B7B46538:lI79|HB7B4659C +B7B4659C:lI84|HB7B46618 +B7B46618:lI80|HB7B46674 +B7B46674:lI32|HB7B466E4 +B7B466E4:lI32|HB7B46750 +B7B46750:lI65|HB7B467C8 +B7B467C8:lI80|HB7B46854 +B7B46854:lI78|HB7B46900 +B7B46900:lI32|HB7B46998 +B7B46998:lI49|HB7B469FC +B7B469FC:lI56|HB7B46A68 +B7B46A68:lI49|HB7B46ADC +B7B46ADC:lI32|HB7B46B58 +B7B46B58:lI48|HB7B46BD4 +B7B46BD4:lI49|N +B7B464E0:t2:A8:starting,A13:applications_loaded +B7B464D8:lHB7B46524|HB7B46530 +B7B46524:t2:A16:application_controller,P<0.5.0> +B7B46530:lHB7B46588|HB7B46594 +B7B46588:t2:AC:error_logger,P<0.4.0> +B7B46594:lHB7B4660C|N +B7B4660C:t2:AF:erl_prim_loader,P<0.2.0> +B7B464D0:lHB7B46514|HB7B4651C +B7B46514:lA8:reloader|N +B7B4651C:lHB7B46578|HB7B46580 +B7B46578:lAA:webmachine|N +B7B46580:lHB7B46604|N +B7B46604:lA6:erlrrd|N +B7B464C8:lHB7B46500|HB7B4650C +B7B46500:t2:A5:-root,HB7B4655C +B7B4655C:lHB7B465C8|N +B7B465C8:Yh15:2F7573722F6C6F63616C2F6C69622F65726C616E67 +B7B4650C:lHB7B46564|HB7B46570 +B7B46564:t2:A9:-progname,HB7B465E8 +B7B465E8:lHB7B4664C|N +B7B4664C:Yh3:65726C +B7B46570:lHB7B465F0|HB7B465FC +B7B465F0:t2:A5:-home,HB7B46658 +B7B46658:lHB7B466B0|N +B7B466B0:YhE:2F686F6D652F6963617275733735 +B7B465FC:lHB7B46660|HB7B4666C +B7B46660:t2:A4:-smp,HB7B466C8 +B7B466C8:lHB7B46728|N +B7B46728:Yh4:6175746F +B7B4666C:lHB7B466D0|HB7B466DC +B7B466D0:t2:A5:-name,HB7B46734 +B7B46734:lHB7B4679C|N +B7B4679C:YhE:61706940666C756B736F2E6E6574 +B7B466DC:lHB7B4673C|HB7B46748 +B7B4673C:t2:A3:-pa,HB7B467B4 +B7B467B4:lHB7B46814|HB7B46844 +B7B46814:Yh25:2F686F6D652F69636172757337352F65726C616E672F7765626D616368696E652F6562696E +B7B46844:lHB7B468A8|HB7B468E4 +B7B468A8:Yh31:2F686F6D652F69636172757337352F65726C616E672F7765626D616368696E652F646570732F65726C7272642F6562696E +B7B468E4:lHB7B4695C|N +B7B4695C:Yh33:2F686F6D652F69636172757337352F65726C616E672F7765626D616368696E652F646570732F6D6F6368697765622F6562696E +B7B46748:lHB7B467BC|N +B7B467BC:t2:A5:-boot,HB7B4684C +B7B4684C:lHB7B468EC|N +B7B468EC:YhA:73746172745F7361736C +B58DAAD8:t3:A4:EXIT,P<0.1.0>,HB58DAAE8 +B58DAAE8:t2:HB58DAD60,HB58DAAF4 +B58DAAF4:t3:AA:gen_server,A4:call,HB58DAB20 +B58DAB20:lA16:application_controller|HB58DAB04 +B58DAB04:lHB58DAB0C|HB58DAB18 +B58DAB0C:t2:A10:load_application,A6:stdlib +B58DAB18:lA8:infinity|N +B58DAD60:lI123|HB58DAD58 +B58DAD58:lI97|HB58DAD50 +B58DAD50:lI112|HB58DAD48 +B58DAD48:lI112|HB58DAD40 +B58DAD40:lI108|HB58DAD38 +B58DAD38:lI105|HB58DAD30 +B58DAD30:lI99|HB58DAD28 +B58DAD28:lI97|HB58DAD20 +B58DAD20:lI116|HB58DAD18 +B58DAD18:lI105|HB58DAD10 +B58DAD10:lI111|HB58DAD08 +B58DAD08:lI110|HB58DAD00 +B58DAD00:lI95|HB58DACF8 +B58DACF8:lI115|HB58DACF0 +B58DACF0:lI116|HB58DACE8 +B58DACE8:lI97|HB58DACE0 +B58DACE0:lI114|HB58DACD8 +B58DACD8:lI116|HB58DACD0 +B58DACD0:lI95|HB58DACC8 +B58DACC8:lI102|HB58DACC0 +B58DACC0:lI97|HB58DACB8 +B58DACB8:lI105|HB58DACB0 +B58DACB0:lI108|HB58DACA8 +B58DACA8:lI117|HB58DACA0 +B58DACA0:lI114|HB58DAC98 +B58DAC98:lI101|HB58DAC90 +B58DAC90:lI44|HB58DAC88 +B58DAC88:lI107|HB58DAC80 +B58DAC80:lI101|HB58DAC78 +B58DAC78:lI114|HB58DAC70 +B58DAC70:lI110|HB58DAC68 +B58DAC68:lI101|HB58DAC60 +B58DAC60:lI108|HB58DAC58 +B58DAC58:lI44|HB58DAC50 +B58DAC50:lI123|HB58DAC48 +B58DAC48:lI115|HB58DAC40 +B58DAC40:lI104|HB58DAC38 +B58DAC38:lI117|HB58DAC30 +B58DAC30:lI116|HB58DAC28 +B58DAC28:lI100|HB58DAC20 +B58DAC20:lI111|HB58DAC18 +B58DAC18:lI119|HB58DAC10 +B58DAC10:lI110|HB58DAC08 +B58DAC08:lI44|HB58DAC00 +B58DAC00:lI123|HB58DABF8 +B58DABF8:lI107|HB58DABF0 +B58DABF0:lI101|HB58DABE8 +B58DABE8:lI114|HB58DABE0 +B58DABE0:lI110|HB58DABD8 +B58DABD8:lI101|HB58DABD0 +B58DABD0:lI108|HB58DABC8 +B58DABC8:lI44|HB58DABC0 +B58DABC0:lI115|HB58DABB8 +B58DABB8:lI116|HB58DABB0 +B58DABB0:lI97|HB58DABA8 +B58DABA8:lI114|HB58DABA0 +B58DABA0:lI116|HB58DAB98 +B58DAB98:lI44|HB58DAB90 +B58DAB90:lI91|HB58DAB88 +B58DAB88:lI110|HB58DAB80 +B58DAB80:lI111|HB58DAB78 +B58DAB78:lI114|HB58DAB70 +B58DAB70:lI109|HB58DAB68 +B58DAB68:lI97|HB58DAB60 +B58DAB60:lI108|HB58DAB58 +B58DAB58:lI44|HB58DAB50 +B58DAB50:lI91|HB58DAB48 +B58DAB48:lI93|HB58DAB40 +B58DAB40:lI93|HB58DAB38 +B58DAB38:lI125|HB58DAB30 +B58DAB30:lI125|HB58DAB28 +B58DAB28:lI125|N +=proc_stack:<0.2.0> +0xb7b24dcc:SReturn addr 0x8248774 () +y0:N +y1:HB7B1E0DC +y2:P<0.1.0> +y3:HB7B2466C +y4:A8:infinity +=proc_heap:<0.2.0> +B7B1E0DC:lHB7B1E100|HB7B1E108 +B7B1E100:lI47|HB7B1E120 +B7B1E120:lI104|HB7B1E148 +B7B1E148:lI111|HB7B1E178 +B7B1E178:lI109|HB7B1E1B0 +B7B1E1B0:lI101|HB7B1E1E8 +B7B1E1E8:lI47|HB7B1E220 +B7B1E220:lI105|HB7B1E258 +B7B1E258:lI99|HB7B1E290 +B7B1E290:lI97|HB7B1E2C8 +B7B1E2C8:lI114|HB7B1E300 +B7B1E300:lI117|HB7B1E338 +B7B1E338:lI115|HB7B1E370 +B7B1E370:lI55|HB7B1E3A0 +B7B1E3A0:lI53|HB7B1E3D0 +B7B1E3D0:lI47|HB7B1E400 +B7B1E400:lI101|HB7B1E430 +B7B1E430:lI114|HB7B1E460 +B7B1E460:lI108|HB7B1E490 +B7B1E490:lI97|HB7B1E4C0 +B7B1E4C0:lI110|HB7B1E4F0 +B7B1E4F0:lI103|HB7B1E520 +B7B1E520:lI47|HB7B1E550 +B7B1E550:lI119|HB7B1E580 +B7B1E580:lI101|HB7B1E5B0 +B7B1E5B0:lI98|HB7B1E5E0 +B7B1E5E0:lI109|HB7B1E610 +B7B1E610:lI97|HB7B1E640 +B7B1E640:lI99|HB7B1E670 +B7B1E670:lI104|HB7B1E6A0 +B7B1E6A0:lI105|HB7B1E6D0 +B7B1E6D0:lI110|HB7B1E700 +B7B1E700:lI101|HB7B1E730 +B7B1E730:lI47|HB7B1E760 +B7B1E760:lI101|HB7B1E790 +B7B1E790:lI98|HB7B1E7C0 +B7B1E7C0:lI105|HB7B1E7F0 +B7B1E7F0:lI110|N +B7B1E108:lHB7B1E128|HB7B1E130 +B7B1E128:lI47|HB7B1E150 +B7B1E150:lI104|HB7B1E180 +B7B1E180:lI111|HB7B1E1B8 +B7B1E1B8:lI109|HB7B1E1F0 +B7B1E1F0:lI101|HB7B1E228 +B7B1E228:lI47|HB7B1E260 +B7B1E260:lI105|HB7B1E298 +B7B1E298:lI99|HB7B1E2D0 +B7B1E2D0:lI97|HB7B1E308 +B7B1E308:lI114|HB7B1E340 +B7B1E340:lI117|HB7B1E378 +B7B1E378:lI115|HB7B1E3A8 +B7B1E3A8:lI55|HB7B1E3D8 +B7B1E3D8:lI53|HB7B1E408 +B7B1E408:lI47|HB7B1E438 +B7B1E438:lI101|HB7B1E468 +B7B1E468:lI114|HB7B1E498 +B7B1E498:lI108|HB7B1E4C8 +B7B1E4C8:lI97|HB7B1E4F8 +B7B1E4F8:lI110|HB7B1E528 +B7B1E528:lI103|HB7B1E558 +B7B1E558:lI47|HB7B1E588 +B7B1E588:lI119|HB7B1E5B8 +B7B1E5B8:lI101|HB7B1E5E8 +B7B1E5E8:lI98|HB7B1E618 +B7B1E618:lI109|HB7B1E648 +B7B1E648:lI97|HB7B1E678 +B7B1E678:lI99|HB7B1E6A8 +B7B1E6A8:lI104|HB7B1E6D8 +B7B1E6D8:lI105|HB7B1E708 +B7B1E708:lI110|HB7B1E738 +B7B1E738:lI101|HB7B1E768 +B7B1E768:lI47|HB7B1E798 +B7B1E798:lI100|HB7B1E7C8 +B7B1E7C8:lI101|HB7B1E7F8 +B7B1E7F8:lI112|HB7B1E820 +B7B1E820:lI115|HB7B1E848 +B7B1E848:lI47|HB7B1E870 +B7B1E870:lI101|HB7B1E898 +B7B1E898:lI114|HB7B1E8B8 +B7B1E8B8:lI108|HB7B1E8D8 +B7B1E8D8:lI114|HB7B1E8F8 +B7B1E8F8:lI114|HB7B1E918 +B7B1E918:lI100|HB7B1E938 +B7B1E938:lI47|HB7B1E958 +B7B1E958:lI101|HB7B1E978 +B7B1E978:lI98|HB7B1E990 +B7B1E990:lI105|HB7B1E9A0 +B7B1E9A0:lI110|N +B7B1E130:lHB7B1E158|HB7B1E160 +B7B1E158:lI47|HB7B1E188 +B7B1E188:lI104|HB7B1E1C0 +B7B1E1C0:lI111|HB7B1E1F8 +B7B1E1F8:lI109|HB7B1E230 +B7B1E230:lI101|HB7B1E268 +B7B1E268:lI47|HB7B1E2A0 +B7B1E2A0:lI105|HB7B1E2D8 +B7B1E2D8:lI99|HB7B1E310 +B7B1E310:lI97|HB7B1E348 +B7B1E348:lI114|HB7B1E380 +B7B1E380:lI117|HB7B1E3B0 +B7B1E3B0:lI115|HB7B1E3E0 +B7B1E3E0:lI55|HB7B1E410 +B7B1E410:lI53|HB7B1E440 +B7B1E440:lI47|HB7B1E470 +B7B1E470:lI101|HB7B1E4A0 +B7B1E4A0:lI114|HB7B1E4D0 +B7B1E4D0:lI108|HB7B1E500 +B7B1E500:lI97|HB7B1E530 +B7B1E530:lI110|HB7B1E560 +B7B1E560:lI103|HB7B1E590 +B7B1E590:lI47|HB7B1E5C0 +B7B1E5C0:lI119|HB7B1E5F0 +B7B1E5F0:lI101|HB7B1E620 +B7B1E620:lI98|HB7B1E650 +B7B1E650:lI109|HB7B1E680 +B7B1E680:lI97|HB7B1E6B0 +B7B1E6B0:lI99|HB7B1E6E0 +B7B1E6E0:lI104|HB7B1E710 +B7B1E710:lI105|HB7B1E740 +B7B1E740:lI110|HB7B1E770 +B7B1E770:lI101|HB7B1E7A0 +B7B1E7A0:lI47|HB7B1E7D0 +B7B1E7D0:lI100|HB7B1E800 +B7B1E800:lI101|HB7B1E828 +B7B1E828:lI112|HB7B1E850 +B7B1E850:lI115|HB7B1E878 +B7B1E878:lI47|HB7B1E8A0 +B7B1E8A0:lI109|HB7B1E8C0 +B7B1E8C0:lI111|HB7B1E8E0 +B7B1E8E0:lI99|HB7B1E900 +B7B1E900:lI104|HB7B1E920 +B7B1E920:lI105|HB7B1E940 +B7B1E940:lI119|HB7B1E960 +B7B1E960:lI101|HB7B1E980 +B7B1E980:lI98|HB7B1E998 +B7B1E998:lI47|HB7B1E9A8 +B7B1E9A8:lI101|HB7B1E9B0 +B7B1E9B0:lI98|HB7B1E9B8 +B7B1E9B8:lI105|HB7B1E9C0 +B7B1E9C0:lI110|N +B7B1E160:lHB7B1E190|HB7B1E198 +B7B1E190:lI47|HB7B1E1C8 +B7B1E1C8:lI117|HB7B1E200 +B7B1E200:lI115|HB7B1E238 +B7B1E238:lI114|HB7B1E270 +B7B1E270:lI47|HB7B1E2A8 +B7B1E2A8:lI108|HB7B1E2E0 +B7B1E2E0:lI111|HB7B1E318 +B7B1E318:lI99|HB7B1E350 +B7B1E350:lI97|HB7B1E388 +B7B1E388:lI108|HB7B1E3B8 +B7B1E3B8:lI47|HB7B1E3E8 +B7B1E3E8:lI108|HB7B1E418 +B7B1E418:lI105|HB7B1E448 +B7B1E448:lI98|HB7B1E478 +B7B1E478:lI47|HB7B1E4A8 +B7B1E4A8:lI101|HB7B1E4D8 +B7B1E4D8:lI114|HB7B1E508 +B7B1E508:lI108|HB7B1E538 +B7B1E538:lI97|HB7B1E568 +B7B1E568:lI110|HB7B1E598 +B7B1E598:lI103|HB7B1E5C8 +B7B1E5C8:lI47|HB7B1E5F8 +B7B1E5F8:lI108|HB7B1E628 +B7B1E628:lI105|HB7B1E658 +B7B1E658:lI98|HB7B1E688 +B7B1E688:lI47|HB7B1E6B8 +B7B1E6B8:lI107|HB7B1E6E8 +B7B1E6E8:lI101|HB7B1E718 +B7B1E718:lI114|HB7B1E748 +B7B1E748:lI110|HB7B1E778 +B7B1E778:lI101|HB7B1E7A8 +B7B1E7A8:lI108|HB7B1E7D8 +B7B1E7D8:lI45|HB7B1E808 +B7B1E808:lI50|HB7B1E830 +B7B1E830:lI46|HB7B1E858 +B7B1E858:lI49|HB7B1E880 +B7B1E880:lI51|HB7B1E8A8 +B7B1E8A8:lI46|HB7B1E8C8 +B7B1E8C8:lI51|HB7B1E8E8 +B7B1E8E8:lI47|HB7B1E908 +B7B1E908:lI101|HB7B1E928 +B7B1E928:lI98|HB7B1E948 +B7B1E948:lI105|HB7B1E968 +B7B1E968:lI110|N +B7B1E198:lHB7B1E1D0|HB7B1E0A4 +B7B1E1D0:lI47|HB7B1E208 +B7B1E208:lI117|HB7B1E240 +B7B1E240:lI115|HB7B1E278 +B7B1E278:lI114|HB7B1E2B0 +B7B1E2B0:lI47|HB7B1E2E8 +B7B1E2E8:lI108|HB7B1E320 +B7B1E320:lI111|HB7B1E358 +B7B1E358:lI99|HB7B1E390 +B7B1E390:lI97|HB7B1E3C0 +B7B1E3C0:lI108|HB7B1E3F0 +B7B1E3F0:lI47|HB7B1E420 +B7B1E420:lI108|HB7B1E450 +B7B1E450:lI105|HB7B1E480 +B7B1E480:lI98|HB7B1E4B0 +B7B1E4B0:lI47|HB7B1E4E0 +B7B1E4E0:lI101|HB7B1E510 +B7B1E510:lI114|HB7B1E540 +B7B1E540:lI108|HB7B1E570 +B7B1E570:lI97|HB7B1E5A0 +B7B1E5A0:lI110|HB7B1E5D0 +B7B1E5D0:lI103|HB7B1E600 +B7B1E600:lI47|HB7B1E630 +B7B1E630:lI108|HB7B1E660 +B7B1E660:lI105|HB7B1E690 +B7B1E690:lI98|HB7B1E6C0 +B7B1E6C0:lI47|HB7B1E6F0 +B7B1E6F0:lI115|HB7B1E720 +B7B1E720:lI116|HB7B1E750 +B7B1E750:lI100|HB7B1E780 +B7B1E780:lI108|HB7B1E7B0 +B7B1E7B0:lI105|HB7B1E7E0 +B7B1E7E0:lI98|HB7B1E810 +B7B1E810:lI45|HB7B1E838 +B7B1E838:lI49|HB7B1E860 +B7B1E860:lI46|HB7B1E888 +B7B1E888:lI49|HB7B1E8B0 +B7B1E8B0:lI54|HB7B1E8D0 +B7B1E8D0:lI46|HB7B1E8F0 +B7B1E8F0:lI51|HB7B1E910 +B7B1E910:lI47|HB7B1E930 +B7B1E930:lI101|HB7B1E950 +B7B1E950:lI98|HB7B1E970 +B7B1E970:lI105|HB7B1E988 +B7B1E988:lI110|N +B7B1E0A4:lHB7B1E0F0|N +B7B1E0F0:lI47|HB7B1E110 +B7B1E110:lI117|HB7B1E138 +B7B1E138:lI115|HB7B1E168 +B7B1E168:lI114|HB7B1E1A0 +B7B1E1A0:lI47|HB7B1E1D8 +B7B1E1D8:lI108|HB7B1E210 +B7B1E210:lI111|HB7B1E248 +B7B1E248:lI99|HB7B1E280 +B7B1E280:lI97|HB7B1E2B8 +B7B1E2B8:lI108|HB7B1E2F0 +B7B1E2F0:lI47|HB7B1E328 +B7B1E328:lI108|HB7B1E360 +B7B1E360:lI105|HB7B1E398 +B7B1E398:lI98|HB7B1E3C8 +B7B1E3C8:lI47|HB7B1E3F8 +B7B1E3F8:lI101|HB7B1E428 +B7B1E428:lI114|HB7B1E458 +B7B1E458:lI108|HB7B1E488 +B7B1E488:lI97|HB7B1E4B8 +B7B1E4B8:lI110|HB7B1E4E8 +B7B1E4E8:lI103|HB7B1E518 +B7B1E518:lI47|HB7B1E548 +B7B1E548:lI108|HB7B1E578 +B7B1E578:lI105|HB7B1E5A8 +B7B1E5A8:lI98|HB7B1E5D8 +B7B1E5D8:lI47|HB7B1E608 +B7B1E608:lI115|HB7B1E638 +B7B1E638:lI97|HB7B1E668 +B7B1E668:lI115|HB7B1E698 +B7B1E698:lI108|HB7B1E6C8 +B7B1E6C8:lI45|HB7B1E6F8 +B7B1E6F8:lI50|HB7B1E728 +B7B1E728:lI46|HB7B1E758 +B7B1E758:lI49|HB7B1E788 +B7B1E788:lI46|HB7B1E7B8 +B7B1E7B8:lI55|HB7B1E7E8 +B7B1E7E8:lI47|HB7B1E818 +B7B1E818:lI101|HB7B1E840 +B7B1E840:lI98|HB7B1E868 +B7B1E868:lI105|HB7B1E890 +B7B1E890:lI110|N +B7B2466C:t9:A5:state,A5:efile,N,A4:none,p<0.1>,A8:infinity,A9:undefined,A5:false,HB7B1E090 +B7B1E090:t4:AA:prim_state,A5:false,A9:undefined,A9:undefined +=proc_dictionary:<0.4.0> +HB7B32D68 +HB7B32D74 +=proc_stack:<0.4.0> +0xb7b1df6c:SReturn addr 0xB7CE101C (proc_lib:init_p_do_apply/3 + 28) +y0:A5:false +y1:N +y2:HB7B1D8C4 +y3:AC:error_logger +y4:P<0.1.0> +0xb7b1df84:SReturn addr 0x8248774 () +y0:SCatch 0xB7CE102C (proc_lib:init_p_do_apply/3 + 44) +=proc_heap:<0.4.0> +B7B1D8C4:lHB7B1D8A0|N +B7B1D8A0:t5:A7:handler,AC:error_logger,A5:false,HB7B1D884,A5:false +B7B1D884:t3:I5,I0,HB7B1D87C +B7B1D87C:lHB7B1D85C|HB7B1D050 +B7B1D85C:t2:HB7B1D850,HB7B1D7A8 +B7B1D7A8:t3:AB:info_report,P<0.0.0>,HB7B1D7B8 +B7B1D7B8:t3:P<0.5.0>,A8:std_info,HB7B1D7C8 +B7B1D7C8:lHB7B1D7E0|HB7B1D7D0 +B7B1D7E0:t2:AB:application,A6:kernel +B7B1D7D0:lHB7B1D7EC|HB7B1D7D8 +B7B1D7EC:t2:A6:exited,HB7B1D804 +B7B1D804:t2:A8:shutdown,HB7B1D810 +B7B1D810:t3:A6:kernel,A5:start,HB7B1D828 +B7B1D828:lA6:normal|HB7B1D820 +B7B1D820:lN|N +B7B1D7D8:lHB7B1D7F8|N +B7B1D7F8:t2:A4:type,A9:permanent +B7B1D850:t2:HB7B1D830,HB7B1D840 +B7B1D840:t3:I20,I29,I7 +B7B1D830:t3:I2009,I11,I24 +B7B1D050:lHB7B1D058|HB7B1D064 +B7B1D058:t2:HB7B1D06C,HB7B1D078 +B7B1D078:t3:AC:error_report,P<0.7.0>,HB7B1D0BC +B7B1D0BC:t3:P<0.9.0>,A11:supervisor_report,HB7B1D0FC +B7B1D0FC:lHB7B1D15C|HB7B1D168 +B7B1D15C:t2:AA:supervisor,HB7B1D1C4 +B7B1D1C4:t2:A5:local,AA:kernel_sup +B7B1D168:lHB7B1D1D0|HB7B1D1DC +B7B1D1D0:t2:AC:errorContext,AB:start_error +B7B1D1DC:lHB7B1D230|HB7B1D23C +B7B1D230:t2:A6:reason,A8:shutdown +B7B1D23C:lHB7B1D284|N +B7B1D284:t2:A8:offender,HB7B1D2D0 +B7B1D2D0:lHB7B1D330|HB7B1D33C +B7B1D330:t2:A3:pid,A9:undefined +B7B1D33C:lHB7B1D38C|HB7B1D398 +B7B1D38C:t2:A4:name,A7:net_sup +B7B1D398:lHB7B1D3FC|HB7B1D408 +B7B1D3FC:t2:A3:mfa,HB7B1D470 +B7B1D470:t3:A10:erl_distribution,AA:start_link,N +B7B1D408:lHB7B1D480|HB7B1D48C +B7B1D480:t2:AC:restart_type,A9:permanent +B7B1D48C:lHB7B1D500|HB7B1D50C +B7B1D500:t2:A8:shutdown,A8:infinity +B7B1D50C:lHB7B1D594|N +B7B1D594:t2:AA:child_type,AA:supervisor +B7B1D06C:t2:HB7B1D09C,HB7B1D0AC +B7B1D0AC:t3:I20,I29,I7 +B7B1D09C:t3:I2009,I11,I24 +B7B1D064:lHB7B1D088|HB7B1D094 +B7B1D088:t2:HB7B1D0CC,HB7B1D0D8 +B7B1D0D8:t3:AC:error_report,P<0.7.0>,HB7B1D124 +B7B1D124:t3:P<0.17.0>,A11:supervisor_report,HB7B1D170 +B7B1D170:lHB7B1D1E4|HB7B1D1F0 +B7B1D1E4:t2:AA:supervisor,HB7B1D244 +B7B1D244:t2:A5:local,A7:net_sup +B7B1D1F0:lHB7B1D250|HB7B1D25C +B7B1D250:t2:AC:errorContext,AB:start_error +B7B1D25C:lHB7B1D290|HB7B1D29C +B7B1D290:t2:A6:reason,HB7B1D2D8 +B7B1D2D8:t2:A4:EXIT,AE:nodistribution +B7B1D29C:lHB7B1D2E4|N +B7B1D2E4:t2:A8:offender,HB7B1D344 +B7B1D344:lHB7B1D3A0|HB7B1D3AC +B7B1D3A0:t2:A3:pid,A9:undefined +B7B1D3AC:lHB7B1D410|HB7B1D41C +B7B1D410:t2:A4:name,AA:net_kernel +B7B1D41C:lHB7B1D494|HB7B1D4A0 +B7B1D494:t2:A3:mfa,HB7B1D514 +B7B1D514:t3:AA:net_kernel,AA:start_link,HB7B1D5A0 +B7B1D5A0:lHB7B1D618|N +B7B1D618:lAE:api@flukso.net|HB7B1D670 +B7B1D670:lA9:longnames|N +B7B1D4A0:lHB7B1D524|HB7B1D530 +B7B1D524:t2:AC:restart_type,A9:permanent +B7B1D530:lHB7B1D5A8|HB7B1D5B4 +B7B1D5A8:t2:A8:shutdown,I2000 +B7B1D5B4:lHB7B1D620|N +B7B1D620:t2:AA:child_type,A6:worker +B7B1D0CC:t2:HB7B1D104,HB7B1D114 +B7B1D114:t3:I20,I29,I7 +B7B1D104:t3:I2009,I11,I24 +B7B1D094:lHB7B1D0E8|HB7B1D0F4 +B7B1D0E8:t2:HB7B1D134,HB7B1D140 +B7B1D140:t3:AC:error_report,P<0.7.0>,HB7B1D198 +B7B1D198:t3:P<0.20.0>,AC:crash_report,HB7B1D1F8 +B7B1D1F8:lHB7B1D264|HB7B1D26C +B7B1D264:lHB7B1D2A4|HB7B1D2B0 +B7B1D2A4:t2:AC:initial_call,HB7B1D2F0 +B7B1D2F0:t3:AA:net_kernel,A4:init,HB7B1D34C +B7B1D34C:lAB:Argument__1|N +B7B1D2B0:lHB7B1D300|HB7B1D30C +B7B1D300:t2:A3:pid,P<0.20.0> +B7B1D30C:lHB7B1D354|HB7B1D360 +B7B1D354:t2:AF:registered_name,N +B7B1D360:lHB7B1D3B4|HB7B1D3C0 +B7B1D3B4:t2:AA:error_info,HB7B1D424 +B7B1D424:t3:A4:exit,HB7B1D4A8,HB7B1D4B4 +B7B1D4B4:lHB7B1D538|HB7B1D548 +B7B1D538:t3:AA:gen_server,A7:init_it,I6 +B7B1D548:lHB7B1D5BC|N +B7B1D5BC:t3:A8:proc_lib,AF:init_p_do_apply,I3 +B7B1D4A8:t2:A5:error,A6:badarg +B7B1D3C0:lHB7B1D434|HB7B1D440 +B7B1D434:t2:A9:ancestors,HB7B1D4BC +B7B1D4BC:lA7:net_sup|HB7B1D550 +B7B1D550:lAA:kernel_sup|HB7B1D5CC +B7B1D5CC:lP<0.8.0>|N +B7B1D440:lHB7B1D4C4|HB7B1D4D0 +B7B1D4C4:t2:A8:messages,N +B7B1D4D0:lHB7B1D558|HB7B1D564 +B7B1D558:t2:A5:links,HB7B1D5D4 +B7B1D5D4:lp<0.193>|HB7B1D62C +B7B1D62C:lP<0.17.0>|N +B7B1D564:lHB7B1D5DC|HB7B1D5E8 +B7B1D5DC:t2:AA:dictionary,HB7B1D634 +B7B1D634:lHB7B1D678|N +B7B1D678:t2:A9:longnames,A4:true +B7B1D5E8:lHB7B1D63C|HB7B1D648 +B7B1D63C:t2:A9:trap_exit,A4:true +B7B1D648:lHB7B1D684|HB7B1D690 +B7B1D684:t2:A6:status,A7:running +B7B1D690:lHB7B1D6B0|HB7B1D6BC +B7B1D6B0:t2:A9:heap_size,I377 +B7B1D6BC:lHB7B1D6CC|HB7B1D6D8 +B7B1D6CC:t2:AA:stack_size,I24 +B7B1D6D8:lHB7B1D6E8|N +B7B1D6E8:t2:AA:reductions,I436 +B7B1D26C:lN|N +B7B1D134:t2:HB7B1D178,HB7B1D188 +B7B1D188:t3:I20,I29,I7 +B7B1D178:t3:I2009,I11,I24 +B7B1D0F4:lHB7B1D150|N +B7B1D150:t2:HB7B1D1A8,HB7B1D1B4 +B7B1D1B4:t3:A8:info_msg,P<0.7.0>,HB7B1D220 +B7B1D220:t3:P<0.20.0>,HB7B1D274,HB7B1D27C +B7B1D27C:lHB7B1D2C0|HB7B1D2C8 +B7B1D2C0:lI105|HB7B1D31C +B7B1D31C:lI110|HB7B1D370 +B7B1D370:lI101|HB7B1D3D0 +B7B1D3D0:lI116|HB7B1D450 +B7B1D450:lI95|HB7B1D4E0 +B7B1D4E0:lI116|HB7B1D574 +B7B1D574:lI99|HB7B1D5F8 +B7B1D5F8:lI112|N +B7B1D2C8:lHB7B1D324|N +B7B1D324:t2:HB7B1D378,HB7B1D384 +B7B1D384:lHB7B1D3E4|HB7B1D3F4 +B7B1D3E4:t3:AD:inet_tcp_dist,A6:listen,I1 +B7B1D3F4:lHB7B1D458|HB7B1D468 +B7B1D458:t3:AA:net_kernel,AC:start_protos,I4 +B7B1D468:lHB7B1D4E8|HB7B1D4F8 +B7B1D4E8:t3:AA:net_kernel,AC:start_protos,I3 +B7B1D4F8:lHB7B1D57C|HB7B1D58C +B7B1D57C:t3:AA:net_kernel,A9:init_node,I2 +B7B1D58C:lHB7B1D600|HB7B1D610 +B7B1D600:t3:AA:net_kernel,A4:init,I1 +B7B1D610:lHB7B1D658|HB7B1D668 +B7B1D658:t3:AA:gen_server,A7:init_it,I6 +B7B1D668:lHB7B1D6A0|N +B7B1D6A0:t3:A8:proc_lib,AF:init_p_do_apply,I3 +B7B1D378:t2:A8:badmatch,HB7B1D3D8 +B7B1D3D8:t2:A5:error,AE:duplicate_name +B7B1D274:lI80|HB7B1D2B8 +B7B1D2B8:lI114|HB7B1D314 +B7B1D314:lI111|HB7B1D368 +B7B1D368:lI116|HB7B1D3C8 +B7B1D3C8:lI111|HB7B1D448 +B7B1D448:lI99|HB7B1D4D8 +B7B1D4D8:lI111|HB7B1D56C +B7B1D56C:lI108|HB7B1D5F0 +B7B1D5F0:lI58|HB7B1D650 +B7B1D650:lI32|HB7B1D698 +B7B1D698:lI126|HB7B1D6C4 +B7B1D6C4:lI112|HB7B1D6E0 +B7B1D6E0:lI58|HB7B1D6F4 +B7B1D6F4:lI32|HB7B1D6FC +B7B1D6FC:lI114|HB7B1D704 +B7B1D704:lI101|HB7B1D70C +B7B1D70C:lI103|HB7B1D714 +B7B1D714:lI105|HB7B1D71C +B7B1D71C:lI115|HB7B1D724 +B7B1D724:lI116|HB7B1D72C +B7B1D72C:lI101|HB7B1D734 +B7B1D734:lI114|HB7B1D73C +B7B1D73C:lI32|HB7B1D744 +B7B1D744:lI101|HB7B1D74C +B7B1D74C:lI114|HB7B1D754 +B7B1D754:lI114|HB7B1D75C +B7B1D75C:lI111|HB7B1D764 +B7B1D764:lI114|HB7B1D76C +B7B1D76C:lI58|HB7B1D774 +B7B1D774:lI32|HB7B1D77C +B7B1D77C:lI126|HB7B1D784 +B7B1D784:lI112|HB7B1D78C +B7B1D78C:lI126|HB7B1D794 +B7B1D794:lI110|N +B7B1D1A8:t2:HB7B1D200,HB7B1D210 +B7B1D210:t3:I20,I29,I7 +B7B1D200:t3:I2009,I11,I24 +B7B32D68:t2:AD:$initial_call,HB7CE3618 +B7CE3618:t3:A9:gen_event,A7:init_it,I6 +B7B32D74:t2:AA:$ancestors,HB7B32D90 +B7B32D90:lP<0.1.0>|N +=proc_stack:<0.12.0> +0xb7b23e5c:SReturn addr 0xB7D3E72C (global:init_the_locker/1 + 192) +y0:HB7B23BD0 +y1:A8:infinity +0xb7b23e68:SReturn addr 0x8248774 () +=proc_heap:<0.12.0> +B7B23BD0:t7:A5:multi,N,N,N,AD:nonode@nohost,A5:false,A5:false +=atoms +'-tokens/4-fun-0-' +'-tokens/4-fun-1-' +'-options/1-fun-0-' +'-default_option/1-fun-0-' +'-scan1/5-fun-10-' +'-scan1/5-fun-2-' +'-scan1/5-fun-6-' +'-scan1/5-fun-4-' +'-scan1/5-fun-5-' +'-scan1/5-fun-9-' +'-scan1/5-fun-1-' +'-scan1/5-fun-3-' +'-scan1/5-fun-7-' +'-scan1/5-fun-0-' +'-scan1/5-fun-8-' +'-scan_atom/6-fun-0-' +'-scan_variable/6-fun-0-' +'-scan_dot/6-fun-0-' +'-scan_newline/5-fun-0-' +'-scan_nl_spcs/6-fun-0-' +'-scan_nl_tabs/6-fun-0-' +'-scan_nl_white_space/6-fun-0-' +'-scan_spcs/6-fun-0-' +'-scan_tabs/6-fun-0-' +'-skip_white_space/6-fun-0-' +'-scan_white_space/6-fun-0-' +'-scan_char/5-fun-1-' +'-scan_char/5-fun-0-' +'-scan_string/6-fun-0-' +'-scan_qatom/6-fun-0-' +'-scan_number/6-fun-1-' +'-scan_number/6-fun-0-' +'-scan_based_int/6-fun-0-' +'-scan_fraction/6-fun-0-' +'-scan_exponent_sign/6-fun-0-' +'-scan_exponent/6-fun-0-' +'-skip_comment/6-fun-0-' +'-scan_comment/6-fun-0-' +after +spec +let +end +cond +case +of +if +catch +query +try +when +begin +nl_tabs +spcs +nl_spcs +scan_error +tok3 +tok2 +comment +scan_comment +skip_comment +float_end +scan_exponent +scan_exponent_sign +scan_fraction +scan_based_int +scan_number +escape_char +scan_esc_end +scan_hex +scan_escape +scan_string1 +scan_string_col +scan_string_no_col +scan_string0 +scan_qatom +unicode_tokens +unicode_nl_tokens +u2l +unicode_string_to_list +char_error +scan_string +integer +scan_char +scan_white_space +skip_white_space +scan_tabs +scan_spcs +newline_end +scan_nl_white_space +scan_nl_tabs +scan_nl_spcs +scan_newline +'.' +scan_dot +scan_name +var +scan_variable +scan_atom +'#' +'&' +'?' +'@' +'\\' +'^' +'`' +'~' +'|' +'||' +'<-' +'<<' +'<=' +'>>' +':' +':-' +'::' +'=' +'->' +white_space +'(' +')' +',' +';' +'[' +']' +'{' +'}' +scan1 +scan +string1 +set_attr +attr_info +expand_opt +reserved_word_fun +opts +return_white_spaces +get_bool +return_comments +string_thing +set_attribute +location +text +column +attributes_info +category +symbol +token_info +no_col +char +base +illegal +binrev +get_until +collect_line_list +collect_line_bin +collect_line1 +collect_chars_list +collect_chars1 +collect_chars +cafu +count_and_find_utf8 +nl +printable_unicode_list +deep_unicode_char_list +deep_char_list +unicode_char_list +char_list +write_unicode_char +write_char +string_char +write_string1 +write_unicode_string +write_string +name_char +name_chars +reserved_word +quote_atom +write_atom +write_binary_body +write_binary +write_ref +write_port +write_tail +fwrite_g +format_prompt +indentation +fread +fwrite +'Argument__2' +'Argument__3' +'Argument__4' +'Argument__1' +try_connect +connect1 +api +'ZZHGJIFABIVMMTXJCYRL' +file_reply +wait_file_reply +file_request +check_args +check_and_call +character +mode_list +make_binary +file_name_1 +file_name +path_open_first +eval_stream2 +parse_erl_exprs +eval_stream +consult_stream +change_time +change_group +change_owner +change_mode +path_open +path_script +path_eval +new_bindings +path_consult +ipread_s32bu_p32bu_2 +ipread_s32bu_p32bu_int +copy_opened_int +copy_int +get_chars +raw_files +ram +file_io_servers +pid2name +terminated +undefined_script +next_random +make_info +random_cookie +raw_write_file_info +create_cookie +check_cookie1 +check_cookie +check_attributes +make_error +raw_read_file_info +read_cookie +cookies +setcookie +init_cookie +auth_reply +ddd_server +hello +echo +distribution_not_started +node_cookie +cookie +pang +pong +ping +no_reg_reply_from_epmd +epmd_close +already_registered +parse_name +parse_line +scan_line +scan_names +names_loop +do_get_names +cstring +wait_for_close +select_best_version +best_version +wait_for_port_reply_name +wait_for_port_reply_cont2 +wait_for_port_reply_cont +wait_for_port_reply +wait_for_port_reply_v0 +get_port +get_port_v0 +wait_for_reg_reply_v0 +garbage_from_epmd +wait_for_reg_reply +epmd_dist_low +epmd_dist_high +do_register_node +do_register_node_v0 +epmd_port +get_epmd_port +r4 +client_info_req +alive +names1 +names +port_please1 +eaddrinuse +'-do_accept/6-fun-0-' +'-do_accept/6-fun-1-' +'-do_accept/6-fun-2-' +'-do_accept/6-fun-3-' +'-do_accept/6-fun-4-' +'-do_accept/6-fun-5-' +'-do_setup/6-fun-0-' +'-do_setup/6-fun-1-' +'-do_setup/6-fun-2-' +split_stat +is_node_name +mask +get_ifs +check_ip +get_tcp_address +splitnode +handshake_we_started +reset_timer +port_please +do_setup +get_remote_id +dist_nodelay +handshake_other_started +hs_data +no_node +do_accept +flush_controller +accept_loop +inet_dist_use_interface +inet_dist_listen_max +inet_dist_listen_min +do_listen +register_node +invalid_key +short +bad_node +ok_pending +nok_pending +already_pending +'-terminate/2-fun-1-' +'-terminate/2-fun-2-' +'-handle_info/2-fun-0-' +'-handle_info/2-fun-1-' +'-init_node/2-fun-0-' +'-create_hostpart/2-fun-0-' +'-print_info/0-fun-0-' +getnode +nformat +fmt_address +display_info +print_info +restart_ticker +all_atoms +reply_waiting1 +reply_waiting +get_nodes_info +get_node_info +net_setuptime +connecttime +set_node +duplicate_name +sync_cookie +start_protos +childspecs +proto_dist +create_hostpart +long +create_name +init_node +get_proto_mod +unsupported_address_type +select_mod +net_address +setup +spawn_func +bye +aux_ticker1 +aux_ticker +start_aux_ticker +ticker_loop +get_up_nodes +get_nodes +disconnect_pid +do_disconnect +mk_monitor_nodes_error +bad_option_value +options_not_a_list +unknown_options +check_options +option_value_mismatch +check_opt +mark_sys_dist_nodedown +up_nodedown +up_pending_nodedown +pending_nodedown +get_conn +ticker_exit +pending_own_exit +conn_own_exit +accept_exit +listen_exit +do_handle_exit +aux_tick +transition_period_end +unsupported_protocol +controller +accept_connection +registered_send +badcookie +bad_request +inserted +remarked +accept_pending +is_pending +change_initiated +shorter +longer +unchanged +ongoing_change_to +tick_change +up_pending +publish_on_nodes +is_auth +not_implemented +bad_cookie +tick +ticker +nodistribution +connection +never +dist_auto_connect +passive_connect_monitor +barred_connection +sys_dist +do_connect +hidden_connect_node +connect_node +update_publish_nodes +publish_on_node +hidden_connect +ticktime_res +get_net_ticktime +new_ticktime +set_net_ticktime +verbose +nodes_info +node_info +kernel_apply +'api@flukso.net' +net_sup_dynamic +net_ticktime +ticktime +lname +start_p +protocol_childspecs +epmd_module +no_epmd +'-to_lower/1-lc$^0/1-0-' +'-to_upper/1-lc$^0/1-0-' +'-join/2-lc$^0/1-0-' +to_upper +to_lower +to_upper_char +to_lower_char +sub_string +centre +r_pad +l_pad +strip_right +strip_left +s_word +sub_word +left +w_count +both +words +copies +tokens2 +tokens1 +substr2 +substr1 +cspan +span +rstr +str +rchr +chr +len +read_only +the +generated +transformed +be +should +real +with +called +transform_error +blog_terms +'-tab2file/3-fun-0-' +log_terms +'-tab2file/3-fun-1-' +'-file2tab/2-fun-0-' +'-file2tab/2-fun-1-' +'-table/2-fun-1-' +'-table/2-fun-0-' +'-table/2-fun-2-' +'-table/2-fun-3-' +'-table/2-fun-4-' +'-table/2-fun-5-' +'-table/2-fun-6-' +'-table/2-fun-7-' +'-table/2-fun-8-' +'-table/2-lc$^1/1-1-' +'-table/2-lc$^0/1-0-' +'-table/2-fun-9-' +'-qlc_next/2-fun-0-' +'-qlc_prev/2-fun-0-' +'-qlc_select/1-fun-0-' +'-i/0-fun-0-' +'-hform/6-lc$^0/1-0-' +re_match +print_re_num +re_display +re_search +substr +do_display_item +do_display_items +do_display +print_number +strip +right +nonl +put_chars +choice +'(c)ontinue (q)uit (p)Digits (k)ill /Regexp -->' +'EOT (q)uit (p)Digits (k)ill /Regexp -->' +pad_right +hform +is_reg +prinfo2 +prinfo +tabs +mem +default_option +n_objects +traverse +qlc_select +qlc_prev +qlc_next +is_unique_objects +is_sorted_key +num_of_objects +table_info +pre_fun +post_fun +info_fun +format_fun +key_equality +lookup_fun +first_next +last_prev +tabfile_info +cannot_create_table +create_tab +load_table +scan_for_endinfo +md5_and_convert +major_version +minor +major +get_header_data +chunk +wrap_chunk +bchunk +wrap_bchunk +verify_header_mandatory +count_mandatory +verify +parse_f2t_opts +invalid_object_count +checksum_error +do_read_and_verify +read_error +unsupported_file_version +repaired +file2tab +object_count +md5sum +parse_ft_info_options +malformed_option +unknown_option +parse_ft_options +md5terms +ft_options_to_list +dump_file +eaccess +filetab_options +extended_info +badtab +tab2file +do_filter +init_table_sub +end_of_input +init_table_continue +init_table +test_ms +from_ets +to_dets +to_ets +from_dets +do_foldr +transform_from_shell +fun_data +fun2ms +repair_continuation +match_spec_run +could_not_start_server +inet_gethost_native_sup +'-handle_message/2-fun-0-' +'-restart_port/1-fun-0-' +pick_names +ndx +pick_addresses_v6 +pick_addresses_v4 +listify +malformed_response +parse_address +ensure_started +getit +soft_restart +debug_level +gethost_poolsize +get_poolsize +gethost_extra_args +gethost_prioritize +get_extra_args +do_open_port +get_rid +pick_client +pick_request +find_request +do_handle_call +restart_port +handle_message +ign_req_index +ign_requests +num_requests +rid +run_once +start_raw +server_init +source_file_not_found +major_os_type +make_abs_path +readable_file +try_rule +source_by_rules +source_file +get_source_file +parse_transform +d +export_all +fast +outdir +filter_options +which +try_file +source_search_rules +find_src +separators +win32_nativename +nativename +win32_split +unix_split +vxworks_split +rootname2 +rootname +maybe_remove_dirsep +join1 +extension +skip_prefix1 +skip_prefix +basename1 +absname_pretty +absname_join +'-services/2-fun-0-' +'-rpc/2-fun-0-' +'-hosts/2-fun-0-' +'-hosts_vxworks/1-fun-0-' +'-resolv/2-fun-0-' +'-host_conf_linux/2-fun-0-' +'-host_conf_freebsd/2-fun-0-' +'-host_conf_bsdos/2-fun-0-' +'-nsswitch_conf/2-fun-0-' +'-protocols/2-fun-0-' +'-netmasks/2-fun-0-' +'-networks/2-fun-0-' +'-ntoa_done/2-fun-0-' +'-ntoa_done/2-fun-1-' +'-ntoa_done/1-fun-0-' +split_mid_comma +split_comma +split_end +split_mid +dig_to_hex +dig_to_dec +ntoa_done +dup +tox +x4 +ipv6_addr_done +ipv6_addr +tod +d3 +is_dom2 +is_dom_ldh +is_dom1 +is_vis1 +port_proto +collect_line +get_line +parse_cs +skip +parse_fd +networks +netmasks +protocols +delete_options +noname +services +not_owner +eafnosupport +'-getiflist/0-fun-0-' +'-ifget/2-fun-0-' +'-ifset/2-fun-0-' +'-getif/0-fun-0-' +'-getif/1-fun-0-' +'-gethostname/0-fun-0-' +'-open/7-lc$^0/1-0-' +'-ii/3-fun-0-' +'-ii/3-lc$^0/1-0-' +'-ii/3-fun-1-' +'-info_lines/3-lc$^0/1-0-' +'-i_line/3-lc$^0/1-0-' +'-h_line/1-lc$^0/1-0-' +'-port_list/1-fun-0-' +udp_sync_input +tcp_sync_input +tcp_controlling_process +tcp_close +exbadseq +exbadport +port_list +udp_sockets +tcp_sockets +fmt_port +ntoa +enotconn +fmt_addr +listening +fmt_status +sent +foreign_address +local_address +upper +hh_field +h_field +h_line +i_line +info_lines +smax +ii +i +gethostbyaddr_tm_native +gethostbyaddr_self +gethostbyname_self +gethostbyname_tm_native +ipv6_address +no_default +wins +getaddrs_tm +add_opt +sctp_opt_ifaddr +sctp_opt +translate_ip +sctp_opts +sctp_options +udp_add +udp_opt +list_add +backlog +list_opt +listen_opts +inet_default_listen_options +listen_options +con_add +ifaddr +con_opt +connect_opts +inet_default_connect_options +connect_options +stats +options +getaddrs +getll +gethostbyaddr_tm +gethostbyname_tm +popf +pushf +withsocket +getif +udp_closed +optuniquify +udp_controlling_process +controlling_process +udp_close +udp_opts +getaddr_tm +getaddr +getserv +'-unix_cmd/1-fun-0-' +'-start_port/0-fun-0-' +validate1 +validate +mk_cmd +eot +unix_get_data +start_port_srv_loop +start_port_srv +os_cmd_port_creator +start_port +unix_cmd +extensions +reverse_element +split_path +verify_executable +find_executable1 +find_executable +not_string +'-init/0-fun-0-' +'-set_hostname/1-fun-0-' +'-load_hosts/2-fun-0-' +'-win32_load1/3-fun-0-' +'-win32_load1/3-fun-1-' +'-vxworks_load_hosts/0-fun-0-' +scan_inetrc +parse_inetrc_skip_line +parse_inetrc +inet_warnings +try_get_rc +inetrc +read_inetrc +valid_type +extract_cfg_files1 +registry +extract_cfg_files +read_rc +next_line +check_hostShow +hosts_vxworks +cmd +vxworks_load_hosts +win32_get_strings +split_line +win32_split_line +expand +win32_load1 +change_key +windows +nt +win32_load_from_registry +load_hosts +load_resolv +set_search_dom +inet_dns_when_nis +add_dns_lookup +no_ERLRESCONF +no_hosts_file +'Warning: No HOSTSFILE specified!' +address +'Warning: No NAMESERVER or RESOLVFILE specified!' +nsswitch_conf +host_conf_linux +host_conf_freebsd +host_conf_bsdos +sunos +netbsd +freebsd +'bsd/os' +do_load_resolv +nonames +longnames +shortnames +sname +erl_dist_mode +gethostbyname +ose_inet_port +loader +formerr +nxdomain +'-add_hosts/1-fun-0-' +'-res_check_option/2-fun-4-' +'-res_check_option/2-fun-2-' +'-res_check_option/2-fun-3-' +'-res_check_option/2-fun-0-' +'-res_check_option/2-fun-1-' +'-res_update_conf/0-fun-0-' +'-res_update_hosts/0-fun-0-' +'-res_cache_answer/1-fun-0-' +'-lookup_type/2-lc$^0/1-0-' +'-lookup_cname/1-lc$^0/1-0-' +'-res_lookup_type/3-lc$^0/1-0-' +'-handle_call/3-lc$^1/1-2-' +'-handle_call/3-lc$^0/1-1-' +'-handle_call/3-lc$^2/1-0-' +chars +'-do_add_host/5-lc$^0/1-0-' +'-do_del_host/3-lc$^0/1-0-' +'-rc_opt_req/1-lc$^0/1-0-' +'-do_refresh_cache/4-fun-0-' +'-delete_older/5-fun-0-' +'-lists_subtract/2-fun-0-' +lists_keydelete +lists_subtract +lists_delete +delete_older +delete_n_oldest +alloc_entry +do_refresh_cache +stop_timer +init_timer +stripdot_1 +stripdot +hex +dnib +dn_in_addr_arpa +dn_ip6_int +tolower +lower_rr +filter_rr +match_rr +do_lookup_rr +times +cache_rr +do_add_rr +is_reqname +is_res_set +set_socks_methods +rc_reqname +clear_search +clear_ns +rc_opt_req +handle_calls +badopt +handle_rc_list +do_del_host +do_add_host +handle_set_file +refresh_timeout +listdel +load_hosts_file +reset_db +inet_hosts_file_byaddr +inet_hosts_byaddr +inet_cache +lookup_socket +unregister_socket +register_socket +dnt +dnip +ent_gethostbyaddr +res_gethostbyaddr +ptr +gethostbyaddr +res_lookup_type +res_hostent_by_domain +lookup_rr +cname +lookup_cname +lookup_type +hostent_by_domain +hostent +a +aaaa +make_hostent +get_searchlist +getbysearch +dots +getbyname +dns_rec +res_cache_answer +del_rr +dns_rr +add_rr +db_get +res_update +res_hosts_file_tm +set_hosts_file_tm +res_hosts_file_info +res_update_hosts +res_resolv_conf_tm +set_resolv_conf_tm +res_resolv_conf_info +res_update_conf +hostname +methods +noproxy +server +socks_option +res_check_search +res_check_ns +res_check_list +res_check_option_absfile +visible_string +res_check_option +res_recurse +alt_nameserver +nameserver +res_optname +res_set +res_id +next_id +res_option +host +get_rc_hosts +get_rc_ns +get_rc_noproxy +res_domain +res_search +res_edns +res_inet6 +res_usevc +inet_hosts_byname +res_alt_ns +res_ns +res_hosts_file +res_resolv_conf +res_retry +res_udp_payload_size +res_timeout +cache_refresh_interval +res_lookup +socks5_server +socks5_noproxy +cache_size +socks5_port +cache_refresh +socks5_methods +get_rc +valid_lookup +dns +yp +native +nis +nisplus +translate_lookup +add_rc_list +add_rc_bin +consult +add_rc +sctp_module +set_sctp_module +udp_module +set_udp_module +tcp_module +set_tcp_module +set_cache_refresh +set_cache_size +del_socks_noproxy +add_socks_noproxy +del_socks_methods +add_socks_methods +set_socks_port +set_socks_server +inet_hosts_file_byname +get_hosts_file +hosts_file +set_hosts_file +resolv_conf +set_resolv_conf +udp_payload_size +set_udp_payload_size +edns +set_edns +usevc +set_usevc +set_inet6 +retry +set_retry +recurse +set_recurse +set_lookup +domain +set_domain +set_hostname +del_search +ins_search +add_search +del_alt_ns +ins_alt_ns +alt_nameservers +add_alt_ns +del_ns +ins +ins_ns +nameservers +listop +add_ns +clear_hosts +del_host +add_host +hosts +add_hosts +resolv +add_resolv +'$5' +'$4' +'$3' +allow +'-register_name/2-fun-0-' +'-register_name/3-fun-0-' +'-check_dupname/2-lc$^0/1-0-' +'-unregister_name/1-fun-0-' +'-re_register_name/2-fun-0-' +'-re_register_name/3-fun-0-' +'-register_name_external/2-fun-0-' +'-register_name_external/3-fun-0-' +'-check_replies/3-lc$^0/1-0-' +'-resolved/5-fun-0-' +'-resolved/5-fun-1-' +'-resolved/5-fun-2-' +'-resolved/5-fun-3-' +'-resolved/5-fun-4-' +'-start_resolver/2-fun-0-' +'-do_ops/5-lc$^1/1-1-' +'-do_ops/5-lc$^0/1-0-' +'-do_ops/5-fun-0-' +'-do_ops/5-lc$^2/1-2-' +'-do_ops/5-lc$^3/1-3-' +'-do_ops/5-fun-1-' +'-do_ops/5-lc$^4/1-4-' +'-do_ops/5-fun-2-' +'-sync_others/1-fun-0-' +'-sync_others/1-fun-1-' +'-lock_still_set_old/3-lc$^0/1-0-' +'-del_name/2-lc$^1/1-1-' +'-del_name/2-lc$^0/1-0-' +'-start_the_locker/1-fun-0-' +'-exclude_known/2-lc$^0/1-0-' +'-delete_lock/2-fun-0-' +'-pid_locks/1-fun-0-' +'-pid_locks/1-lc$^0/1-0-' +'-send_again/1-fun-0-' +'-start_sync/2-fun-0-' +'-sync_init/2-fun-0-' +'-sync_loop/2-fun-0-' +'-start_the_deleter/1-fun-0-' +'-loop_the_deleter/1-fun-0-' +'-loop_the_deleter/1-fun-1-' +'-start_the_registrar/0-fun-0-' +'-do_monitor/1-fun-0-' +intersection +do_monitor +unexpected_message +loop_the_registrar +start_the_registrar +deleter +collect_deletions +loop_the_deleter +start_the_deleter +get_own_nodes_with_errors +get_own_nodes +check_sync_nodes +synced +sync_loop +sync_init +start_sync +new_node_name +change_our_node_name +send_again +uniform +seed +random_seed +random_sleep +tab2list +get_names +handle_nodedown +del_locks +rpid_is_locking +pid_locks +delete_lock +pid_is_locking +unlink_pid +dounlink_ext +dolink_ext +notify_all_name +global_name_conflict +random_notify_name +random_exit_name +minmax +resolve_it +exchange_names +reset_node_state +kill_resolver +cancel_locker +split_node +is_node_local +remove_node2 +remove_node +find_node_tag2 +find_node_tag +the_boss +delete_global_lock +call_fun +exclude_known +random_element +update_locker_known +lock_rejected +locker_failed +locker_succeeded +rejected +locker_trace +delete_nonode +not_ok +lock_nodes_safely +locker_lock_id +select_node +him +no_fun +lock_set +remove_from_known +do_trace +the_locker_message +loop_the_locker +locker_exited +multi +init_the_locker +start_the_locker +delete_global_name +delete_global_name2 +delete_global_name_keep_pid +delete_name +del_names +del_name +extra_info +lock_still_set_old +lock_still_set +insert_global_name +sync_other +global_connect_retries +sync_others +do_ops +kill_monitor_proc +rem_lock +remove_lock +handle_del_lock +is_lock_set +is_global_lock_set +ins_lock +insert_lock +can_set_lock +handle_set_lock +ins_name_ext +ins_name +resend_pre_connect +resolver +start_resolver +do_whereis +cancel_resolved_locker +ops +added +add_to_known +lock +resolve +wait_lock +lock_id +pre_connect +his_the_locker +prot_vsn +check_replies +local_lock_check +set_lock_on_nodes +lock_on_known_nodes +set_lock_known +send_high_level_trace +wait_high_level_trace +nodes_changed +to_external +symmetric_partition +high_level_trace +registrar_died +locker_died +deleter_died +new_resolver +no_longer_a_pid +locker +trace_message +extra_nodedown +new_nodes +sync_tag_his +exit_resolver +exchange +exchange_ops +save_ops +resolved +lock_is_set +init_connect +sync_tag_my +in_sync +async_del_name +async_del_lock +get_names_ext +high_level_trace_start +high_level_trace_stop +get_protocol_version +high_level_trace_get +get_synced +get_known +register_ext +trans_all_known +connect_all +no_trace +global_pid_ids +global_names_ext +global_locks +trans +del_lock +set_lock +unregister_name_external +register_name_external +global_names +registered_names +re_register_name +global_multi_name_action +global_pid_names +check_dupname +registrar +node_disconnected +whereis_name +map_1 +iterator_1 +iterator +keys +to_list_1 +largest_1 +largest +take_largest1 +take_largest +smallest_1 +smallest +take_smallest1 +take_smallest +delete_1 +delete_any +from_orddict +balance_list_1 +balance_list +balance +count +enter +key_exists +insert_1 +update_1 +get_1 +is_defined_1 +is_defined +nil +lookup_1 +is_empty +'-handle_cast/2-fun-0-' +'-proxy_user/0-fun-0-' +'-do_call/3-fun-0-' +'-do_multicall/5-fun-0-' +'-async_call/4-fun-0-' +'-parallel_eval/1-lc$^0/1-0-' +pinfo +build_args +pmap +map_nodes +parallel_eval +promise_reply +do_yield +nb_yield +async_call +safe_multi_server_call +multi_server_call +do_multicall +multicall +eval_everywhere +rpc_check +rpc_check_t +local_call +proxy_user_flush +proxy_user_loop +rex_proxy_user +proxy_user +nonexisting_name +sbcast +block_call +empty +'-to_list/1-fun-0-' +'-from_list/1-fun-0-' +'-fetch_keys/1-fun-0-' +'-erase/2-fun-0-' +'-store/3-fun-0-' +'-append/3-fun-0-' +'-append_list/3-fun-0-' +'-update/3-fun-0-' +'-update/4-fun-0-' +'-update_counter/3-fun-0-' +'-merge/3-fun-0-' +'-merge/3-fun-1-' +'-merge/3-fun-2-' +'-merge/3-fun-3-' +contract_segs +expand_segs +mk_seg +rehash +maybe_contract_segs +maybe_contract +maybe_expand_segs +maybe_expand_aux +maybe_expand +put_bucket_s +get_bucket_s +filter_bucket +filter_bkt_list +filter_seg_list +filter_dict +map_bucket +map_bkt_list +map_seg_list +map_dict +fold_bucket +fold_seg +fold_segs +fold_dict +on_bucket +get_bucket +get_slot +fold +counter_bkt +update_bkt +app_list_bkt +append_list +append_bkt +store_bkt_val +erase_key +fetch_keys +find_val +fetch_val +fetch +find_key +already_present +'-update_chsp/2-fun-0-' +invalid_module +'-validMods/1-fun-0-' +report_progress +restart_type +child_type +extract_child +supervisor_report +errorContext +reason +offender +difference +inPeriod +add_restart +invalid_modules +dynamic +validMods +invalid_shutdown +validShutdown +invalid_mfa +validFunc +validName +invalid_child_type +validChildType +invalid_child_spec +check_childspec +duplicate_child_name +check_startspec +supname +invalid_period +validPeriod +invalid_intensity +validIntensity +invalid_strategy +validStrategy +invalid_type +init_state1 +init_state +remove_child +do_replace_child +replace_child +split_child +del_child +state_del_child +monitor_child +brutal_kill +terminate_children +rest_for_one +reached_max_restart_intensity +child_terminated +do_restart +find +handle_start_child +update_chsp +update_childspec1 +update_childspec +bad_flags +check_flags +null +store +do_start_child_i +do_start_child +child +start_children +bad_start_spec +init_dynamic +start_spec +init_children +supervisor_data +simple_one_for_one +check_childspecs +which_children +delete_child +restart_child +start_child +distribution_not_changed +one_for_all +kernel_safe_sup +one_for_one +global_groups +is_gg_changed +global_groups_removed +global_groups_added +global_groups_changed +do_global_groups_change +is_dist_changed +distribution_changed +do_distribution_change +start_pg2 +start_disk_log +boot_server_slaves +get_boot_args +start_boot_server +start_dist_ac +nostick +get_code_args +worker +safe +bad_config +get_error_logger_type +shutdown_error +'-kill_children/1-fun-0-' +exit_after +set_timer +kill_all_procs_1 +kill_all_procs +kill_children +terminate_child +terminate_child_i +get_child_i +prep_stop +loop_it +start_supervisor +start_the_app +start_it_new +bad_return +start_it_old +bad_keys +terminate_loop +main_loop +io_request +init_loop +get_child +'-print_log/1-fun-0-' +close_log_file +remove_debug +install_debug +trim +stat +no_statistics +start_time +current_time +messages_in +messages_out +get_stat +init_stat +standard_io +do_change_code +unknown_debug +debug_cmd +unknown_system_msg +do_cmd +suspend_loop_hib +suspend_loop +mfa +send_system_msg +install +no_debug +log_to_file +change_code +resume +permit_only_loaded_application +permit +takeover_application +'-do_multi_call/4-fun-0-' +get_debug +could_not_find_registerd_name +name_to_pid +process_was_not_started_by_proc_lib +get_parent +process_not_registered_globally +process_not_registered +get_proc_name +dbg_opts +generic_debug +dbg_options +print_log +handle_common_reply +dispatch +unmonitor +start_monitor +rec_nodes_rest +rec_nodes +send_nodes +do_multi_call +do_send +decode_msg +unregister_name +bad_return_value +multi_call +do_abcast +abcast +'$gen_cast' +cast_msg +do_cast +'$gen_call' +'-concat/1-fun-0-' +'-filter/2-lc$^0/1-0-' +rufmerge2_2 +rufmerge2_1 +ufmerge2_2 +ufmerge2_1 +rufmergel +ufmergel +ufsplit_2 +ufsplit_1_1 +ufsplit_1 +rfmerge2_2 +rfmerge2_1 +fmerge2_2 +fmerge2_1 +rfmergel +fmergel +fsplit_2_1 +fsplit_2 +fsplit_1_1 +fsplit_1 +rukeymerge2_2 +rukeymerge2_1 +ukeymerge2_2 +ukeymerge2_1 +rukeymerge3_21_3 +rukeymerge3_12_3 +rukeymerge3_2 +rukeymerge3_1 +ukeymerge3_21_3 +ukeymerge3_12_3 +ukeymerge3_2 +ukeymerge3_1 +rukeymergel +ukeymergel +ukeysplit_2 +ukeysplit_1_1 +ukeysplit_1 +rkeymerge2_2 +rkeymerge2_1 +keymerge2_2 +keymerge2_1 +rkeymerge3_21_3 +rkeymerge3_12_3 +rkeymerge3_2 +rkeymerge3_1 +keymerge3_21_3 +keymerge3_12_3 +keymerge3_2 +keymerge3_1 +rkeymergel +keymergel +keysplit_2_1 +keysplit_2 +keysplit_1_1 +keysplit_1 +rumerge2_2 +rumerge2_1 +umerge2_2 +umerge2_1 +rumerge3_21_3 +rumerge3_12_3 +rumerge3_2 +rumerge3_1 +umerge3_21_3 +umerge3_12_3 +umerge3_2 +umerge3_1 +rumergel +umergel +usplit_2_1 +desc +usplit_2 +usplit_1_1 +asc +usplit_1 +rmerge2_2 +rmerge2_1 +merge2_2 +merge2_1 +rmerge3_21_3 +rmerge3_12_3 +rmerge3_2 +rmerge3_1 +merge3_21_3 +merge3_12_3 +merge3_2 +merge3_1 +rmergel +mergel +split_2_1 +split_2 +split_1_1 +split_1 +dropwhile +takewhile +mapfoldr +mapfoldl +partition +foldr +flatmap +rumerge3 +umerge3 +rumerge +umerge +usort_1 +usort +keymap +rukeymerge +ukeymerge +ukeysort_1 +ukeysort +rkeymerge +keymerge +keysort_1 +keystore2 +keystore +keytake +keyreplace3 +keydelete3 +flatlength +flat_length +do_flatten +thing_to_list +rmerge +rmerge3 +merge3 +sort_1 +zipwith3 +zipwith +unzip3 +zip3 +sublist_2 +sublist +duplicate +seq_loop +seq +suffix +prefix +nthtail +nth +open_file +only_loaded +'configuration must be a list ended by ' +'-start/1-fun-0-' +'-loaded_applications/0-fun-0-' +'-get_all_env/1-fun-0-' +'-handle_call/3-fun-2-' +'-handle_call/3-fun-0-' +'-handle_call/3-fun-1-' +'-terminate/2-fun-0-' +'-load/2-fun-0-' +'-unload/2-fun-0-' +'-check_start_cond/4-fun-0-' +'-start_appl/3-fun-0-' +'-prim_parse/2-fun-0-' +'-do_change_apps/3-fun-0-' +'-do_change_apps/3-fun-1-' +'-get_cmd_env/1-fun-0-' +'-add_env/2-fun-0-' +'-do_config_diff/3-fun-0-' +'-check_conf/0-fun-0-' +'-reply_to_requester/3-fun-0-' +test_make_apps +test_do_change_appl +test_change_apps +update_permissions +reply_to_requester +exited +info_exited +started_at +info_started +config_error +strip_comment +only_ws +parse_file +done +tokens +scan_file +load_file +dirname +basename +check_conf_sys +config +check_conf +do_config_diff +application_not_found +module_not_defined +sort +do_config_change +do_prep_config_change +check_user +del_env +add_env +get_env_key +merge_app_env +merge_env +get_env_i +make_term +conv +get_cmd_env +get_opt +do_change_appl +is_loaded_app +do_change_apps +invalid_options +badstartspec +invalid_name +make_appl_i +parse_term +splitwith +prim_parse +prim_consult +format_error +non_existing +where_is_file +make_appl +bad_application +get_appl_name +get_restart_type +nd +invalid_restart_type +validRestartType +keyreplaceadd +ksd +keysearchdelete +stopped +stop_appl +start_appl +cast +init_starter +spawn_starter +do_start +check_start_cond +do_load_application +get_loaded +del_cntrl +ac_application_run +notify_cntrl_started +cntrl +shutdown_func +application_terminated +match_delete +keyreplace +ac_application_not_run +not_running +stop_it +failover +takeover +ac_load_application_reply +ac_start_application_reply +ac_change_application_req +application_start_failure +transient +temporary +handle_application_started +application_started +handle_cast +loading +start_p_false +permissions +ac_start_application_req +distributed_application +ac_application_unloaded +noreply +ac_load_application_req +not_started +ac_application_stopped +keydelete +check_para +check_distributed +distributed +check_para_kernel +check_conf_data +'load error' +enter_loop +unset_env +set_env +permit_application +in_modules +get_application_module +get_application +get_master +start_type +get_all_key +get_pid_all_key +appl_data +appl +get_key +get_pid_key +'$2' +'$1' +get_all_env +get_pid_all_env +get_env +get_pid_env +config_change +prep_config_change +change_application_data +control_application +ac_tab +loaded_applications +which_applications +stop_application +bad_environment_value +start_boot_application +start_application +unload_application +load_application +'-format_exception/3-fun-0-' +'-pp_fun/0-fun-0-' +format_tag +pp_fun +format_mfa +format_exception +format_rep +format_report +badrpc +proc_info +get_my_name +translate_process_info +get_process_info +no_trap +adjacents +visit +max_neighbours +neighbours +get_initial_call +make_neighbour_report +neighbour +make_neighbour_reports1 +linked_info +get_dictionary +clean_dict +get_cleaned_dictionary +ancestors +get_ancestors +my_info_1 +my_info +crash_report +trans_init +raw_init_call +raw_initial_call +translate_initial_call +make_dummy_args +ack +sync_wait +exit_p +init_p_do_apply +'$initial_call' +'$ancestors' +ensure_link +wake_up +check_for_monitor +init_p +opt +spawn_opts +register_name +name_register +safe_whereis_name +where +reply +wait_resp +wait_resp_mon +do_call +init_it2 +do_spawn +already_started +bad_module +module_not_found +'-do_unlink/2-fun-0-' +parent_terminated +'-terminate_supervised/4-fun-0-' +code_change +'-system_code_change/4-fun-0-' +'-the_handlers/1-lc$^0/1-0-' +'-get_modules/1-lc$^0/1-0-' +items +format_status +stop_handlers +the_handlers +'function not exported' +'module could not be loaded' +report_error +gen_event_EXIT +report_terminate +do_terminate +server_call_update +replace +server_call +new_handler +do_swap +remove_handler +server_update +server_notify +swapped +split_and_terminate +s_s_h +server_swap_handler +server_delete_handler +server_add_sup_handler +handler +server_add_handler +print_event +zf +system_code_change +system_terminate +system_continue +filter +terminate_supervised +do_unlink +terminate_server +get_modules +handle_debug +handle_system_msg +fetch_msg +wake_hib +call1 +swap_sup_handler +sync_notify +add_sup_handler +debug_options +init_it +nolink +'no callback module' +callbacks +behaviour_info +bad_query +no_log_file +allready_have_logfile +display2 +add_node +tag_event +handle_event2 +lost_messages +swap +handle_call +handle_info +handle_event +go_back +which_handlers +simple_logger +delete_report_handler +add_handler +add_report_handler +delete_handler +logfile +swap_handler +error_info +info_report +std_info +std_warning +warning_report +format +error_msg +stop_error +report_problem +send_shutdown +get_heart_cmd +send_heart_cmd +send_heart_beat +do_cycle_port_program +no_reboot_shutdown +port_terminated +bad_cmd +wait_ack +bad_heart_flag +check_start_heart +get_heart_timeouts +port_problem +start_portprogram +wait +cycle +clear_cmd +get_cmd +set_cmd +start_error +no_heart +wait_for_init_ack +abstract +extends +check_inheritance +stub_function +'no -init_debug flag' +'no -mode flag' +erlangrc +start_boot +applications_loaded +errlog_type +sasl_error_logger +sasl_sup +pool_master +take_over_monitor +rsh_starter +timer_server +stdlib +init_kernel_started +mod +maxP +maxT +start_phases +tty +included_applications +applications +ddll_server +os_server +rex +net_sup +kernel_sup +global_name_server +fixtable_server +file_server_2 +boot_server +modules +vsn +description +start_link +modules_loaded +systools_relup +systools_rc +systools_make +systools_lib +systools +sasl_report_tty_h +sasl_report_file_h +sasl_report +sasl +release_handler_1 +release_handler +rb_format_supp +rb +overload +misc_supp +format_lib_supp +erlsrv +alarm_handler +win32reg +sys +supervisor_bridge +supervisor +sofs +slave +shell_default +shell +sets +regexp +random +queue +qlc_pt +qlc +proplists +proc_lib +pool +pg +otp_internal +ordsets +orddict +ms_transform +log_mf_h +lib +io_lib_pretty +io_lib_fread +io_lib_format +io_lib +gen_fsm +gen_event +gen +gb_trees +gb_sets +filelib +file_sorter +eval_bits +escript +error_logger_tty_h +error_logger_file_h +erl_tar +erl_pp +erl_posix_msg +erl_lint +erl_internal +erl_expand_records +erl_compile +erl_bits +epp +edlin_expand +edlin +digraph_utils +digraph +dict +dets_v9 +dets_v8 +dets_utils +dets_sup +dets_server +dets +c +beam_lib +base64 +wrap_log_reader +user_sup +user_drv +standard_error +rpc +ram_file +pg2 +packages +net_adm +net +kernel_config +kernel +inet_udp +inet_tcp_dist +inet_tcp +inet_sctp +inet_res +inet_parse +inet_hosts +inet_gethost_native +inet_dns +inet_db +inet_config +inet6_udp +inet6_tcp_dist +inet6_tcp +inet6_sctp +hipe_unified_loader +group +global_search +global_group +gen_udp +gen_tcp +gen_sctp +file_server +file_io_server +erl_reply +erl_epmd +erl_distribution +erl_boot_server +dist_util +dist_ac +disk_log_sup +disk_log_server +disk_log_1 +disk_log +code_server +application_starter +application_master +application_controller +application +linux +pa +smp +home +progname +root +'no -path flag' +'no -id flag' +'no -hosts flag' +'no -loader flag' +'-name' +'-smp' +'-home' +'-progname' +erlrrd +webmachine +reloader +'-spawn_opt/5-fun-0-' +rvrs +processor_node +cput_i2e_tag +cput_i2e_tag_map +cput_i2e +cput_e2i +logical +core +thread +processor +cput_e2i_clvl +internal_cpu_topology +list_to_integer_sign +concat_binary +get_cookie +auth +set_cookie +ignored +passive_cnct +send_nosuspend +fun_info_1 +disconnect +disconnect_node +is_well_formed_list +crasher +remote_spawn_error +fault +no_link +gen_server +spawn_monitor +einal +clean +file_not_found +'no server found' +ebusy +no_multi_get +ose_inet +'-start/3-fun-0-' +'-handle_get_files/4-fun-0-' +'-handle_get_file/3-fun-0-' +'-handle_get_file/3-fun-1-' +'-handle_set_primary_archive/3-fun-0-' +'-handle_release_archives/1-fun-0-' +'-handle_list_dir/2-fun-0-' +'-handle_list_dir/2-fun-1-' +'-handle_read_file_info/2-fun-0-' +'-handle_read_file_info/2-fun-1-' +'-handle_get_cwd/2-fun-0-' +'-handle_get_cwd/2-fun-1-' +'-efile_multi_get_file_from_port2/8-fun-0-' +'-prim_set_primary_archive/3-fun-0-' +'-prim_get_file/2-fun-0-' +'-prim_list_dir/2-fun-0-' +'-prim_read_file_info/2-fun-0-' +'-open_archive/3-fun-0-' +'-foldl_archive/3-fun-0-' +normalize +vxworks_first2 +not_device +vxworks_first +win32_pathtype +unix_pathtype +ose +unix +pathtype +absname_vr +volumerelative +relative +absolute +ipv4_addr +ipv4_address +ipv4_list +string_split2 +string_split +string_match +no_match +no_split +do_name_split +name_split +to_strs +min +keyins +keysort +deep_member +send_all +win32 +is_basename +clear_cache +cache_new +foldl_archive +open_archive +apply_archive +prim_get_cwd +archive_read_file_info +prim_read_file_info +archive_list_dir +prim_list_dir +archive_get_file +archive +prim_get_file +prim_set_primary_archive +cache +release +primary +prim_do_release_archives +prim_release_archives +loader_debug +prim_init +port_error +ll_close +ll_open_set_bind +ll_udp_open +ll_tcp_connect +udp_options +tcp_timeout +tcp_options +inet_stop_port +inet_get_cwd +inet_read_file_info +inet_list_dir +inet_send_and_rcv +inet_get_file_from_port1 +inet_get_file_from_port +inet_timeout_handler +inet_exit_port +find_collect +find_loop +connect_master +find_master +efile_timeout_handler +efile_exit_port +noport +efile_stop_port +efile_get_cwd +efile_read_file_info +efile_list_dir +efile_release_archives +efile_set_primary_archive +efile_get_file_from_port3 +'prim_load port died' +efile_get_file_from_port2 +efile_get_file_from_port +efile_par_get_file +emfile +efile_multi_get_file_from_port2 +efile_multi_get_file_from_port +handle_timeout +handle_exit +handle_stop +handle_get_cwd +handle_read_file_info +handle_list_dir +handle_release_archives +handle_set_primary_archive +handle_get_file +handle_get_files +bad_state +error_report +std_error +enotdir +enoent +check_file_result +release_archives +set_primary_archive +get_path +init_ack +prim_state +'-filter_fun/0-fun-0-' +'-get_zip_input/1-fun-0-' +'-get_zip_input/1-fun-1-' +'-get_cd_loop/11-fun-0-' +'-get_cd_loop/11-fun-1-' +pwrite_binary +pwrite_iolist +skipper +skip_iolist +splitter +split_iolist +local_file_header_from_bin +bad_cd_file_header +cd_file_header_from_bin +dos_date_time_to_datetime +add_extra_info +cd_file_header_to_file_info +eocd_and_comment_from_bin +local_time +calendar +binary_io +set_file_info +prim_file_io +find_eocd_header +seek +bad_eocd +get_end_of_central_dir +get_filename_from_b2 +bad_central_directory +cd_file_header +get_file_header +get_cd_loop +eocd +get_central_dir +offset_over_z_data_descriptor +unsupported_compression +get_z_all +bad_local_file_header +bad_local_file_offset +local_file_header +get_z_file +get_zip_input +illegal_filter +primzip_file +do_foldl +foldl +primzip +do_open +filter_fun +prim_zip +need_dictionary +arg_mem +arg_bitsz +arg_method +filtered +huffman_only +arg_strategy +best_speed +best_compression +arg_level +full +arg_flush +collect +gunzip +gzip +unzip +deflated +zip +data_error +uncompress +finish +default +compress +getQSize +getBufSize +setBufSize +inflateEnd +inflate +inflateReset +inflateSync +inflateSetDictionary +inflateInit +deflateEnd +deflate +deflateParams +deflateReset +deflateSetDictionary +deflateInit +zlib +premature_end_of_list +lists_split +transform_ldata +get_uint32s +get_uint32 +get_uint64 +date_to_bytes +int_to_bytes +file_access +other +device +regular +symlink +file_type +transform_info_ints +translate_response +cur +bof +lseek_position +delayed_write +read_ahead +open_mode +port_died +bad_response_from_port +drv_get_response +drv_command +drv_command_raw +drv_close +drv_open +list_dir_int +list_dir +read_link_info_int +read_link_info +read_link_int +read_link +make_symlink_int +make_symlink +make_link_int +make_link +write_file_info_int +write_file_info +altname_int +altname +read_file_info_int +del_dir_int +del_dir +make_dir_int +make_dir +rename_int +delete_int +absname +filename +vxworks +set_cwd_int +set_cwd +get_cwd_int +get_cwd +write_file +read_file +ipread_s32bu_p32bu +copy_opened +file +truncate +position +pread_int +pread +enomem +read_line +sync +pwrite_int +pwrite +file_descriptor +open_int_setopts +open_int +efile +prim_file +bound +connecting +accepting +busy +multicast +no_pointtopoint +pointtopoint +no_broadcast +down +up +ssl +off +on +term +sctp_setadaptation +sctp_assoc_value +sctp_prim +sctp_setpeerprim +sctp_assocparams +sackdelay_disable +sackdelay_enable +pmtud_disable +pmtud_enable +hb_demand +hb_disable +hb_enable +sctp_paddrparams +sctp_event_subscribe +abort +addr_over +unordered +sctp_paddrinfo +'-bindx/3-lc$^0/1-0-' +ctl_cmd +get_ip6 +get_ip4 +get_ip +ip6_to_bytes +ip4_to_bytes +ip_to_bytes +rev +build_iflist +encode_ifname +enc_time +dec_status +dec_stats +decode_stats +recv_cnt +recv_max +recv_avg +recv_dvi +send_cnt +send_max +send_avg +recv_oct +send_oct +enc_stats +encode_stats +dec_subs +decode_subs +enc_subs +encode_subs +encode_ifopt_val +encode_ifopts +decode_ifopts +dec_ifopt +enc_ifopt +hwaddr +mtu +dstaddr +broadaddr +netmask +type_ifopt +merge_fields +merge +merge_options +need_template +dec +dec_opt_val +decode_opt_val +einval +enc_opts +encode_opts +once +enc_opt_val +encode_opt_val +enum_name +enum_val +enum_names +enum_vals +borlist +dec_value_tuple +decode +dec_value +enc_value_2 +enc_value_tuple +enc_value_1 +enc_value_default +enc_value +loopback +binary_or_uint +ether +addr +uint8 +uint16 +uint24 +uint32 +sctp_assoc_id +enum +bitenumlist +type_value_2 +type_value_record +type_value_tuple +type_value_1 +type_value_default +record +type_value +bool +int +ip +uint +type_opt_1 +type_opt +dec_opt +reuseaddr +keepalive +dontroute +linger +broadcast +sndbuf +recbuf +tos +nodelay +multicast_if +multicast_ttl +multicast_loop +add_membership +drop_membership +buffer +header +mode +deliver +exit_on_close +high_watermark +low_watermark +bit8 +send_timeout +delay_send +read_packets +send_timeout_close +sctp_rtoinfo +sctp_associnfo +sctp_initmsg +sctp_autoclose +sctp_nodelay +sctp_disable_fragments +sctp_i_want_mapped_v4_addr +sctp_maxseg +sctp_set_peer_primary_addr +sctp_primary_addr +sctp_adaptation_layer +sctp_peer_addr_params +sctp_events +sctp_delayed_ack_time +sctp_status +sctp_get_peer_addr_info +enc_opt +is_sockopt_val +attach +detach +unrecv +getservbyport1 +getservbyport +getservbyname1 +getservbyname +gethostname +getstatus +getprotocol +dgram +seqpacket +gettype +getindex +getfd +getstat +subscribe +ifset +ifget +getiflist +chgopts +chgopt +sctp_reply +getopts +getopt +setopt +setsockname +sockname +setpeername +peername +recvfrom0 +recvfrom +async_recv +recv0 +recv +sctp_default_send_param +sctp_sndrcvinfo +sendmsg +sendto +bool8 +listen +async_accept +accept_opts +accept0 +accept +async_connect +connect0 +bindx +remove +add +bind +close_pend_loop +send_pend +shutdown_pend_loop +shutdown_2 +subs_empty_out_q +shutdown_1 +write +read_write +drv2protocol +eprotonosupport +sctp +protocol2drv +open0 +fdopen1 +open1 +fdopen +inet +inet6 +prim_inet +shutdown_timeout +not_allowed +badrecord +starting +'-bs2as/1-fun-0-' +'-bs2ss/1-fun-0-' +'-boot/1-fun-0-' +'-notify/1-fun-0-' +'-alive_processes/0-lc$^0/1-0-' +'-do_boot/2-fun-0-' +'-par_load_modules/2-lc$^0/1-0-' +'-par_load_modules/2-fun-0-' +'-patch_path/2-lc$^0/1-0-' +'-patch_dir/2-lc$^0/1-1-' +'-patch_dir/2-lc$^1/1-0-' +'-shutdown_timer/1-fun-0-' +archive_extension +objfile_extension +concat +set_argument +get_argument1 +to_strings +get_flag_args +get_flag_list +get_flag +get_args +check +start_arg2 +eval_arg +start_arg +flag +start_extra_arg +end_args +arg +parse_boot_args +timer +flush_timout +'-shutdown_time' +shutdown_timer +load_mod_code +load_mod +exprs +erl_eval +parse_exprs +erl_parse +dot +erl_scan +start_it +start_em +start_in_kernel +join +funny_splitwith +funny_split +directory +file_info +read_file_info +patch_dir +patch_path +get_var_val +get_var_value +extract_var +add_var +fix_path +make_path +'cannot load' +get_files +par_load_modules +load_modules +'unexpected command in bootfile' +kernelProcess +preLoaded +primLoad +path +embedded +kernel_load_completed +eval_script +script +get_file +'bootfile format error' +'cannot get bootfile' +not_found +get_boot +'-pz' +'-pa' +path_flags +bootfile +'-boot_var' +'-init_debug' +'-mode' +'-root' +do_boot +'-path' +'-id' +'-hosts' +'-loader' +prim_load_flags +add_to_kernel +set_path +erl_prim_loader +start_prim_loader +sleep +kernel_pid +terminate +del +sub +do_unload +unload +kill_all_ports +kill_em +get_pids +alive_processes +kill_all_pids +resend +shutdown_loop +shutdown_kernel_pid +shutdown +heart +get_heart +shutdown_pids +stop_heart +clear_system +do_stop +stopping +'-config' +'-boot' +user +do_handle_msg +new_state +handle_msg +loop +ignore +new_kernelpid +garb_boot_loop +foreach +started +progress +boot_loop +crash +first198 +halt_string +things_to_string +flatten +printable_list +state +relaxed +strict +code_path_choice +flags_to_atoms_again +map +b2s +b2a +s +eval +prepare_run_args +reboot +request +wait_until_started +notify_when_started +make_permanent +ensure_loaded +fetch_loaded +get_status +bs2ss +bs2as +script_id +get_argument +get_plain_arguments +get_arguments +debug +fatal +boot +otp_ring0 +'TRACE' +'DELETE' +'PUT' +'POST' +'HEAD' +'GET' +'OPTIONS' +'Proxy-Connection' +'Keep-Alive' +'Cookie' +'X-Forwarded-For' +'Set-Cookie2' +'Set-Cookie' +'Accept-Ranges' +'Last-Modified' +'Expires' +'Etag' +'Content-Type' +'Content-Range' +'Content-Md5' +'Content-Location' +'Content-Length' +'Content-Language' +'Content-Encoding' +'Content-Base' +'Allow' +'Www-Authenticate' +'Warning' +'Vary' +'Server' +'Retry-After' +'Public' +'Proxy-Authenticate' +'Location' +'Age' +'User-Agent' +'Referer' +'Range' +'Proxy-Authorization' +'Max-Forwards' +'If-Unmodified-Since' +'If-Range' +'If-None-Match' +'If-Match' +'If-Modified-Since' +'Host' +'From' +'Authorization' +'Accept-Language' +'Accept-Encoding' +'Accept-Charset' +'Accept' +'Via' +'Upgrade' +'Transfer-Encoding' +'Pragma' +'Date' +'Connection' +'Cache-Control' +process_low +process_normal +process_high +process_max +characters_to_list_trap_4 +characters_to_list_trap_3 +characters_to_list_trap_2 +characters_to_list_trap_1 +characters_to_utf8_trap +md5_trap +empty_out_q +udp_error +tcp_error +tcp_closed +inet_reply +inet_async +udp +tcp +select_trap +'count_trap\000' +delete_trap +x86 +inc_stack_0 +handle_fp_exception +show_message_area +modeswitch_debug_off +modeswitch_debug_on +in_native +show_literals +show_term +show_pcb +nstack_used_size +show_nstack +show_heap +show_estack +stop_hrvtime +get_hrvtime +misc_timer_clear +misc_timer +gc_timer_clear +shared_gc_timer +gc_timer +send_timer_clear +send_timer +system_timer_clear +system_timer +pause_times +gc_info_clear +incremental_gc_info +shared_gc_info +gc_info +message_sizes +message_info_clear +message_info +process_info_clear +trap_count_clear +trap_count_get +call_count_clear +call_count_get +call_count_off +call_count_on +bs_validate_unicode_retract +bs_validate_unicode +bs_get_utf16 +bs_put_utf16le +bs_put_utf16be +bs_utf16_size +bs_get_utf8 +bs_put_utf8 +bs_utf8_size +bs_reallocate +bs_get_binary_2 +bs_get_float_2 +bs_get_integer_2 +bs_allocate +bs_put_bits +bs_put_small_float +bs_put_big_integer +fclearerror_error +conv_big_to_float +op_exact_eqeq_2 +cmp_2 +set_timeout +select_msg +check_get_msg +clear_timeout +atomic_inc +nonclosure_address +rethrow +hipe_apply +gc_1 +suspend_0 +suspend_msg_timeout +suspend_msg +load_fe +x86_abs_pcrel +constant +closure +c_const +remote +load_mfa +redirect_referred_from +remove_refs_from +mark_referred_from +add_ref +patch_call +patch_insn +get_rts_param +system_crc +check_crc +find_na_or_make_stub +make_fe +term_to_word +atom_to_word +primop_address +bif_address +enter_sdesc +code_size +update_code_size +invalidate_funinfo_native_addresses +set_funinfo_native_address +set_native_address +fun_to_address +merge_term +constants_size +alloc_data +enter_code +ref_set +ref_get +ref +array_update +array_sub +array_length +array +bitarray_update +bitarray_sub +bitarray +bytearray_update +bytearray_sub +bytearray +write_u32 +write_u8 +hipe_bifs +setopts +give_away +dflag_unicode_io +binary_to_existing_atom +binary_to_atom +atom_to_binary +bin_is_7bit +characters_to_list +characters_to_binary +decode_packet +update_element +bitstring_to_list +list_to_bitstring +bit_size +byte_size +tuple_size +is_bitstring +list_to_existing_atom +iolist_to_binary +iolist_size +make_fun +to_integer +string +is_boolean +get_module_info +warning_map +hibernate +blocking_read_file +is_native +is_module_native +make_stub_module +module_md5 +get_chunk +lock_counters +dump_links +dump_monitors +dist_ext_to_term +set_internal_state +get_internal_state +flat_size +same +disassemble +erts_debug +keyfind +search +keysearch +'erl.lang.list.keylist' +keymember +reverse +is_element +lists +run +format_error_int +loaded_drivers +try_unload +try_load +erl_ddll +getpid +setenv +'erl.system.os' +putenv +os +match_spec_run_r +match_spec_compile +select_delete +select_reverse +select_count +select +update_counter +slot +fixtable +safe_fixtable +rename +insert_new +insert +prev +is_key +member +match_object +last +lookup_element +lookup +is_compiled_ms +delete_object +delete_all_objects +'erl.lang.ets' +match_spec_test +is_record +is_function +is_binary +is_reference +is_port +is_pid +is_number +is_integer +is_float +is_tuple +is_list +is_atom +subtract +'--' +append +'++' +send +'!' +divide +multiply +minus +plus +not_arith_equal +not_equal +arith_equal +equal +less_or_equal +less +greater_or_equal +greater +'erl.lang.bool' +is_builtin +get_stacktrace +raise +is_process_alive +demonitor +fun_to_list +port_to_list +ref_to_list +system_profile +system_monitor +system_info +system_flag +append_element +make +make_tuple +read +read_timer +cancel +cancel_timer +send_after +'erl.lang.timer' +start_timer +pow +atan2 +sqrt +log10 +log +exp +erfc +erf +atanh +atan +asinh +asin +acosh +acos +tanh +tan +sinh +sin +cosh +'erl.lang.math' +cos +math +bump_reductions +process_display +resume_process +suspend_process +seq_trace_print +seq_trace_info +seq_trace +trace_delivered +trace_info +trace_pattern +get_data +port_get_data +set_data +port_set_data +port_connect +port_close +control +port_control +port_command +port_call +dist_exit +setnode +spawn_opt +whereis +unlink +utc_to_local +universaltime_to_localtime +utc +universaltime +tuple_to_list +trunc +tl +time_of_day +time +from_term +term_to_binary +statistics +split +split_binary +spawn_link +spawn +setelement +self +round +registered +put +purge +purge_module +process_info +set_flag +process_flag +preloaded +pre_loaded +port_info +pid_to_string +pid_to_list +'erl.lang.port' +open_port +now +nodes +monitor_node +function_exported +is_loaded +module_loaded +final +md5_final +update +md5_update +init +md5_init +digest +'erl.util.crypt.md5' +md5 +'erl.lang.ref' +make_ref +local_to_utc +localtime_to_universaltime +localtime +load +load_module +list_to_tuple +string_to_pid +list_to_pid +list_to_integer +list_to_float +from_list +list_to_binary +from_string +list_to_atom +link +length +'erl.lang.node' +is_alive +'erl.lang.integer' +integer_to_list +'erl.lang.list' +hd +hash +phash2 +phash +'erl.lang.system' +halt +set_group_leader +group_leader +get_keys +get +garbage_collect_message_area +'erl.system' +garbage_collect +'erl.lang.function' +fun_info +'erl.lang.float' +float_to_list +to_float +float +'erl.lang.term' +external_size +signal +'erl.lang.proc' +'erl.lang.proc.pdict' +erase +'erl.lang.tuple' +element +display_nl +display_string +'erl.system.debug' +display +delete +delete_module +today +'erl.util.date' +date +crc32_combine +'erl.util.crypt.crc32' +crc32 +check_process +'erl.system.code' +check_process_code +to_term +binary_to_term +to_list +'erl.lang.binary' +binary_to_list +to_string +'erl.lang.atom' +atom_to_list +'erl.lang' +apply +combine +adler32_combine +sum +'erl.util.crypt.adler32' +adler32 +'erl.lang.number' +abs +yield +yes +xor +write_concurrency +wordsize +warning_msg +warning +wall_clock +waiting +visible +version +values +value +unload_cancelled +unloaded_only +unloading +unloaded +unless_suspending +uniq +unblock +utf8 +used +use_stdio +urun +unregister +unicode +ungreedy +undef +ucompile +type +tuple +try_clause +trap_exit +tracer +trace_control_word +traced +trace_ts +trace +tpkt +total_heap_size +total +timestamp +'*' +timeout_value +threads +thread_pool_size +this +table +'SYSTEM' +system_architecture +system_version +system_limit +system_error +system +sys_misc +suspending +suspended +suspend +sunrm +stream +stop +stderr_to_stdout +static +status +start +stack_size +ssl_tls +spawn_driver +spawn_executable +sl_alloc +size +silent +shared +separate +set_tcw_fake +set_tcw +set_seq_token +set_on_spawn +set_on_link +set_on_first_spawn +set_on_first_link +set_cpu_topology +set +serial +sequential_trace_token +sequential_tracer +sensitive +scheme +schedulers_online +scheduler_id +scheduler +save_calls +runtime +running_procs +running_ports +running +runnable_procs +runnable_ports +runnable +run_queue +return_trace +return_to +return_from +restart +reset +rem +reload +registered_name +register +refc +reductions +recent_size +receive +ready_async +ready_output +ready_input +re_run_trap +re_pattern +re +raw +queue_size +quantify +purify +public +protection +protected +profile +procs +process_dump +process_limit +process_count +processes_used +processes_trap +processes +process +private +priority +print +port_count +ports +port +pid +permanent +pending_reload +pending_process +pending_driver +pending +pause +'+' +packet_size +packet +owner +output +out_exiting +out_exited +out +ose_ti_proc +ose_process_type +ose_process_prio +ose_pri_proc +ose_phantom +ose_int_proc +ose_bg_proc +os_version +os_type +orelse +ordered_set +or +open_error +open +on_load +old_heap_size +old_heap_block_size +ok +offset +objects +nouse_stdio +notsup +notify +notempty +noteol +notbol +notalive +not_purged +not_pending +not_loaded_by_this_process +not_loaded +not_a_list +not +no_network +no_integer +no_float +nosuspend +noproc +nofile +noeol +nodeup +nodedown_reason +nodedown +node_type +node +nocookie +noconnection +noconnect +no_auto_capture +none +nomatch +no +next +newline +new_uniq +new_index +new +net_kernel_terminated +net_kernel +'/=' +'=/=' +native_addresses +named_table +name +multiline +multi_scheduling +more +monitors +monitor_nodes +monitor +monitored_by +module_info +module +'-' +minor_version +min_heap_size +meta_match_spec +meta +messages +message_queue_len +message_binary +message +memory_types +memory +mbuf_size +max_processes +max_tables +maximum +max +match_spec +match +machine +'<' +low +long_gc +local +load_failure +load_cancelled +loaded +little +list +links +linked_in_driver +line_length +line +lf +'=<' +latin1 +last_calls +large_heap +label +known +kill_ports +killed +kill +keypos +io +is_seq_trace +is_constant +invalid +instruction_counts +internal_status +internal_error +input +initial_call +info_msg +info +index +inconsistent +incomplete +inactive +in_exiting +in +imports +if_clause +id +hybrid +httph_bin +http_bin +http_error +http_eoh +http_header +http_request +http_response +https +httph +http +hipe_architecture +high +hide +hidden +heir +heap_type +heap_sizes +heap_size +heap_block_size +grun +'>' +global_heaps_size +global +getting_unlinked +getting_linked +getenv +get_tcw +get_seq_token +generational +'>=' +gc_start +gc_end +garbage_collection +garbage_collecting +function_clause +functions +function +fullsweep_if_old_binaries +fullsweep_after +free +format_cpu_topology +flush_monitor_message +flush +flags +firstline +first +fd +fcgi +external +exports +exiting +existing +exit_status +exclusive +exact_reductions +event +'ETS-TRANSFER' +ets +error_logger +error_handler +'ERROR' +erlang +'==' +'=:=' +extended +exception_trace +exception_from +eol +eof +env +endian +enabled +enable_trace +emulator +elib_malloc +dupnames +duplicate_bag +dunlink +dsend +driver_options +driver +dotall +dollar_endonly +'$_' +'$$' +dmonitor_p +dmonitor_node +dlink +div +'/' +dist +display_items +disabled +disable_trace +dictionary +dgroup_leader +depth +dexit +delay_trap +debug_flags +data +current_function +creation +crlf +cr +cpu_timestamp +cpu +copy +context_switches +const +cons +connection_closed +connected +connect +compressed +compile +compat_rel +command +code +closed +close +clear +characters_to_list_int +characters_to_binary_int +cdr +cd +catchlevel +caseless +case_clause +capture +caller +call_count +busy_port +busy_dist_port +bsr_unicode +bsr_anycrlf +bsr +bsl +breakpoint +break_ignored +bxor +bor +bnot +blocked +block +binary +bif_return_trap +big +band +bag +badfun +badsig +badmatch +badfile +badarity +badarith +badarg +backtrace_depth +backtrace +awaiting_unload +awaiting_load +attributes +atom_used +atom +asynchronous +asn1 +arity +arg0 +args +anycrlf +any +andthen +andalso +and +anchored +already_loaded +allow_passive_connect +alloc_util_allocators +allocator_sizes +allocator +allocated_areas +allocated +all_but_first +all +active +absoluteURI +abs_path +aborted +'EXIT' +'UP' +'DOWN' +undefined_lambda +undefined_function +nocatch +undefined +exit +error +throw +return +call +normal +timeout +infinity +fun +'' +'$end_of_table' +'nonode@nohost' +'_' +true +false +=end diff --git a/web/api/webmachine/include/webmachine.hrl b/web/api/webmachine/include/webmachine.hrl new file mode 100644 index 0000000..be8d50c --- /dev/null +++ b/web/api/webmachine/include/webmachine.hrl @@ -0,0 +1,8 @@ +-export([ping/2]). + +-include_lib("webmachine/include/wm_reqdata.hrl"). + +ping(ReqData, State) -> + {pong, ReqData, State}. + + diff --git a/web/api/webmachine/include/wm_reqdata.hrl b/web/api/webmachine/include/wm_reqdata.hrl new file mode 100644 index 0000000..522fb48 --- /dev/null +++ b/web/api/webmachine/include/wm_reqdata.hrl @@ -0,0 +1,8 @@ +-record(wm_reqdata, {method, version, peer, wmreq, + disp_path, path, raw_path, path_info, path_tokens, + app_root,response_code,max_recv_body, + req_cookie, req_qs, req_headers, req_body, + resp_redirect, resp_headers, resp_body, + host_tokens, port + }). + diff --git a/web/api/webmachine/priv/skel/Emakefile b/web/api/webmachine/priv/skel/Emakefile new file mode 100644 index 0000000..7ee92ef --- /dev/null +++ b/web/api/webmachine/priv/skel/Emakefile @@ -0,0 +1,6 @@ +% -*- mode: erlang -*- +{["src/*"], + [{i, "include"}, + {outdir, "ebin"}, + debug_info] +}. diff --git a/web/api/webmachine/priv/skel/Makefile b/web/api/webmachine/priv/skel/Makefile new file mode 100644 index 0000000..f452051 --- /dev/null +++ b/web/api/webmachine/priv/skel/Makefile @@ -0,0 +1,19 @@ +ERL ?= erl +EBIN_DIRS := $(wildcard deps/*/ebin) +APP := skel + +all: erl ebin/$(APP).app + +erl: + @$(ERL) -pa $(EBIN_DIRS) -noinput +B \ + -eval 'case make:all() of up_to_date -> halt(0); error -> halt(1) end.' + +docs: + @erl -noshell -run edoc_run application '$(APP)' '"."' '[]' + +clean: + @echo "removing:" + @rm -fv ebin/*.beam ebin/*.app + +ebin/$(APP).app: src/$(APP).app + @cp -v src/$(APP).app $@ diff --git a/web/api/webmachine/priv/skel/deps/.empty b/web/api/webmachine/priv/skel/deps/.empty new file mode 100644 index 0000000..e69de29 diff --git a/web/api/webmachine/priv/skel/ebin/.empty b/web/api/webmachine/priv/skel/ebin/.empty new file mode 100644 index 0000000..e69de29 diff --git a/web/api/webmachine/priv/skel/priv/dispatch.conf b/web/api/webmachine/priv/skel/priv/dispatch.conf new file mode 100644 index 0000000..ef67fe2 --- /dev/null +++ b/web/api/webmachine/priv/skel/priv/dispatch.conf @@ -0,0 +1 @@ +{[], skel_resource, []}. diff --git a/web/api/webmachine/priv/skel/priv/www/index.html b/web/api/webmachine/priv/skel/priv/www/index.html new file mode 100644 index 0000000..8e7a2c6 --- /dev/null +++ b/web/api/webmachine/priv/skel/priv/www/index.html @@ -0,0 +1,8 @@ + + +It Worked + + +MochiWeb running. + + diff --git a/web/api/webmachine/priv/skel/src/skel.app b/web/api/webmachine/priv/skel/src/skel.app new file mode 100644 index 0000000..90e2026 --- /dev/null +++ b/web/api/webmachine/priv/skel/src/skel.app @@ -0,0 +1,14 @@ +{application, skel, + [{description, "skel"}, + {vsn, "0.1"}, + {modules, [ + skel, + skel_app, + skel_sup, + skel_deps, + skel_resource + ]}, + {registered, []}, + {mod, {skel_app, []}}, + {env, []}, + {applications, [kernel, stdlib, crypto]}]}. diff --git a/web/api/webmachine/priv/skel/src/skel.erl b/web/api/webmachine/priv/skel/src/skel.erl new file mode 100644 index 0000000..b0a5175 --- /dev/null +++ b/web/api/webmachine/priv/skel/src/skel.erl @@ -0,0 +1,40 @@ +%% @author author +%% @copyright YYYY author. + +%% @doc TEMPLATE. + +-module(skel). +-author('author '). +-export([start/0, start_link/0, stop/0]). + +ensure_started(App) -> + case application:start(App) of + ok -> + ok; + {error, {already_started, App}} -> + ok + end. + +%% @spec start_link() -> {ok,Pid::pid()} +%% @doc Starts the app for inclusion in a supervisor tree +start_link() -> + skel_deps:ensure(), + ensure_started(crypto), + ensure_started(webmachine), + skel_sup:start_link(). + +%% @spec start() -> ok +%% @doc Start the skel server. +start() -> + skel_deps:ensure(), + ensure_started(crypto), + ensure_started(webmachine), + application:start(skel). + +%% @spec stop() -> ok +%% @doc Stop the skel server. +stop() -> + Res = application:stop(skel), + application:stop(webmachine), + application:stop(crypto), + Res. diff --git a/web/api/webmachine/priv/skel/src/skel.hrl b/web/api/webmachine/priv/skel/src/skel.hrl new file mode 100644 index 0000000..8b13789 --- /dev/null +++ b/web/api/webmachine/priv/skel/src/skel.hrl @@ -0,0 +1 @@ + diff --git a/web/api/webmachine/priv/skel/src/skel_app.erl b/web/api/webmachine/priv/skel/src/skel_app.erl new file mode 100644 index 0000000..6b8228e --- /dev/null +++ b/web/api/webmachine/priv/skel/src/skel_app.erl @@ -0,0 +1,22 @@ +%% @author author +%% @copyright YYYY author. + +%% @doc Callbacks for the skel application. + +-module(skel_app). +-author('author '). + +-behaviour(application). +-export([start/2,stop/1]). + + +%% @spec start(_Type, _StartArgs) -> ServerRet +%% @doc application start callback for skel. +start(_Type, _StartArgs) -> + skel_deps:ensure(), + skel_sup:start_link(). + +%% @spec stop(_State) -> ServerRet +%% @doc application stop callback for skel. +stop(_State) -> + ok. diff --git a/web/api/webmachine/priv/skel/src/skel_deps.erl b/web/api/webmachine/priv/skel/src/skel_deps.erl new file mode 100644 index 0000000..b53552d --- /dev/null +++ b/web/api/webmachine/priv/skel/src/skel_deps.erl @@ -0,0 +1,84 @@ +%% @author author +%% @copyright YYYY author. + +%% @doc Ensure that the relatively-installed dependencies are on the code +%% loading path, and locate resources relative +%% to this application's path. + +-module(skel_deps). +-author('author '). + +-export([ensure/0, ensure/1]). +-export([get_base_dir/0, get_base_dir/1]). +-export([local_path/1, local_path/2]). +-export([deps_on_path/0, new_siblings/1]). + +%% @spec deps_on_path() -> [ProjNameAndVers] +%% @doc List of project dependencies on the path. +deps_on_path() -> + F = fun (X, Acc) -> + ProjDir = filename:dirname(X), + case {filename:basename(X), + filename:basename(filename:dirname(ProjDir))} of + {"ebin", "deps"} -> + [filename:basename(ProjDir) | Acc]; + _ -> + Acc + end + end, + ordsets:from_list(lists:foldl(F, [], code:get_path())). + +%% @spec new_siblings(Module) -> [Dir] +%% @doc Find new siblings paths relative to Module that aren't already on the +%% code path. +new_siblings(Module) -> + Existing = deps_on_path(), + SiblingEbin = filelib:wildcard(local_path(["deps", "*", "ebin"], Module)), + Siblings = [filename:dirname(X) || X <- SiblingEbin, + ordsets:is_element( + filename:basename(filename:dirname(X)), + Existing) =:= false], + lists:filter(fun filelib:is_dir/1, + lists:append([[filename:join([X, "ebin"]), + filename:join([X, "include"])] || + X <- Siblings])). + + +%% @spec ensure(Module) -> ok +%% @doc Ensure that all ebin and include paths for dependencies +%% of the application for Module are on the code path. +ensure(Module) -> + code:add_paths(new_siblings(Module)), + code:clash(), + ok. + +%% @spec ensure() -> ok +%% @doc Ensure that the ebin and include paths for dependencies of +%% this application are on the code path. Equivalent to +%% ensure(?Module). +ensure() -> + ensure(?MODULE). + +%% @spec get_base_dir(Module) -> string() +%% @doc Return the application directory for Module. It assumes Module is in +%% a standard OTP layout application in the ebin or src directory. +get_base_dir(Module) -> + {file, Here} = code:is_loaded(Module), + filename:dirname(filename:dirname(Here)). + +%% @spec get_base_dir() -> string() +%% @doc Return the application directory for this application. Equivalent to +%% get_base_dir(?MODULE). +get_base_dir() -> + get_base_dir(?MODULE). + +%% @spec local_path([string()], Module) -> string() +%% @doc Return an application-relative directory from Module's application. +local_path(Components, Module) -> + filename:join([get_base_dir(Module) | Components]). + +%% @spec local_path(Components) -> string() +%% @doc Return an application-relative directory for this application. +%% Equivalent to local_path(Components, ?MODULE). +local_path(Components) -> + local_path(Components, ?MODULE). diff --git a/web/api/webmachine/priv/skel/src/skel_resource.erl b/web/api/webmachine/priv/skel/src/skel_resource.erl new file mode 100644 index 0000000..7371681 --- /dev/null +++ b/web/api/webmachine/priv/skel/src/skel_resource.erl @@ -0,0 +1,13 @@ +%% @author author +%% @copyright YYYY author. +%% @doc Example webmachine_resource. + +-module(skel_resource). +-export([init/1, to_html/2]). + +-include_lib("webmachine/include/webmachine.hrl"). + +init([]) -> {ok, undefined}. + +to_html(ReqData, State) -> + {"Hello, new world", ReqData, State}. diff --git a/web/api/webmachine/priv/skel/src/skel_sup.erl b/web/api/webmachine/priv/skel/src/skel_sup.erl new file mode 100644 index 0000000..dda1c56 --- /dev/null +++ b/web/api/webmachine/priv/skel/src/skel_sup.erl @@ -0,0 +1,57 @@ +%% @author author +%% @copyright YYYY author. + +%% @doc Supervisor for the skel application. + +-module(skel_sup). +-author('author '). + +-behaviour(supervisor). + +%% External exports +-export([start_link/0, upgrade/0]). + +%% supervisor callbacks +-export([init/1]). + +%% @spec start_link() -> ServerRet +%% @doc API for starting the supervisor. +start_link() -> + supervisor:start_link({local, ?MODULE}, ?MODULE, []). + +%% @spec upgrade() -> ok +%% @doc Add processes if necessary. +upgrade() -> + {ok, {_, Specs}} = init([]), + + Old = sets:from_list( + [Name || {Name, _, _, _} <- supervisor:which_children(?MODULE)]), + New = sets:from_list([Name || {Name, _, _, _, _, _} <- Specs]), + Kill = sets:subtract(Old, New), + + sets:fold(fun (Id, ok) -> + supervisor:terminate_child(?MODULE, Id), + supervisor:delete_child(?MODULE, Id), + ok + end, ok, Kill), + + [supervisor:start_child(?MODULE, Spec) || Spec <- Specs], + ok. + +%% @spec init([]) -> SupervisorTree +%% @doc supervisor callback. +init([]) -> + Ip = case os:getenv("WEBMACHINE_IP") of false -> "0.0.0.0"; Any -> Any end, + {ok, Dispatch} = file:consult(filename:join( + [filename:dirname(code:which(?MODULE)), + "..", "priv", "dispatch.conf"])), + WebConfig = [ + {ip, Ip}, + {port, 8000}, + {log_dir, "priv/log"}, + {dispatch, Dispatch}], + Web = {webmachine_mochiweb, + {webmachine_mochiweb, start, [WebConfig]}, + permanent, 5000, worker, dynamic}, + Processes = [Web], + {ok, {{one_for_one, 10, 10}, Processes}}. diff --git a/web/api/webmachine/priv/skel/start-dev.sh b/web/api/webmachine/priv/skel/start-dev.sh new file mode 100755 index 0000000..4d2b9c0 --- /dev/null +++ b/web/api/webmachine/priv/skel/start-dev.sh @@ -0,0 +1,3 @@ +#!/bin/sh +cd `dirname $0` +exec erl -pa $PWD/ebin $PWD/deps/*/ebin $PWD/deps/*/deps/*/ebin -boot start_sasl -s reloader -s skel diff --git a/web/api/webmachine/priv/skel/start.sh b/web/api/webmachine/priv/skel/start.sh new file mode 100755 index 0000000..49d5a94 --- /dev/null +++ b/web/api/webmachine/priv/skel/start.sh @@ -0,0 +1,3 @@ +#!/bin/sh +cd `dirname $0` +exec erl -pa $PWD/ebin $PWD/deps/*/ebin $PWD/deps/*/deps/*/ebin -boot start_sasl -s skel diff --git a/web/api/webmachine/priv/www/index.html b/web/api/webmachine/priv/www/index.html new file mode 100644 index 0000000..e999208 --- /dev/null +++ b/web/api/webmachine/priv/www/index.html @@ -0,0 +1,8 @@ + + +It Worked + + +Running. + + diff --git a/web/api/webmachine/scripts/new_webmachine.erl b/web/api/webmachine/scripts/new_webmachine.erl new file mode 100755 index 0000000..67c813b --- /dev/null +++ b/web/api/webmachine/scripts/new_webmachine.erl @@ -0,0 +1,32 @@ +#!/usr/bin/env escript +%% -*- mode: erlang -*- +-export([main/1]). + +%% External API + +main([Name]) -> + case Name of + "." ++ _Rest -> usage(); + "~" ++ _Rest -> usage(); + "/" ++ _Rest -> usage(); + _Any -> main([Name, "."]) + end; +main([Name, Dest]) -> + ensure(), + DestDir = filename:absname(Dest), + ok = webmachine_skel:skelcopy(DestDir, Name); +main(_) -> + usage(). + +%% Internal API + +ensure() -> + code:add_patha(filename:join(filename:dirname(escript:script_name()), + "../ebin")). + +usage() -> + io:format("usage: ~s name [destdir]~n", + [filename:basename(escript:script_name())]), + halt(1). + + diff --git a/web/api/webmachine/src/webmachine.app b/web/api/webmachine/src/webmachine.app new file mode 100644 index 0000000..e9749fc --- /dev/null +++ b/web/api/webmachine/src/webmachine.app @@ -0,0 +1,27 @@ +{application, webmachine, + [{description, "webmachine"}, + {vsn, "1.5"}, + {modules, [ + webmachine, + webmachine_app, + webmachine_decision_core, + webmachine_deps, + webmachine_dispatcher, + webmachine_error_handler, + webmachine_logger, + webmachine_perf_logger, + webmachine_resource, + webmachine_request, + webmachine_request_srv, + webmachine_skel, + webmachine_sup, + webmachine_mochiweb, + webmachine_multipart, + webmachine_util, + wrq, + wmtrace_resource + ]}, + {registered, []}, + {mod, {webmachine_app, []}}, + {env, []}, + {applications, [kernel, stdlib, crypto]}]}. diff --git a/web/api/webmachine/src/webmachine.erl b/web/api/webmachine/src/webmachine.erl new file mode 100644 index 0000000..d9e3653 --- /dev/null +++ b/web/api/webmachine/src/webmachine.erl @@ -0,0 +1,45 @@ +%% @author Justin Sheehy +%% @author Andy Gross +%% @copyright 2007-2009 Basho Technologies +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. + +-module(webmachine). +-author('Justin Sheehy '). +-author('Andy Gross '). +-export([start/0, stop/0]). +-export([new_request/2]). + +%% @spec start() -> ok +%% @doc Start the webmachine server. +start() -> + webmachine_deps:ensure(), + application:start(crypto), + application:start(webmachine). + +%% @spec stop() -> ok +%% @doc Stop the webmachine server. +stop() -> + application:stop(webmachine). + +new_request(mochiweb, Request) -> + Socket = Request:get(socket), + Method = Request:get(method), + RawPath = Request:get(raw_path), + Version = Request:get(version), + Headers = Request:get(headers), + {ok, Pid} = webmachine_request_srv:start_link(Socket, Method, RawPath, Version, Headers), + webmachine_request:new(Pid). + + + diff --git a/web/api/webmachine/src/webmachine_app.erl b/web/api/webmachine/src/webmachine_app.erl new file mode 100644 index 0000000..a480458 --- /dev/null +++ b/web/api/webmachine/src/webmachine_app.erl @@ -0,0 +1,36 @@ +%% @author Justin Sheehy +%% @author Andy Gross +%% @copyright 2007-2008 Basho Technologies +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. + +%% @doc Callbacks for the webmachine application. + +-module(webmachine_app). +-author('Justin Sheehy '). +-author('Andy Gross '). + +-behaviour(application). +-export([start/2,stop/1]). + + +%% @spec start(_Type, _StartArgs) -> ServerRet +%% @doc application start callback for webmachine. +start(_Type, _StartArgs) -> + webmachine_deps:ensure(), + webmachine_sup:start_link(). + +%% @spec stop(_State) -> ServerRet +%% @doc application stop callback for webmachine. +stop(_State) -> + ok. diff --git a/web/api/webmachine/src/webmachine_decision_core.erl b/web/api/webmachine/src/webmachine_decision_core.erl new file mode 100644 index 0000000..5e466b7 --- /dev/null +++ b/web/api/webmachine/src/webmachine_decision_core.erl @@ -0,0 +1,634 @@ +%% @author Justin Sheehy +%% @author Andy Gross +%% @author Bryan Fink +%% @copyright 2007-2009 Basho Technologies +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. + +%% @doc Decision core for webmachine + +-module(webmachine_decision_core). +-author('Justin Sheehy '). +-author('Andy Gross '). +-author('Bryan Fink '). +-export([handle_request/2]). +-export([do_log/1]). +-include("webmachine_logger.hrl"). + +handle_request(Req, Resource) -> + put(req, Req), + put(resource, Resource), + try + d(v3b13) + catch + error:_ -> + error_response(erlang:get_stacktrace()) + end. + +wrcall(X) -> + Req = get(req), + Req:call(X). + +get_header_val(H) -> wrcall({get_req_header, H}). + +method() -> wrcall(method). + +d(DecisionID) -> + put(decision, DecisionID), + log_decision(DecisionID), + decision(DecisionID). + +respond(Code) -> + Resource = get(resource), + EndTime = now(), + case Code of + 404 -> + {ok, ErrorHandler} = application:get_env(webmachine, error_handler), + Reason = {none, none, []}, + ErrorHTML = ErrorHandler:render_error(Code, get(req), Reason), + wrcall({set_resp_body, ErrorHTML}); + 304 -> + wrcall({remove_resp_header, "Content-Type"}), + case resource_call(generate_etag) of + undefined -> nop; + ETag -> wrcall({set_resp_header, "ETag", ETag}) + end, + case resource_call(expires) of + undefined -> nop; + Exp -> + wrcall({set_resp_header, "Expires", + httpd_util:rfc1123_date( + calendar:universal_time_to_local_time(Exp))}) + end; + _ -> ignore + end, + put(code, Code), + wrcall({set_response_code, Code}), + resource_call(finish_request), + wrcall({send_response, Code}), + RMod = wrcall({get_metadata, 'resource_module'}), + LogData0 = wrcall(log_data), + LogData = LogData0#wm_log_data{resource_module=RMod, + end_time=EndTime}, + spawn(fun() -> do_log(LogData) end), + Resource:stop(), + Req = get(req), + Req:stop(). + +respond(Code, Headers) -> + wrcall({set_resp_headers, Headers}), + respond(Code). + +error_response(Code, Reason) -> + {ok, ErrorHandler} = application:get_env(webmachine, error_handler), + ErrorHTML = ErrorHandler:render_error(Code, get(req), Reason), + wrcall({set_resp_body, ErrorHTML}), + respond(Code). +error_response(Reason) -> + error_response(500, Reason). + +decision_test(Test,TestVal,TrueFlow,FalseFlow) -> + case Test of + {error, Reason} -> error_response(Reason); + {error, Reason0, Reason1} -> error_response({Reason0, Reason1}); + {halt, Code} -> respond(Code); + TestVal -> decision_flow(TrueFlow, Test); + _ -> decision_flow(FalseFlow, Test) + end. + +decision_flow(X, TestResult) when is_integer(X) -> + if X >= 500 -> error_response(X, TestResult); + true -> respond(X) + end; +decision_flow(X, _TestResult) when is_atom(X) -> d(X); +decision_flow({ErrCode, Reason}, _TestResult) when is_integer(ErrCode) -> + error_response(ErrCode, Reason). + +do_log(LogData) -> + LoggerModule = + case application:get_env(webmachine, webmachine_logger_module) of + {ok, Val} -> Val; + _ -> webmachine_logger + end, + LoggerModule:log_access(LogData), + case application:get_env(webmachine, enable_perf_logger) of + {ok, true} -> + webmachine_perf_logger:log(LogData); + _ -> + ignore + end. + +resource_call(Fun) -> + Resource = get(resource), + {Reply, NewResource} = Resource:do(Fun,get()), + put(resource, NewResource), + Reply. + +log_decision(DecisionID) -> + Resource = get(resource), + Resource:log_d(DecisionID). + +%% "Service Available" +decision(v3b13) -> + decision_test(resource_call(ping), pong, v3b13b, 503); +decision(v3b13b) -> + decision_test(resource_call(service_available), true, v3b12, 503); +%% "Known method?" +decision(v3b12) -> + decision_test(lists:member(method(), resource_call(known_methods)), + true, v3b11, 501); +%% "URI too long?" +decision(v3b11) -> + decision_test(resource_call(uri_too_long), true, 414, v3b10); +%% "Method allowed?" +decision(v3b10) -> + Methods = resource_call(allowed_methods), + case lists:member(method(), Methods) of + true -> + d(v3b9); + false -> + wrcall({set_resp_headers, [{"Allow", + string:join([atom_to_list(M) || M <- Methods], ", ")}]}), + respond(405) + end; +%% "Malformed?" +decision(v3b9) -> + decision_test(resource_call(malformed_request), true, 400, v3b8); +%% "Authorized?" +decision(v3b8) -> + case resource_call(is_authorized) of + true -> d(v3b7); + {error, Reason} -> + error_response(Reason); + {halt, Code} -> + respond(Code); + AuthHead -> + wrcall({set_resp_header, "WWW-Authenticate", AuthHead}), + respond(401) + end; +%% "Forbidden?" +decision(v3b7) -> + decision_test(resource_call(forbidden), true, 403, v3b6); +%% "Okay Content-* Headers?" +decision(v3b6) -> + decision_test(resource_call(valid_content_headers), true, v3b5, 501); +%% "Known Content-Type?" +decision(v3b5) -> + decision_test(resource_call(known_content_type), true, v3b4, 415); +%% "Req Entity Too Large?" +decision(v3b4) -> + decision_test(resource_call(valid_entity_length), true, v3b3, 413); +%% "OPTIONS?" +decision(v3b3) -> + case method() of + 'OPTIONS' -> + Hdrs = resource_call(options), + respond(200, Hdrs); + _ -> + d(v3c3) + end; +%% Accept exists? +decision(v3c3) -> + PTypes = [Type || {Type,_Fun} <- resource_call(content_types_provided)], + case get_header_val("accept") of + undefined -> + wrcall({set_metadata, 'content-type', hd(PTypes)}), + d(v3d4); + _ -> + d(v3c4) + end; +%% Acceptable media type available? +decision(v3c4) -> + PTypes = [Type || {Type,_Fun} <- resource_call(content_types_provided)], + AcceptHdr = get_header_val("accept"), + case webmachine_util:choose_media_type(PTypes, AcceptHdr) of + none -> + respond(406); + MType -> + wrcall({set_metadata, 'content-type', MType}), + d(v3d4) + end; +%% Accept-Language exists? +decision(v3d4) -> + decision_test(get_header_val("accept-language"), + undefined, v3e5, v3d5); +%% Acceptable Language available? %% WMACH-46 (do this as proper conneg) +decision(v3d5) -> + decision_test(resource_call(language_available), true, v3e5, 406); +%% Accept-Charset exists? +decision(v3e5) -> + case get_header_val("accept-charset") of + undefined -> decision_test(choose_charset("*"), + none, 406, v3f6); + _ -> d(v3e6) + end; +%% Acceptable Charset available? +decision(v3e6) -> + decision_test(choose_charset(get_header_val("accept-charset")), + none, 406, v3f6); +%% Accept-Encoding exists? +% (also, set content-type header here, now that charset is chosen) +decision(v3f6) -> + CType = wrcall({get_metadata, 'content-type'}), + CSet = case wrcall({get_metadata, 'chosen-charset'}) of + undefined -> ""; + CS -> "; charset=" ++ CS + end, + wrcall({set_resp_header, "Content-Type", CType ++ CSet}), + case get_header_val("accept-encoding") of + undefined -> + decision_test(choose_encoding("identity;q=1.0,*;q=0.5"), + none, 406, v3g7); + _ -> d(v3f7) + end; +%% Acceptable encoding available? +decision(v3f7) -> + decision_test(choose_encoding(get_header_val("accept-encoding")), + none, 406, v3g7); +%% "Resource exists?" +decision(v3g7) -> + % this is the first place after all conneg, so set Vary here + case variances() of + [] -> nop; + Variances -> + wrcall({set_resp_header, "Vary", string:join(Variances, ", ")}) + end, + decision_test(resource_call(resource_exists), true, v3g8, v3h7); +%% "If-Match exists?" +decision(v3g8) -> + decision_test(get_header_val("if-match"), undefined, v3h10, v3g9); +%% "If-Match: * exists" +decision(v3g9) -> + decision_test(get_header_val("if-match"), "*", v3h10, v3g11); +%% "ETag in If-Match" +decision(v3g11) -> + ReqETag = webmachine_util:unquote_header(get_header_val("if-match")), + decision_test(resource_call(generate_etag), ReqETag, v3h10, 412); +%% "If-Match: * exists" +decision(v3h7) -> + decision_test(get_header_val("if-match"), "*", 412, v3i7); +%% "If-unmodified-since exists?" +decision(v3h10) -> + decision_test(get_header_val("if-unmodified-since"),undefined,v3i12,v3h11); +%% "I-UM-S is valid date?" +decision(v3h11) -> + IUMSDate = get_header_val("if-unmodified-since"), + decision_test(webmachine_util:convert_request_date(IUMSDate), + bad_date, v3i12, v3h12); +%% "Last-Modified > I-UM-S?" +decision(v3h12) -> + ReqDate = get_header_val("if-unmodified-since"), + ReqErlDate = webmachine_util:convert_request_date(ReqDate), + ResErlDate = resource_call(last_modified), + decision_test(ResErlDate > ReqErlDate, + true, 412, v3i12); +%% "Moved permanently? (apply PUT to different URI)" +decision(v3i4) -> + case resource_call(moved_permanently) of + {true, MovedURI} -> + wrcall({set_resp_header, "Location", MovedURI}), + respond(301); + false -> + d(v3p3); + {error, Reason} -> + error_response(Reason); + {halt, Code} -> + respond(Code) + end; +%% PUT? +decision(v3i7) -> + decision_test(method(), 'PUT', v3i4, v3k7); +%% "If-none-match exists?" +decision(v3i12) -> + decision_test(get_header_val("if-none-match"), undefined, v3l13, v3i13); +%% "If-None-Match: * exists?" +decision(v3i13) -> + decision_test(get_header_val("if-none-match"), "*", v3j18, v3k13); +%% GET or HEAD? +decision(v3j18) -> + decision_test(lists:member(method(),['GET','HEAD']), + true, 304, 412); +%% "Moved permanently?" +decision(v3k5) -> + case resource_call(moved_permanently) of + {true, MovedURI} -> + wrcall({set_resp_header, "Location", MovedURI}), + respond(301); + false -> + d(v3l5); + {error, Reason} -> + error_response(Reason); + {halt, Code} -> + respond(Code) + end; +%% "Previously existed?" +decision(v3k7) -> + decision_test(resource_call(previously_existed), true, v3k5, v3l7); +%% "Etag in if-none-match?" +decision(v3k13) -> + ReqETag = webmachine_util:unquote_header(get_header_val("if-none-match")), + decision_test(resource_call(generate_etag), ReqETag, v3j18, v3l13); +%% "Moved temporarily?" +decision(v3l5) -> + case resource_call(moved_temporarily) of + {true, MovedURI} -> + wrcall({set_resp_header, "Location", MovedURI}), + respond(307); + false -> + d(v3m5); + {error, Reason} -> + error_response(Reason); + {halt, Code} -> + respond(Code) + end; +%% "POST?" +decision(v3l7) -> + decision_test(method(), 'POST', v3m7, 404); +%% "IMS exists?" +decision(v3l13) -> + decision_test(get_header_val("if-modified-since"), undefined, v3m16, v3l14); +%% "IMS is valid date?" +decision(v3l14) -> + IMSDate = get_header_val("if-modified-since"), + decision_test(webmachine_util:convert_request_date(IMSDate), + bad_date, v3m16, v3l15); +%% "IMS > Now?" +decision(v3l15) -> + NowDateTime = calendar:universal_time(), + ReqDate = get_header_val("if-modified-since"), + ReqErlDate = webmachine_util:convert_request_date(ReqDate), + decision_test(ReqErlDate > NowDateTime, + true, v3m16, v3l17); +%% "Last-Modified > IMS?" +decision(v3l17) -> + ReqDate = get_header_val("if-modified-since"), + ReqErlDate = webmachine_util:convert_request_date(ReqDate), + ResErlDate = resource_call(last_modified), + decision_test(ResErlDate =:= undefined orelse ResErlDate > ReqErlDate, + true, v3m16, 304); +%% "POST?" +decision(v3m5) -> + decision_test(method(), 'POST', v3n5, 410); +%% "Server allows POST to missing resource?" +decision(v3m7) -> + decision_test(resource_call(allow_missing_post), true, v3n11, 404); +%% "DELETE?" +decision(v3m16) -> + decision_test(method(), 'DELETE', v3m20, v3n16); +%% DELETE enacted immediately? +%% Also where DELETE is forced. +decision(v3m20) -> + decision_test(resource_call(delete_resource), true, v3m20b, 500); +decision(v3m20b) -> + decision_test(resource_call(delete_completed), true, v3o20, 202); +%% "Server allows POST to missing resource?" +decision(v3n5) -> + decision_test(resource_call(allow_missing_post), true, v3n11, 410); +%% "Redirect?" +decision(v3n11) -> + Stage1 = case resource_call(post_is_create) of + true -> + case resource_call(create_path) of + undefined -> error_response("post_is_create w/o create_path"); + NewPath -> + case is_list(NewPath) of + false -> error_response("create_path not a string"); + true -> + wrcall({set_disp_path, NewPath}), + Res = accept_helper(), + case Res of + {respond, Code} -> respond(Code); + {halt, Code} -> respond(Code); + {error, _,_} -> error_response(Res); + {error, _} -> error_response(Res); + _ -> stage1_ok + end + end + end; + _ -> + case resource_call(process_post) of + true -> + encode_body_if_set(), + stage1_ok; + {halt, Code} -> respond(Code); + Err -> error_response(Err) + end + end, + case Stage1 of + stage1_ok -> + case wrcall(resp_redirect) of + true -> + case wrcall({get_resp_header, "Location"}) of + undefined -> + respond(500, + "Response had do_redirect but no Location"); + _ -> + respond(303) + end; + _ -> + d(v3p11) + end; + _ -> nop + end; +%% "POST?" +decision(v3n16) -> + decision_test(method(), 'POST', v3n11, v3o16); +%% Conflict? +decision(v3o14) -> + case resource_call(is_conflict) of + true -> respond(409); + _ -> Res = accept_helper(), + case Res of + {respond, Code} -> respond(Code); + {halt, Code} -> respond(Code); + {error, _,_} -> error_response(Res); + {error, _} -> error_response(Res); + _ -> d(v3p11) + end + end; +%% "PUT?" +decision(v3o16) -> + decision_test(method(), 'PUT', v3o14, v3o18); +%% Multiple representations? +% (also where body generation for GET and HEAD is done) +decision(v3o18) -> + BuildBody = case method() of + 'GET' -> true; + 'HEAD' -> true; + _ -> false + end, + FinalBody = case BuildBody of + true -> + case resource_call(generate_etag) of + undefined -> nop; + ETag -> wrcall({set_resp_header, "ETag", ETag}) + end, + CT = wrcall({get_metadata, 'content-type'}), + case resource_call(last_modified) of + undefined -> nop; + LM -> + wrcall({set_resp_header, "Last-Modified", + httpd_util:rfc1123_date( + calendar:universal_time_to_local_time(LM))}) + end, + case resource_call(expires) of + undefined -> nop; + Exp -> + wrcall({set_resp_header, "Expires", + httpd_util:rfc1123_date( + calendar:universal_time_to_local_time(Exp))}) + end, + F = hd([Fun || {Type,Fun} <- resource_call(content_types_provided), + CT =:= Type]), + resource_call(F); + false -> nop + end, + case FinalBody of + {error, _} -> error_response(FinalBody); + {error, _,_} -> error_response(FinalBody); + {halt, Code} -> respond(Code); + nop -> d(v3o18b); + _ -> wrcall({set_resp_body, + encode_body(FinalBody)}), + d(v3o18b) + end; + +decision(v3o18b) -> + decision_test(resource_call(multiple_choices), true, 300, 200); +%% Response includes an entity? +decision(v3o20) -> + decision_test(wrcall(has_resp_body), true, v3o18, 204); +%% Conflict? +decision(v3p3) -> + case resource_call(is_conflict) of + true -> respond(409); + _ -> Res = accept_helper(), + case Res of + {respond, Code} -> respond(Code); + {halt, Code} -> respond(Code); + {error, _,_} -> error_response(Res); + {error, _} -> error_response(Res); + _ -> d(v3p11) + end + end; + +%% New resource? (at this point boils down to "has location header") +decision(v3p11) -> + case wrcall({get_resp_header, "Location"}) of + undefined -> d(v3o20); + _ -> respond(201) + end. + +accept_helper() -> + CT = case get_header_val("Content-Type") of + undefined -> "application/octet-stream"; + Other -> Other + end, + {MT, MParams} = webmachine_util:media_type_to_detail(CT), + wrcall({set_metadata, 'mediaparams', MParams}), + case [Fun || {Type,Fun} <- + resource_call(content_types_accepted), MT =:= Type] of + [] -> {respond,415}; + AcceptedContentList -> + F = hd(AcceptedContentList), + case resource_call(F) of + true -> + encode_body_if_set(), + true; + Result -> Result + end + end. + +encode_body_if_set() -> + case wrcall(has_resp_body) of + true -> + Body = wrcall(resp_body), + wrcall({set_resp_body, encode_body(Body)}), + true; + _ -> false + end. + +encode_body(Body) -> + ChosenCSet = wrcall({get_metadata, 'chosen-charset'}), + Charsetter = + case resource_call(charsets_provided) of + no_charset -> fun(X) -> X end; + CP -> hd([Fun || {CSet,Fun} <- CP, ChosenCSet =:= CSet]) + end, + ChosenEnc = wrcall({get_metadata, 'content-encoding'}), + Encoder = hd([Fun || {Enc,Fun} <- resource_call(encodings_provided), + ChosenEnc =:= Enc]), + case Body of + {stream, StreamBody} -> + {stream, make_encoder_stream(Encoder, Charsetter, StreamBody)}; + _ -> + Encoder(Charsetter(iolist_to_binary(Body))) + end. + +make_encoder_stream(Encoder, Charsetter, {Body, done}) -> + {Encoder(Charsetter(Body)), done}; +make_encoder_stream(Encoder, Charsetter, {Body, Next}) -> + {Encoder(Charsetter(Body)), + fun() -> make_encoder_stream(Encoder, Charsetter, Next()) end}. + +choose_encoding(AccEncHdr) -> + Encs = [Enc || {Enc,_Fun} <- resource_call(encodings_provided)], + case webmachine_util:choose_encoding(Encs, AccEncHdr) of + none -> none; + ChosenEnc -> + case ChosenEnc of + "identity" -> + nop; + _ -> + wrcall({set_resp_header, "Content-Encoding",ChosenEnc}) + end, + wrcall({set_metadata, 'content-encoding',ChosenEnc}), + ChosenEnc + end. + +choose_charset(AccCharHdr) -> + case resource_call(charsets_provided) of + no_charset -> + no_charset; + CL -> + CSets = [CSet || {CSet,_Fun} <- CL], + case webmachine_util:choose_charset(CSets, AccCharHdr) of + none -> none; + Charset -> + wrcall({set_metadata, 'chosen-charset',Charset}), + Charset + end + end. + +variances() -> + Accept = case length(resource_call(content_types_provided)) of + 1 -> []; + 0 -> []; + _ -> ["Accept"] + end, + AcceptEncoding = case length(resource_call(encodings_provided)) of + 1 -> []; + 0 -> []; + _ -> ["Accept-Encoding"] + end, + AcceptCharset = case resource_call(charsets_provided) of + no_charset -> []; + CP -> + case length(CP) of + 1 -> []; + 0 -> []; + _ -> ["Accept-Charset"] + end + end, + Accept ++ AcceptEncoding ++ AcceptCharset ++ resource_call(variances). + diff --git a/web/api/webmachine/src/webmachine_deps.erl b/web/api/webmachine/src/webmachine_deps.erl new file mode 100644 index 0000000..f511e8f --- /dev/null +++ b/web/api/webmachine/src/webmachine_deps.erl @@ -0,0 +1,92 @@ +%% @author Justin Sheehy +%% @author Andy Gross +%% @copyright 2007-2008 Basho Technologies +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. + +%% @doc Ensure that the relatively-installed dependencies are on the code +%% loading path, and locate resources relative +%% to this application's path. + +-module(webmachine_deps). +-author('Justin Sheehy '). +-author('Andy Gross '). + +-export([ensure/0, ensure/1]). +-export([get_base_dir/0, get_base_dir/1]). +-export([local_path/1, local_path/2]). +-export([deps_on_path/0, new_siblings/1]). + +%% @spec deps_on_path() -> [ProjNameAndVers] +%% @doc List of project dependencies on the path. +deps_on_path() -> + ordsets:from_list([filename:basename(filename:dirname(X)) || X <- code:get_path()]). + +%% @spec new_siblings(Module) -> [Dir] +%% @doc Find new siblings paths relative to Module that aren't already on the +%% code path. +new_siblings(Module) -> + Existing = deps_on_path(), + SiblingEbin = [ X || X <- filelib:wildcard(local_path(["deps", "*", "ebin"], Module)), + filename:basename(filename:dirname(X)) /= %% don't include self + filename:basename(filename:dirname( + filename:dirname( + filename:dirname(X)))) ], + Siblings = [filename:dirname(X) || X <- SiblingEbin, + ordsets:is_element( + filename:basename(filename:dirname(X)), + Existing) =:= false], + lists:filter(fun filelib:is_dir/1, + lists:append([[filename:join([X, "ebin"]), + filename:join([X, "include"])] || + X <- Siblings])). + + +%% @spec ensure(Module) -> ok +%% @doc Ensure that all ebin and include paths for dependencies +%% of the application for Module are on the code path. +ensure(Module) -> + code:add_paths(new_siblings(Module)), + code:clash(), + ok. + +%% @spec ensure() -> ok +%% @doc Ensure that the ebin and include paths for dependencies of +%% this application are on the code path. Equivalent to +%% ensure(?Module). +ensure() -> + ensure(?MODULE). + +%% @spec get_base_dir(Module) -> string() +%% @doc Return the application directory for Module. It assumes Module is in +%% a standard OTP layout application in the ebin or src directory. +get_base_dir(Module) -> + {file, Here} = code:is_loaded(Module), + filename:dirname(filename:dirname(Here)). + +%% @spec get_base_dir() -> string() +%% @doc Return the application directory for this application. Equivalent to +%% get_base_dir(?MODULE). +get_base_dir() -> + get_base_dir(?MODULE). + +%% @spec local_path([string()], Module) -> string() +%% @doc Return an application-relative directory from Module's application. +local_path(Components, Module) -> + filename:join([get_base_dir(Module) | Components]). + +%% @spec local_path(Components) -> string() +%% @doc Return an application-relative directory for this application. +%% Equivalent to local_path(Components, ?MODULE). +local_path(Components) -> + local_path(Components, ?MODULE). diff --git a/web/api/webmachine/src/webmachine_dispatcher.erl b/web/api/webmachine/src/webmachine_dispatcher.erl new file mode 100644 index 0000000..4c4356b --- /dev/null +++ b/web/api/webmachine/src/webmachine_dispatcher.erl @@ -0,0 +1,203 @@ +%% @author Robert Ahrens +%% @author Justin Sheehy +%% @copyright 2007-2009 Basho Technologies +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. + +%% @doc Module for URL-dispatch by pattern matching. + +-module(webmachine_dispatcher). +-author('Robert Ahrens '). +-author('Justin Sheehy '). +-author('Bryan Fink '). + +-export([dispatch/2, dispatch/3]). + +-define(SEPARATOR, $\/). +-define(MATCH_ALL, '*'). + +%% @spec dispatch(Path::string(), DispatchList::[matchterm()]) -> +%% dispterm() | dispfail() +%% @doc Interface for URL dispatching. +%% See also http://bitbucket.org/justin/webmachine/wiki/DispatchConfiguration +dispatch(PathAsString, DispatchList) -> + dispatch([], PathAsString, DispatchList). + +%% @spec dispatch(Host::string(), Path::string(), +%% DispatchList::[matchterm()]) -> +%% dispterm() | dispfail() +%% @doc Interface for URL dispatching. +%% See also http://bitbucket.org/justin/webmachine/wiki/DispatchConfiguration +dispatch(HostAsString, PathAsString, DispatchList) -> + Path = string:tokens(PathAsString, [?SEPARATOR]), + % URIs that end with a trailing slash are implicitly one token + % "deeper" than we otherwise might think as we are "inside" + % a directory named by the last token. + ExtraDepth = case lists:last(PathAsString) == ?SEPARATOR of + true -> 1; + _ -> 0 + end, + {Host, Port} = split_host_port(HostAsString), + try_host_binding(DispatchList, lists:reverse(Host), Port, + Path, ExtraDepth). + +split_host_port(HostAsString) -> + case string:tokens(HostAsString, ":") of + [HostPart, PortPart] -> + {split_host(HostPart), list_to_integer(PortPart)}; + [HostPart] -> + {split_host(HostPart), 80}; + [] -> + %% no host header + {[], 80} + end. + +split_host(HostAsString) -> + string:tokens(HostAsString, "."). + +%% @type matchterm() = hostmatchterm() | pathmatchterm() +% The dispatch configuration is a list of these terms, and the +% first one whose host and path terms match the input is used. +% Using a pathmatchterm() here is equivalent to using a hostmatchterm() +% of the form {{['*'],'*'}, [pathmatchterm()]}. + +%% @type hostmatchterm() = {hostmatch(), [pathmatchterm()]} +% The dispatch configuration contains a list of these terms, and the +% first one whose host and one pathmatchterm match is used. + +%% @type hostmatch() = [hostterm()] | {[hostterm()], portterm()} +% A host header (Host, X-Forwarded-For, etc.) will be matched against +% this term. Using a raws [hostterm()] list is equivalent to using +% {[hostterm()], '*'}. + +%% @type hostterm() = '*' | string() | atom() +% A list of hostterms is matched against a '.'-separated hostname. +% The '*' hosterm matches all remaining tokens, and is only allowed at +% the head of the list. +% A string hostterm will match a token of exactly the same string. +% Any atom hostterm other than '*' will match any token and will +% create a binding in the result if a complete match occurs. + +%% @type portterm() = '*' | integer() | atom() +% A portterm is matched against the integer port after any ':' in +% the hostname, or 80 if no port is found. +% The '*' portterm patches any port +% An integer portterm will match a port of exactly the same integer. +% Any atom portterm other than '*' will match any port and will +% create a binding in the result if a complete match occurs. + +%% @type pathmatchterm() = {[pathterm()], matchmod(), matchopts()}. +% The dispatch configuration contains a list of these terms, and the +% first one whose list of pathterms matches the input path is used. + +%% @type pathterm() = '*' | string() | atom(). +% A list of pathterms is matched against a '/'-separated input path. +% The '*' pathterm matches all remaining tokens. +% A string pathterm will match a token of exactly the same string. +% Any atom pathterm other than '*' will match any token and will +% create a binding in the result if a complete match occurs. + +%% @type matchmod() = atom(). +% This atom, if present in a successful matchterm, will appear in +% the resulting dispterm. In Webmachine this is used to name the +% resource module that will handle the matching request. + +%% @type matchopts() = [term()]. +% This term, if present in a successful matchterm, will appear in +% the resulting dispterm. In Webmachine this is used to provide +% arguments to the resource module handling the matching request. + +%% @type dispterm() = {matchmod(), matchopts(), pathtokens(), +%% bindings(), approot(), stringpath()}. + +%% @type pathtokens() = [pathtoken()]. +% This is the list of tokens matched by a trailing '*' pathterm. + +%% @type pathtoken() = string(). + +%% @type bindings() = [{bindingterm(),pathtoken()}]. +% This is a proplist of bindings indicated by atom terms in the +% matching spec, bound to the matching tokens in the request path. + +%% @type approot() = string(). + +%% @type stringpath() = string(). +% This is the path portion matched by a trailing '*' pathterm. + +%% @type dispfail() = {no_dispatch_match, pathtokens()}. + +try_host_binding([], Host, Port, Path, _Depth) -> + {no_dispatch_match, {Host, Port}, Path}; +try_host_binding([Dispatch|Rest], Host, Port, Path, Depth) -> + {{HostSpec,PortSpec},PathSpec} = + case Dispatch of + {{H,P},S} -> {{H,P},S}; + {H,S} -> {{H,?MATCH_ALL},S}; + S -> {{[?MATCH_ALL],?MATCH_ALL},[S]} + end, + case bind_port(PortSpec, Port, []) of + {ok, PortBindings} -> + case bind(lists:reverse(HostSpec), Host, PortBindings, 0) of + {ok, HostRemainder, HostBindings, _} -> + case try_path_binding(PathSpec, Path, HostBindings, Depth) of + {Mod, Props, PathRemainder, PathBindings, + AppRoot, StringPath} -> + {Mod, Props, HostRemainder, Port, PathRemainder, + PathBindings, AppRoot, StringPath}; + {no_dispatch_match, _} -> + try_host_binding(Rest, Host, Port, Path, Depth) + end; + fail -> + try_host_binding(Rest, Host, Port, Path, Depth) + end; + fail -> + try_host_binding(Rest, Host, Port, Path, Depth) + end. + +bind_port(Port, Port, Bindings) -> {ok, Bindings}; +bind_port(?MATCH_ALL, _Port, Bindings) -> {ok, Bindings}; +bind_port(PortAtom, Port, Bindings) when is_atom(PortAtom) -> + {ok, [{PortAtom, Port}|Bindings]}; +bind_port(_, _, _) -> fail. + +try_path_binding([], PathTokens, _, _) -> + {no_dispatch_match, PathTokens}; +try_path_binding([{PathSchema, Mod, Props}|Rest], PathTokens, + Bindings, ExtraDepth) -> + case bind(PathSchema, PathTokens, Bindings, 0) of + {ok, Remainder, NewBindings, Depth} -> + {Mod, Props, Remainder, NewBindings, + calculate_app_root(Depth + ExtraDepth), reconstitute(Remainder)}; + fail -> + try_path_binding(Rest, PathTokens, Bindings, ExtraDepth) + end. + +bind([], [], Bindings, Depth) -> + {ok, [], Bindings, Depth}; +bind([?MATCH_ALL], Rest, Bindings, Depth) when is_list(Rest) -> + {ok, Rest, Bindings, Depth + length(Rest)}; +bind(_, [], _, _) -> + fail; +bind([Token|RestToken],[Match|RestMatch],Bindings,Depth) when is_atom(Token) -> + bind(RestToken, RestMatch, [{Token, Match}|Bindings], Depth + 1); +bind([Token|RestToken], [Token|RestMatch], Bindings, Depth) -> + bind(RestToken, RestMatch, Bindings, Depth + 1); +bind(_, _, _, _) -> + fail. + +reconstitute([]) -> ""; +reconstitute(UnmatchedTokens) -> string:join(UnmatchedTokens, [?SEPARATOR]). + +calculate_app_root(1) -> "."; +calculate_app_root(N) when N > 1 -> + string:join(lists:duplicate(N, ".."), [?SEPARATOR]). diff --git a/web/api/webmachine/src/webmachine_error_handler.erl b/web/api/webmachine/src/webmachine_error_handler.erl new file mode 100644 index 0000000..0530326 --- /dev/null +++ b/web/api/webmachine/src/webmachine_error_handler.erl @@ -0,0 +1,71 @@ +%% @author Justin Sheehy +%% @author Andy Gross +%% @author Jeremy Latt +%% @copyright 2007-2008 Basho Technologies +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. + + +%% @doc Some fairly minimal error message formatters. + +-module(webmachine_error_handler). +-author('Justin Sheehy '). +-author('Andy Gross '). +-author('Jeremy Latt '). + +-export([render_error/3]). + +render_error(Code, Req, Reason) -> + case Req:has_response_body() of + true -> Req:response_body(); + false -> render_error_body(Code, Req, Reason) + end. + +render_error_body(404, Req, _Reason) -> + Req:add_response_header("Content-Type", "text/html"), + <<"404 Not Found

Not Found

The requested document was not found on this server.


mochiweb+webmachine web server
">>; + +render_error_body(500, Req, Reason) -> + Req:add_response_header("Content-Type", "text/html"), + error_logger:error_msg("webmachine error: path=~p~n~p~n", [Req:path(), Reason]), + STString = io_lib:format("~p", [Reason]), + ErrorStart = "500 Internal Server Error

Internal Server Error

The server encountered an error while processing this request:
",
+    ErrorEnd = "


mochiweb+webmachine web server
", + ErrorIOList = [ErrorStart,STString,ErrorEnd], + erlang:iolist_to_binary(ErrorIOList); + +render_error_body(501, Req, _Reason) -> + Req:add_response_header("Content-Type", "text/html"), + error_logger:error_msg("Webmachine does not support method ~p~n", + [Req:method()]), + ErrorStr = io_lib:format("501 Not Implemented" + "

Internal Server Error

" + "The server does not support the ~p method.
" + "


mochiweb+webmachine web server" + "
", + [Req:method()]), + erlang:iolist_to_binary(ErrorStr); + +render_error_body(503, Req, _Reason) -> + Req:add_response_header("Content-Type", "text/html"), + error_logger:error_msg("Webmachine cannot fulfill" + " the request at this time"), + ErrorStr = "503 Service Unavailable" + "

Service Unavailable

" + "The server is currently unable to handle " + "the request due to a temporary overloading " + "or maintenance of the server.
" + "


mochiweb+webmachine web server" + "
", + list_to_binary(ErrorStr). + diff --git a/web/api/webmachine/src/webmachine_logger.erl b/web/api/webmachine/src/webmachine_logger.erl new file mode 100644 index 0000000..1de6945 --- /dev/null +++ b/web/api/webmachine/src/webmachine_logger.erl @@ -0,0 +1,224 @@ +%% @author Justin Sheehy +%% @author Andy Gross +%% @copyright 2007-2008 Basho Technologies +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. + +-module(webmachine_logger). +-author('Justin Sheehy '). +-author('Andy Gross '). +-behaviour(gen_server). +-export([start_link/1]). +-export([init/1, handle_call/3, handle_cast/2, handle_info/2, + terminate/2, code_change/3]). +-export([log_access/1, refresh/0]). +-include("webmachine_logger.hrl"). +-record(state, {hourstamp, filename, handle}). + +alog_path(BaseDir) -> + filename:join(BaseDir, "access.log"). + +start_link(BaseDir) -> + gen_server:start_link({local, ?MODULE}, ?MODULE, [BaseDir], []). + +init([BaseDir]) -> + defer_refresh(), + FileName = alog_path(BaseDir), + DateHour = datehour(), + filelib:ensure_dir(FileName), + Handle = log_open(FileName, DateHour), + {ok, #state{filename=FileName, handle=Handle, hourstamp=DateHour}}. + +refresh() -> + refresh(now()). + +refresh(Time) -> + gen_server:cast(?MODULE, {refresh, Time}). + +log_access(#wm_log_data{}=D) -> + gen_server:call(?MODULE, {log_access, D}). + +handle_call({log_access, LogData}, _From, State) -> + NewState = maybe_rotate(State, now()), + Msg = format_req(LogData), + log_write(NewState#state.handle, Msg), + {reply, ok, NewState}. + +handle_cast({refresh, Time}, State) -> + {noreply, maybe_rotate(State, Time)}. + +handle_info({_Label, {From, MRef}, get_modules}, State) -> + From ! {MRef, [?MODULE]}, + {noreply, State}; +handle_info(_Info, State) -> + {noreply, State}. + +terminate(_Reason, _State) -> + ok. + +code_change(_OldVsn, State, _Extra) -> + {ok, State}. + +log_open(FileName, DateHour) -> + LogName = FileName ++ suffix(DateHour), + io:format("opening log file: ~p~n", [LogName]), + {ok, FD} = file:open(LogName, [read, write, raw]), + {ok, Location} = file:position(FD, eof), + fix_log(FD, Location), + file:truncate(FD), + {?MODULE, LogName, FD}. + +log_write({?MODULE, _Name, FD}, IoData) -> + file:write(FD, lists:flatten(IoData)). + + +log_close({?MODULE, Name, FD}) -> + io:format("~p: closing log file: ~p~n", [?MODULE, Name]), + file:close(FD). + +maybe_rotate(State, Time) -> + ThisHour = datehour(Time), + if ThisHour == State#state.hourstamp -> + State; + true -> + defer_refresh(), + log_close(State#state.handle), + Handle = log_open(State#state.filename, ThisHour), + State#state{hourstamp=ThisHour, handle=Handle} + end. + +format_req(#wm_log_data{method=Method, + headers=Headers, + peer=Peer, + path=Path, + version=Version, + response_code=ResponseCode, + response_length=ResponseLength}) -> + User = "-", + Time = fmtnow(), + Status = integer_to_list(ResponseCode), + Length = integer_to_list(ResponseLength), + Referer = + case mochiweb_headers:get_value("Referer", Headers) of + undefined -> ""; + R -> R + end, + UserAgent = + case mochiweb_headers:get_value("User-Agent", Headers) of + undefined -> ""; + U -> U + end, + fmt_alog(Time, Peer, User, fmt_method(Method), Path, Version, + Status, Length, Referer, UserAgent). + +fmt_method(M) when is_atom(M) -> atom_to_list(M); +fmt_method(M) when is_list(M) -> M. + + +%% Seek backwards to the last valid log entry +fix_log(_FD, 0) -> + ok; +fix_log(FD, 1) -> + {ok, 0} = file:position(FD, 0), + ok; +fix_log(FD, Location) -> + case file:pread(FD, Location - 1, 1) of + {ok, [$\n | _]} -> + ok; + {ok, _} -> + fix_log(FD, Location - 1) + end. + +defer_refresh() -> + {_, {_, M, S}} = calendar:universal_time(), + Time = 1000 * (3600 - ((M * 60) + S)), + timer:apply_after(Time, ?MODULE, refresh, []). + +datehour() -> + datehour(now()). + +datehour(Now) -> + {{Y, M, D}, {H, _, _}} = calendar:now_to_universal_time(Now), + {Y, M, D, H}. + +zeropad_str(NumStr, Zeros) when Zeros > 0 -> + zeropad_str([$0 | NumStr], Zeros - 1); +zeropad_str(NumStr, _) -> + NumStr. + +zeropad(Num, MinLength) -> + NumStr = integer_to_list(Num), + zeropad_str(NumStr, MinLength - length(NumStr)). + +suffix({Y, M, D, H}) -> + YS = zeropad(Y, 4), + MS = zeropad(M, 2), + DS = zeropad(D, 2), + HS = zeropad(H, 2), + lists:flatten([$., YS, $_, MS, $_, DS, $_, HS]). + +fmt_alog(Time, Ip, User, Method, Path, {VM,Vm}, + Status, Length, Referrer, UserAgent) -> + [fmt_ip(Ip), " - ", User, [$\s], Time, [$\s, $"], Method, " ", Path, + " HTTP/", integer_to_list(VM), ".", integer_to_list(Vm), [$",$\s], + Status, [$\s], Length, [$\s,$"], Referrer, + [$",$\s,$"], UserAgent, [$",$\n]]. + +month(1) -> + "Jan"; +month(2) -> + "Feb"; +month(3) -> + "Mar"; +month(4) -> + "Apr"; +month(5) -> + "May"; +month(6) -> + "Jun"; +month(7) -> + "Jul"; +month(8) -> + "Aug"; +month(9) -> + "Sep"; +month(10) -> + "Oct"; +month(11) -> + "Nov"; +month(12) -> + "Dec". +zone() -> + Time = erlang:universaltime(), + LocalTime = calendar:universal_time_to_local_time(Time), + DiffSecs = calendar:datetime_to_gregorian_seconds(LocalTime) - calendar:datetime_to_gregorian_seconds(Time), + zone((DiffSecs/3600)*100). + +%% Ugly reformatting code to get times like +0000 and -1300 + +zone(Val) when Val < 0 -> + io_lib:format("-~4..0w", [trunc(abs(Val))]); +zone(Val) when Val >= 0 -> + io_lib:format("+~4..0w", [trunc(abs(Val))]). + +fmt_ip(IP) when is_tuple(IP) -> + inet_parse:ntoa(IP); +fmt_ip(undefined) -> + "0.0.0.0"; +fmt_ip(HostName) -> + HostName. + +fmtnow() -> + {{Year, Month, Date}, {Hour, Min, Sec}} = calendar:local_time(), + io_lib:format("[~2..0w/~s/~4..0w:~2..0w:~2..0w:~2..0w ~s]", + [Date,month(Month),Year, Hour, Min, Sec, zone()]). diff --git a/web/api/webmachine/src/webmachine_logger.hrl b/web/api/webmachine/src/webmachine_logger.hrl new file mode 100644 index 0000000..3d6a1ed --- /dev/null +++ b/web/api/webmachine/src/webmachine_logger.hrl @@ -0,0 +1,13 @@ +-record(wm_log_data, + {resource_module :: atom(), + start_time :: tuple(), + method :: atom(), + headers, + peer, + path :: string(), + version, + response_code, + response_length, + end_time :: tuple(), + finish_time :: tuple()}). + diff --git a/web/api/webmachine/src/webmachine_mochiweb.erl b/web/api/webmachine/src/webmachine_mochiweb.erl new file mode 100644 index 0000000..92551bc --- /dev/null +++ b/web/api/webmachine/src/webmachine_mochiweb.erl @@ -0,0 +1,89 @@ +%% @author Justin Sheehy +%% @author Andy Gross +%% @copyright 2007-2008 Basho Technologies +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. + +%% @doc Mochiweb interface for webmachine. +-module(webmachine_mochiweb). +-author('Justin Sheehy '). +-author('Andy Gross '). +-export([start/1, stop/0, loop/1]). + +start(Options) -> + {DispatchList, Options1} = get_option(dispatch, Options), + {ErrorHandler0, Options2} = get_option(error_handler, Options1), + {EnablePerfLog, Options3} = get_option(enable_perf_logger, Options2), + ErrorHandler = + case ErrorHandler0 of + undefined -> + webmachine_error_handler; + EH -> EH + end, + {LogDir, Options4} = get_option(log_dir, Options3), + webmachine_sup:start_logger(LogDir), + case EnablePerfLog of + true -> + application:set_env(webmachine, enable_perf_logger, true), + webmachine_sup:start_perf_logger(LogDir); + _ -> + ignore + end, + application:set_env(webmachine, dispatch_list, DispatchList), + application:set_env(webmachine, error_handler, ErrorHandler), + mochiweb_http:start([{name, ?MODULE}, {loop, fun loop/1} | Options4]). + +stop() -> + mochiweb_http:stop(?MODULE). + +loop(MochiReq) -> + Req = webmachine:new_request(mochiweb, MochiReq), + {ok, DispatchList} = application:get_env(webmachine, dispatch_list), + Host = case host_headers(Req) of + [H|_] -> H; + [] -> [] + end, + case webmachine_dispatcher:dispatch(Host, Req:path(), DispatchList) of + {no_dispatch_match, _UnmatchedHost, _UnmatchedPathTokens} -> + {ok, ErrorHandler} = application:get_env(webmachine, error_handler), + ErrorHTML = ErrorHandler:render_error(404, Req, {none, none, []}), + Req:append_to_response_body(ErrorHTML), + Req:send_response(404), + LogData = Req:log_data(), + LogModule = + case application:get_env(webmachine,webmachine_logger_module) of + {ok, Val} -> Val; + _ -> webmachine_logger + end, + spawn(LogModule, log_access, [LogData]), + Req:stop(); + {Mod, ModOpts, HostTokens, Port, PathTokens, Bindings, + AppRoot, StringPath} -> + BootstrapResource = webmachine_resource:new(x,x,x,x), + {ok, Resource} = BootstrapResource:wrap(Mod, ModOpts), + Req:load_dispatch_data(Bindings,HostTokens,Port,PathTokens, + AppRoot,StringPath,Req), + Req:set_metadata('resource_module', Mod), + webmachine_decision_core:handle_request(Req, Resource) + end. + +get_option(Option, Options) -> + {proplists:get_value(Option, Options), proplists:delete(Option, Options)}. + +host_headers(Req) -> + [ V || V <- [Req:get_header_value(H) + || H <- ["x-forwarded-for", + "x-forwarded-host", + "x-forwarded-server", + "host"]], + V /= undefined]. diff --git a/web/api/webmachine/src/webmachine_multipart.erl b/web/api/webmachine/src/webmachine_multipart.erl new file mode 100644 index 0000000..68e8848 --- /dev/null +++ b/web/api/webmachine/src/webmachine_multipart.erl @@ -0,0 +1,165 @@ +%% @author Justin Sheehy +%% @author Andy Gross +%% @copyright 2009 Basho Technologies + +%% @doc Utility for parsing multipart form bodies. + +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at + +%% http://www.apache.org/licenses/LICENSE-2.0 + +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. + +-module(webmachine_multipart). +-author('Justin Sheehy '). +-author('Andy Gross '). +-export([get_all_parts/2,stream_parts/2, find_boundary/1]). +-export([test_body/0,test_body2/0]). + +% @type incoming_req_body() = binary(). +% The request body, in "multipart/form-data" (rfc2388) form, + +% @type boundary() = string(). +% The multipart boundary, as taken from the containing message's content-type. + +% @type fpart() = {fpartname(), {[fparam()],[fheader()]}, fcontent()}. +% A single part of a multipart form. + +% @type fpartname() = string(). +% The name from the form field of a form part. + +% @type fparam() = {binary(), binary()}. +% A key-value parameter from the content-disposition header in a form part. + +% @type fheader() = {binary(), binary()}. +% A header name and value supplied within a form part. + +% @type fcontent() = binary(). +% The body content within a form part. + +% @doc Find the multipart boundary for a request. +% @spec find_boundary(wrq:wm_reqdata()) -> boundary() +find_boundary(ReqData) -> + ContentType = wrq:get_req_header("content-type", ReqData), + string:substr(ContentType, string:str(ContentType, "boundary=") + + length("boundary=")). + +% @doc Turn a multipart form into component parts. +% @spec get_all_parts(incoming_req_body(), boundary()) -> [fpart()] +get_all_parts(Body, Boundary) when is_binary(Body), is_list(Boundary) -> + StreamStruct = send_streamed_body(Body,1024), + getparts1(stream_parts(StreamStruct, "--" ++ Boundary), []). + +% @doc Similar to get_all_parts/2, but for streamed/chunked bodies. +% Takes as input the result of wrq:stream_req_body/2, and provides +% either the atom 'done_parts' when no more parts are available, or +% a tuple with the next part and a function. That function will +% have 0-arity and the same return type as stream_parts/2 itself. +% @spec stream_parts(wm_stream(), boundary()) -> +% 'done_parts' | {fpart(), function()} +stream_parts(StreamStruct, Boundary) -> + stream_form(StreamStruct, "--" ++ Boundary, []). + +stream_form(_, _, [<<"----\n">>|_]) -> done_parts; +stream_form(_, _, [<<"--\n">>|_]) -> done_parts; +stream_form({Hunk, Next}, Boundary, []) -> + stream_form(get_more_data(Next), Boundary, re:split(Hunk, Boundary,[])); +stream_form({Hunk, Next}, Boundary, [<<>>|DQ]) -> + stream_form({Hunk, Next}, Boundary, DQ); +stream_form({Hunk, Next}, Boundary, [H|[T1|T2]]) -> + {make_part(H), fun() -> + stream_form({Hunk, Next}, Boundary, [T1|T2]) end}; +stream_form({Hunk, really_done}, Boundary, DQ) -> + DQBin = iolist_to_binary(DQ), + FullHunk = <>, + stream_parts(re:split(FullHunk, Boundary,[])); +stream_form({Hunk, Next}, Boundary, [Single]) -> + FullHunk = <>, + stream_form(get_more_data(Next), Boundary, re:split(FullHunk, Boundary,[])). + +stream_parts([]) -> done_parts; +% browsers are fun, and terminate posts slightly differently from each other: +stream_parts([<<"----\n">>]) -> done_parts; +stream_parts([<<"--\n">>]) -> done_parts; +stream_parts([<<"----\r\n">>]) -> done_parts; +stream_parts([<<"--\r\n">>]) -> done_parts; +stream_parts([<<"--\r\n--\n">>]) -> done_parts; +stream_parts([<<"--\r\n--\r\n">>]) -> done_parts; +stream_parts([H|T]) -> {make_part(H), fun() -> stream_parts(T) end}. + +get_more_data(done) -> {<<"--\n">>, really_done}; +get_more_data(Fun) -> Fun(). + +make_part(PartData) -> + [HeadData, Body] = re:split(PartData, "\\r\\n\\r\\n", [{parts,2}]), + HeadList = [list_to_binary(X) || + X <- string:tokens(binary_to_list(HeadData), "\r\n")], + {Name, Params, Headers} = make_headers(HeadList), + {Name, {Params,Headers}, Body}. + +make_headers(X) -> + make_headers(X, name_undefined, params_undefined, []). +make_headers([], Name, Params, Headers) -> {Name, Params, Headers}; +make_headers([<<>>|HL], Name, Params, Headers) -> + make_headers(HL, Name, Params, Headers); +make_headers( + [<<"Content-Disposition: form-data; ", Names/binary>>|HL], + _, _, Headers) -> + {Name, Params} = extract_names(Names), + make_headers(HL, Name, Params, Headers); +make_headers([H|HL], Name, Params, Headers) -> + make_headers(HL, Name, Params, [cheap_parse_header(H)|Headers]). + +extract_names(NamesString) -> + Params = [{K, V} || + {K, [<<>>, V, <<>>]} <- [{K0, re:split(V0,"\"",[])} || + [K0, V0] <- [re:split(N, "=", [{parts, 2}]) || + N <- re:split(NamesString, "; ", [])]]], + Name = hd([binary_to_list(V) || {<<"name">>,V} <- Params]), + {Name, Params}. + +cheap_parse_header(HeadBin) -> + [K,V] = re:split(HeadBin, ": ", [{parts,2}]), + {K,V}. + +getparts1(done_parts, Acc) -> + lists:reverse(Acc); +getparts1({Part, Streamer}, Acc) -> + getparts1(Streamer(), [Part|Acc]). + +send_streamed_body(Body, Max) -> + HunkLen=8*Max, + case Body of + <> -> + {<>, fun() -> send_streamed_body(Rest,Max) end}; + _ -> + {Body, done} + end. + +test_body() -> + Body = <<"------------ae0gL6gL6Ij5KM7Ef1KM7ei4ae0cH2\r\nContent-Disposition: form-data; name=\"Filename\"\r\n\r\ntestfile.txt\r\n------------ae0gL6gL6Ij5KM7Ef1KM7ei4ae0cH2\r\nContent-Disposition: form-data; name=\"Filedata\"; filename=\"testfile.txt\"\r\nContent-Type: application/octet-stream\r\n\r\n%%% The contents of this file are a test,\n%%% do not be alarmed.\n\r\n------------ae0gL6gL6Ij5KM7Ef1KM7ei4ae0cH2\r\nContent-Disposition: form-data; name=\"Upload\"\r\n\r\nSubmit Query\r\n------------ae0gL6gL6Ij5KM7Ef1KM7ei4ae0cH2--">>, + Boundary = "----------ae0gL6gL6Ij5KM7Ef1KM7ei4ae0cH2", + [{"Filename", + {[{<<"name">>,<<"Filename">>}],[]}, + <<"testfile.txt\r\n">>}, + {"Filedata", + {[{<<"name">>,<<"Filedata">>}, + {<<"filename">>,<<"testfile.txt">>}], + [{<<"Content-Type">>,<<"application/octet-stream">>}]}, + <<"%%% The contents of this file are a test,\n%%% do not be alarmed.\n\r\n">>}, + {"Upload", + {[{<<"name">>,<<"Upload">>}],[]}, + <<"Submit Query\r\n">>}] + = get_all_parts(Body, Boundary), + ok. + +test_body2() -> + Body = <<"-----------------------------89205314411538515011004844897\r\nContent-Disposition: form-data; name=\"Filedata\"; filename=\"akamai.txt\"\r\nContent-Type: text/plain\r\n\r\nCAMBRIDGE, MA - February 18, 2009 - Akamai Technologies, Inc. (NASDAQ: AKAM), the leader in powering rich media, dynamic transactions and enterprise applications online, today announced that its Service & Support organization was awarded top honors for Innovation in Customer Service at the 3rd Annual Stevie Awards for Sales & Customer Service, an international competition recognizing excellence in disciplines that are crucial to business success.\n\n\"We have always set incredibly high standards with respect to the service and support we provide our customers,\" said Sanjay Singh, vice president of Global Service & Support at Akamai. \"Our support team provides highly responsive service around the clock to our global customer base and, as a result, has become an extension of our customers' online businesses. This prestigious award is validation of Akamai's commitment to customer service and technical support.\"\n\nAkamai Service & Support professionals are dedicated to working with customers on a daily basis to fine tune, optimize, and support their Internet initiatives. Akamai's winning submission highlighted the key pillars of its service and support offering, as well as the initiatives established to meet customer requirements for proactive communication, simplification, and faster response times.\n\n\"This year's honorees demonstrate that even in challenging economic times, it's possible for organizations to continue to shine in sales and customer service, the two most important functions in business: acquiring and keeping customers,\" said Michael Gallagher, president of the Stevie Awards.\n\nThe awards are presented by the Stevie Awards, which organizes several of the world's leading business awards shows, including the prestigious American Business Awards. Nicknamed the Stevies for the Greek word \"crowned,\" winners were announced during a gala banquet on Monday, February 9 at Caesars Palace in Las Vegas. Nominated customer service and sales executives from the U.S.A. and several other countries attended. More than 500 entries from companies of all sizes and in virtually every industry were submitted to this year's competition. There are 27 categories for customer service professionals, as well as 41 categories for sales professionals.\n\nDetails about the Stevie Awards for Sales & Customer Service and the list of honorees in all categories are available at www.stevieawards.com/sales. \n\r\n-----------------------------89205314411538515011004844897--\r\n">>, + Boundary = "---------------------------89205314411538515011004844897", + get_all_parts(Body,Boundary). diff --git a/web/api/webmachine/src/webmachine_perf_logger.erl b/web/api/webmachine/src/webmachine_perf_logger.erl new file mode 100644 index 0000000..ec31dd0 --- /dev/null +++ b/web/api/webmachine/src/webmachine_perf_logger.erl @@ -0,0 +1,210 @@ +%% @author Justin Sheehy +%% @author Andy Gross +%% @copyright 2007-2008 Basho Technologies +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. + +-module(webmachine_perf_logger). +-author('Justin Sheehy '). +-author('Andy Gross '). +-behaviour(gen_server). +-export([start_link/1]). +-export([init/1, handle_call/3, handle_cast/2, handle_info/2, + terminate/2, code_change/3]). +-export([log/1, refresh/0]). +-include("webmachine_logger.hrl"). +-record(state, {hourstamp, filename, handle}). + +alog_path(BaseDir) -> + filename:join(BaseDir, "perf.log"). + +start_link(BaseDir) -> + gen_server:start_link({local, ?MODULE}, ?MODULE, [BaseDir], []). + +init([BaseDir]) -> + defer_refresh(), + FileName = alog_path(BaseDir), + DateHour = datehour(), + filelib:ensure_dir(FileName), + Handle = log_open(FileName, DateHour), + {ok, #state{filename=FileName, handle=Handle, hourstamp=DateHour}}. + +refresh() -> + refresh(now()). + +refresh(Time) -> + gen_server:cast(?MODULE, {refresh, Time}). + +log(#wm_log_data{}=D) -> + gen_server:call(?MODULE, {log, D}). + +handle_call({log, LogData}, _From, State) -> + NewState = maybe_rotate(State, now()), + Msg = format_req(LogData), + log_write(NewState#state.handle, Msg), + {reply, ok, NewState}. + +handle_cast({refresh, Time}, State) -> + {noreply, maybe_rotate(State, Time)}. + +handle_info(_Info, State) -> + {noreply, State}. + +terminate(_Reason, _State) -> + ok. + +code_change(_OldVsn, State, _Extra) -> + {ok, State}. + +log_open(FileName, DateHour) -> + LogName = FileName ++ suffix(DateHour), + io:format("opening log file: ~p~n", [LogName]), + {ok, FD} = file:open(LogName, [read, write, raw]), + {ok, Location} = file:position(FD, eof), + fix_log(FD, Location), + file:truncate(FD), + {?MODULE, LogName, FD}. + +log_write({?MODULE, _Name, FD}, IoData) -> + file:write(FD, lists:flatten(IoData)). + + +log_close({?MODULE, Name, FD}) -> + io:format("~p: closing log file: ~p~n", [?MODULE, Name]), + file:close(FD). + +maybe_rotate(State, Time) -> + ThisHour = datehour(Time), + if ThisHour == State#state.hourstamp -> + State; + true -> + defer_refresh(), + log_close(State#state.handle), + Handle = log_open(State#state.filename, ThisHour), + State#state{hourstamp=ThisHour, handle=Handle} + end. + +format_req(#wm_log_data{resource_module=Mod, + start_time=StartTime, + method=Method, + peer=Peer, + path=Path, + version=Version, + response_code=ResponseCode, + response_length=ResponseLength, + end_time=EndTime, + finish_time=FinishTime}) -> + Time = fmtnow(), + Status = integer_to_list(ResponseCode), + Length = integer_to_list(ResponseLength), + TTPD = webmachine_util:now_diff_milliseconds(EndTime, StartTime), + TTPS = webmachine_util:now_diff_milliseconds(FinishTime, EndTime), + fmt_plog(Time, Peer, atom_to_list(Method), Path, Version, + Status, Length, atom_to_list(Mod), integer_to_list(TTPD), + integer_to_list(TTPS)). + +%% Seek backwards to the last valid log entry +fix_log(_FD, 0) -> + ok; +fix_log(FD, 1) -> + {ok, 0} = file:position(FD, 0), + ok; +fix_log(FD, Location) -> + case file:pread(FD, Location - 1, 1) of + {ok, [$\n | _]} -> + ok; + {ok, _} -> + fix_log(FD, Location - 1) + end. + +defer_refresh() -> + {_, {_, M, S}} = calendar:universal_time(), + Time = 1000 * (3600 - ((M * 60) + S)), + timer:apply_after(Time, ?MODULE, refresh, []). + +datehour() -> + datehour(now()). + +datehour(Now) -> + {{Y, M, D}, {H, _, _}} = calendar:now_to_universal_time(Now), + {Y, M, D, H}. + +zeropad_str(NumStr, Zeros) when Zeros > 0 -> + zeropad_str([$0 | NumStr], Zeros - 1); +zeropad_str(NumStr, _) -> + NumStr. + +zeropad(Num, MinLength) -> + NumStr = integer_to_list(Num), + zeropad_str(NumStr, MinLength - length(NumStr)). + +suffix({Y, M, D, H}) -> + YS = zeropad(Y, 4), + MS = zeropad(M, 2), + DS = zeropad(D, 2), + HS = zeropad(H, 2), + lists:flatten([$., YS, $_, MS, $_, DS, $_, HS]). + +fmt_plog(Time, Ip, Method, Path, {VM,Vm}, Status, Length, Mod, TTPD, TTPS) -> + [fmt_ip(Ip), " - ", [$\s], Time, [$\s, $"], Method, " ", Path, + " HTTP/", integer_to_list(VM), ".", integer_to_list(Vm), [$",$\s], + Status, [$\s], Length, " " , Mod, " ", TTPD, " ", TTPS, $\n]. + +month(1) -> + "Jan"; +month(2) -> + "Feb"; +month(3) -> + "Mar"; +month(4) -> + "Apr"; +month(5) -> + "May"; +month(6) -> + "Jun"; +month(7) -> + "Jul"; +month(8) -> + "Aug"; +month(9) -> + "Sep"; +month(10) -> + "Oct"; +month(11) -> + "Nov"; +month(12) -> + "Dec". +zone() -> + Time = erlang:universaltime(), + LocalTime = calendar:universal_time_to_local_time(Time), + DiffSecs = calendar:datetime_to_gregorian_seconds(LocalTime) - calendar:datetime_to_gregorian_seconds(Time), + zone((DiffSecs/3600)*100). + +%% Ugly reformatting code to get times like +0000 and -1300 + +zone(Val) when Val < 0 -> + io_lib:format("-~4..0w", [trunc(abs(Val))]); +zone(Val) when Val >= 0 -> + io_lib:format("+~4..0w", [trunc(abs(Val))]). + +fmt_ip(IP) when is_tuple(IP) -> + inet_parse:ntoa(IP); +fmt_ip(undefined) -> + "0.0.0.0"; +fmt_ip(HostName) -> + HostName. + +fmtnow() -> + {{Year, Month, Date}, {Hour, Min, Sec}} = calendar:local_time(), + io_lib:format("[~2..0w/~s/~4..0w:~2..0w:~2..0w:~2..0w ~s]", + [Date,month(Month),Year, Hour, Min, Sec, zone()]). diff --git a/web/api/webmachine/src/webmachine_request.erl b/web/api/webmachine/src/webmachine_request.erl new file mode 100644 index 0000000..ec97875 --- /dev/null +++ b/web/api/webmachine/src/webmachine_request.erl @@ -0,0 +1,180 @@ +%% @author Justin Sheehy +%% @author Andy Gross +%% @copyright 2007-2009 Basho Technologies +%% Based on mochiweb_request.erl, which is Copyright 2007 Mochi Media, Inc. +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. + +%% @doc Webmachine HTTP Request Abstraction. + +-module(webmachine_request, [Pid]). +-author('Justin Sheehy '). +-author('Andy Gross '). + +-export([ + get_reqdata/0, + set_reqdata/1, + socket/0, + method/0, + version/0, + disp_path/0, + path/0, + raw_path/0, + req_headers/0, + req_body/1, + stream_req_body/1, + headers/0, + resp_headers/0, + out_headers/0, + get_out_header/1, + has_out_header/1, + peer/0, + get_header_value/1, + add_response_header/2, + add_response_headers/1, + remove_response_header/1, + merge_response_headers/1, + append_to_response_body/1, + send_response/1, + response_code/0, + set_response_code/1, + set_resp_body/1, + response_body/0, + has_response_body/0, + stop/0, + do_redirect/0, + resp_redirect/0, + set_metadata/2, + get_metadata/1, + get_path_info/0, + get_path_info/1, + load_dispatch_data/7, + get_path_tokens/0, + get_app_root/0, + parse_cookie/0, + get_cookie_value/1, + parse_qs/0, + get_qs_value/1, + get_qs_value/2, + range/0, + log_data/0, + call/1 + ]). + +-define(TIMEOUT, 150000). + +call(Message) -> gen_server:call(Pid, Message, ?TIMEOUT). + +get_reqdata() -> call(get_reqdata). + +set_reqdata(RD) -> call({set_reqdata, RD}). + +socket() -> call(socket). + +method() -> call(method). + +version() -> call(version). + +disp_path() -> call(disp_path). + +path() -> call(path). + +raw_path() -> call(raw_path). + +req_headers() -> call(req_headers). +headers() -> req_headers(). + +req_body(MaxRevBody) -> call({req_body,MaxRevBody}). +stream_req_body(MaxHunk) -> call({stream_req_body, MaxHunk}). + +resp_headers() -> call(resp_headers). +out_headers() -> resp_headers(). + +get_resp_header(HeaderName) -> call({get_resp_header, HeaderName}). +get_out_header(HeaderName) -> get_resp_header(HeaderName). + +has_resp_header(HeaderName) -> + case get_out_header(HeaderName) of + undefined -> false; + _ -> true + end. +has_out_header(HeaderName) -> has_resp_header(HeaderName). + +has_resp_body() -> call(has_resp_body). +has_response_body() -> has_resp_body(). + +response_code() -> call(response_code). +set_response_code(Code) -> call({set_response_code, Code}). + +peer() -> call(peer). + +range() -> call(range). + +req_cookie() -> call(req_cookie). +parse_cookie() -> req_cookie(). +get_cookie_value(Key) -> proplists:get_value(Key, req_cookie()). + +req_qs() -> call(req_qs). +parse_qs() -> req_qs(). +get_qs_value(Key) -> proplists:get_value(Key, req_qs()). +get_qs_value(Key, Default) -> proplists:get_value(Key, req_qs(), Default). + +stop() -> gen_server:cast(Pid, stop). + +set_resp_body(Body) -> call({set_resp_body, Body}). +resp_body() -> call(resp_body). +response_body() -> resp_body(). + +get_req_header(K) -> call({get_req_header, K}). +get_header_value(K) -> get_req_header(K). + +set_resp_header(K, V) -> call({set_resp_header, K, V}). +add_response_header(K, V) -> set_resp_header(K, V). + +set_resp_headers(Hdrs) -> call({set_resp_headers, Hdrs}). +add_response_headers(Hdrs) -> set_resp_headers(Hdrs). + +remove_resp_header(K) -> call({remove_resp_header, K}). +remove_response_header(K) -> remove_resp_header(K). + +merge_resp_headers(Hdrs) -> call({merge_resp_headers, Hdrs}). +merge_response_headers(Hdrs) -> merge_resp_headers(Hdrs). + +append_to_response_body(Data) -> call({append_to_response_body, Data}). + +do_redirect() -> call({do_redirect}). + +resp_redirect() -> call({resp_redirect}). + +send_response(Code) -> call({send_response, Code}). + +get_metadata(Key) -> call({get_metadata, Key}). + +set_metadata(Key, Value) -> call({set_metadata, Key, Value}). + +get_path_info() -> call(get_path_info). + +get_path_info(Key) -> call({get_path_info, Key}). + +path_tokens() -> call(path_tokens). +get_path_tokens() -> path_tokens(). + +app_root() -> call(app_root). +get_app_root() -> app_root(). + +load_dispatch_data(Bindings, HostTokens, Port, PathTokens, + AppRoot, DispPath, Req) -> + call({load_dispatch_data, Bindings, HostTokens, Port, + PathTokens, AppRoot, DispPath, Req}). + +log_data() -> call(log_data). diff --git a/web/api/webmachine/src/webmachine_request_srv.erl b/web/api/webmachine/src/webmachine_request_srv.erl new file mode 100644 index 0000000..2f4c6dd --- /dev/null +++ b/web/api/webmachine/src/webmachine_request_srv.erl @@ -0,0 +1,615 @@ +%% @author Andy Gross +%% @author Justin Sheehy +%% @copyright 2007-2009 Basho Technologies +%% Portions derived from code Copyright 2007-2008 Bob Ippolito, Mochi Media +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. + +-module(webmachine_request_srv). +-author('Justin Sheehy '). +-author('Andy Gross '). +-behaviour(gen_server). +-export([start_link/5]). +-export([init/1, handle_call/3, handle_cast/2, handle_info/2, + terminate/2, code_change/3]). +-include("webmachine_logger.hrl"). +-include_lib("include/wm_reqdata.hrl"). + +-define(WMVSN, "1.5"). +-define(QUIP, "hack the charles gibson"). + +% 120 second default idle timeout +-define(IDLE_TIMEOUT, infinity). +-record(state, {socket=undefined, + metadata=dict:new(), + range=undefined, + peer=undefined, + reqdata=undefined, + bodyfetch=undefined, + log_data=#wm_log_data{} + }). + +start_link(Socket, Method, RawPath, Version, Headers) -> + gen_server:start_link(?MODULE, + [Socket, Method, RawPath, Version, Headers], []). + +init([Socket, Method, RawPath, Version, Headers]) -> + %%process_flag(trap_exit, true), + %% Calling get_peer() here is a little bit of an ugly way to populate the + %% client IP address but it will do for now. + {Peer, State} = get_peer(#state{socket=Socket, + reqdata=wrq:create(Method,Version,RawPath,Headers)}), + PeerState = State#state{reqdata=wrq:set_peer(Peer,State#state.reqdata)}, + LogData = #wm_log_data{start_time=now(), + method=Method, + headers=Headers, + peer=State#state.peer, + path=RawPath, + version=Version, + response_code=404, + response_length=0}, + {ok, PeerState#state{log_data=LogData}}. + +handle_call(socket, _From, State) -> + Reply = State#state.socket, + {reply, Reply, State}; +handle_call(get_reqdata, _From, State) -> + {reply, State#state.reqdata, State}; +handle_call({set_reqdata, RD=#wm_reqdata{req_body=RBody}}, _From, State) -> + TheRD = case RBody of + not_fetched_yet -> + OldRD = State#state.reqdata, + OldBody = OldRD#wm_reqdata.req_body, + RD#wm_reqdata{req_body=OldBody}; + _ -> + RD + end, + {reply, ok, State#state{reqdata=TheRD}}; +handle_call(method, _From, State) -> + {reply, wrq:method(State#state.reqdata), State}; +handle_call(version, _From, State) -> + {reply, wrq:version(State#state.reqdata), State}; +handle_call(raw_path, _From, State) -> + {reply, wrq:raw_path(State#state.reqdata), State}; +handle_call(req_headers, _From, State) -> + {reply, wrq:req_headers(State#state.reqdata), State}; +handle_call(req_body, _From, State=#state{bodyfetch=stream}) -> + {reply, stream_conflict, State}; +handle_call({req_body, MaxRecvBody}, _From, State0=#state{reqdata=RD0}) -> + RD=RD0#wm_reqdata{max_recv_body=MaxRecvBody}, + State=State0#state{reqdata=RD}, + {Body, FinalState} = case RD#wm_reqdata.req_body of + not_fetched_yet -> + NewBody = do_recv_body(State), + NewRD = RD#wm_reqdata{req_body=NewBody}, + {NewBody, State#state{bodyfetch=standard,reqdata=NewRD}}; + X -> + {X, State#state{bodyfetch=standard}} + end, + {reply, Body, FinalState}; +handle_call({stream_req_body,_}, _From, State=#state{bodyfetch=standard}) -> + {reply, stream_conflict, State}; +handle_call({stream_req_body, MaxHunk}, _From, State) -> + {reply, recv_stream_body(State, MaxHunk), State#state{bodyfetch=stream}}; +handle_call(resp_headers, _From, State) -> + {reply, wrq:resp_headers(State#state.reqdata), State}; +handle_call(resp_redirect, _From, State) -> + {reply, wrq:resp_redirect(State#state.reqdata), State}; +handle_call({get_resp_header, HdrName}, _From, State) -> + Reply = mochiweb_headers:get_value(HdrName, + wrq:resp_headers(State#state.reqdata)), + {reply, Reply, State}; +handle_call(get_path_info, _From, State) -> + PropList = dict:to_list(wrq:path_info(State#state.reqdata)), + {reply, PropList, State}; +handle_call({get_path_info, Key}, _From, State) -> + {reply, wrq:path_info(Key, State#state.reqdata), State}; +handle_call(peer, _From, State) -> + {Reply, NewState} = get_peer(State), + {reply, Reply, NewState}; +handle_call(range, _From, State) -> + {Reply, NewState} = get_range(State), + {reply, Reply, NewState}; +handle_call(response_code, _From, State) -> + {reply, wrq:response_code(State#state.reqdata), State}; +handle_call(app_root, _From, State) -> + {reply, wrq:app_root(State#state.reqdata), State}; +handle_call(disp_path, _From, State) -> + {reply, wrq:disp_path(State#state.reqdata), State}; +handle_call(path, _From, State) -> + {reply, wrq:path(State#state.reqdata), State}; +handle_call({get_req_header, K}, _From, State) -> + {reply, wrq:get_req_header(K, State#state.reqdata), State}; +handle_call({set_response_code, Code}, _From, State) -> + NewState = State#state{reqdata=wrq:set_response_code( + Code, State#state.reqdata)}, + {reply, ok, NewState}; +handle_call({set_resp_header, K, V}, _From, State) -> + NewState = State#state{reqdata=wrq:set_resp_header( + K, V, State#state.reqdata)}, + {reply, ok, NewState}; +handle_call({set_resp_headers, Hdrs}, _From, State) -> + NewState = State#state{reqdata=wrq:set_resp_headers( + Hdrs, State#state.reqdata)}, + {reply, ok, NewState}; +handle_call({remove_resp_header, K}, _From, State) -> + NewState = State#state{reqdata=wrq:remove_resp_header( + K, State#state.reqdata)}, + {reply, ok, NewState}; +handle_call({merge_resp_headers, Hdrs}, _From, State) -> + NewState = State#state{reqdata=wrq:merge_resp_headers( + Hdrs, State#state.reqdata)}, + {reply, ok, NewState}; +handle_call({append_to_response_body, Data}, _From, State) -> + NewState = State#state{reqdata=wrq:append_to_response_body( + Data, State#state.reqdata)}, + {reply, ok, NewState}; +handle_call({set_disp_path, P}, _From, State) -> + NewState = State#state{reqdata=wrq:set_disp_path( + P, State#state.reqdata)}, + {reply, ok, NewState}; +handle_call(do_redirect, _From, State) -> + NewState = State#state{reqdata=wrq:do_redirect(true, + State#state.reqdata)}, + {reply, ok, NewState}; +handle_call({send_response, Code}, _From, State) -> + {Reply, NewState} = + case Code of + 200 -> + send_ok_response(Code, State); + _ -> + send_response(Code, State) + end, + LogData = NewState#state.log_data, + NewLogData = LogData#wm_log_data{finish_time=now()}, + {reply, Reply, NewState#state{log_data=NewLogData}}; +handle_call(resp_body, _From, State) -> + {reply, wrq:resp_body(State#state.reqdata), State}; +handle_call({set_resp_body, Body}, _From, State) -> + NewState = State#state{reqdata=wrq:set_resp_body(Body, + State#state.reqdata)}, + {reply, ok, NewState}; +handle_call(has_resp_body, _From, State) -> + Reply = case wrq:resp_body(State#state.reqdata) of + undefined -> false; + <<>> -> false; + [] -> false; + _ -> true + end, + {reply, Reply, State}; +handle_call({get_metadata, Key}, _From, State) -> + Reply = case dict:find(Key, State#state.metadata) of + {ok, Value} -> Value; + error -> undefined + end, + {reply, Reply, State}; +handle_call({set_metadata, Key, Value}, _From, State) -> + NewDict = dict:store(Key, Value, State#state.metadata), + {reply, ok, State#state{metadata=NewDict}}; +handle_call(path_tokens, _From, State) -> + {reply, wrq:path_tokens(State#state.reqdata), State}; +handle_call(req_cookie, _From, State) -> + {reply, wrq:req_cookie(State#state.reqdata), State}; +handle_call(req_qs, _From, State) -> + {reply, wrq:req_qs(State#state.reqdata), State}; +handle_call({load_dispatch_data, PathProps,HostTokens,Port, + PathTokens,AppRoot,DispPath,WMReq}, + _From, State) -> + PathInfo = dict:from_list(PathProps), + NewState = State#state{reqdata=wrq:load_dispatch_data( + PathInfo,HostTokens,Port,PathTokens,AppRoot, + DispPath,WMReq,State#state.reqdata)}, + {reply, ok, NewState}; +handle_call(log_data, _From, State) -> {reply, State#state.log_data, State}. + +handle_cast(stop, State) -> {stop, normal, State}. + +handle_info(_Info, State) -> {noreply, State}. + +terminate(_Reason, _State) -> ok. + +code_change(_OldVsn, State, _Extra) -> {ok, State}. + +get_peer(State) -> + case State#state.peer of + undefined -> + Socket = State#state.socket, + Peer = case inet:peername(Socket) of + {ok, {Addr={10, _, _, _}, _Port}} -> + case get_header_value("x-forwarded-for", State) of + {undefined, _} -> + inet_parse:ntoa(Addr); + {Hosts, _} -> + string:strip(lists:last(string:tokens(Hosts, ","))) + end; + {ok, {{127, 0, 0, 1}, _Port}} -> + case get_header_value("x-forwarded-for", State) of + {undefined, _} -> + "127.0.0.1"; + {Hosts, _} -> + string:strip(lists:last(string:tokens(Hosts, ","))) + end; + {ok, {Addr, _Port}} -> + inet_parse:ntoa(Addr) + end, + NewState = State#state{peer=Peer}, + {Peer, NewState}; + _ -> + {State#state.peer, State} + end. + +get_header_value(K, State) -> + {wrq:get_req_header(K, State#state.reqdata), State}. + +get_outheader_value(K, State) -> + {mochiweb_headers:get_value(K, + wrq:resp_headers(State#state.reqdata)), State}. + +send(Socket, Data) -> + case gen_tcp:send(Socket, iolist_to_binary(Data)) of + ok -> ok; + {error,closed} -> ok; + _ -> exit(normal) + end. + +send_stream_body(Socket, X) -> send_stream_body(Socket, X, 0). +send_stream_body(Socket, {<<>>, done}, SoFar) -> + send_chunk(Socket, <<>>), + SoFar; +send_stream_body(Socket, {Data, done}, SoFar) -> + Size = send_chunk(Socket, Data), + send_chunk(Socket, <<>>), + Size + SoFar; +send_stream_body(Socket, {<<>>, Next}, SoFar) -> + send_stream_body(Socket, Next(), SoFar); +send_stream_body(Socket, {[], Next}, SoFar) -> + send_stream_body(Socket, Next(), SoFar); +send_stream_body(Socket, {Data, Next}, SoFar) -> + Size = send_chunk(Socket, Data), + send_stream_body(Socket, Next(), Size + SoFar). + +send_chunk(Socket, Data) -> + Size = iolist_size(Data), + send(Socket, mochihex:to_hex(Size)), + send(Socket, <<"\r\n">>), + send(Socket, Data), + send(Socket, <<"\r\n">>), + Size. + +send_ok_response(200, InitState) -> + RD0 = InitState#state.reqdata, + {Range, State} = get_range(InitState), + case Range of + X when X =:= undefined; X =:= fail -> + send_response(200, State); + Ranges -> + {PartList, Size} = range_parts(RD0, Ranges), + case PartList of + [] -> %% no valid ranges + %% could be 416, for now we'll just return 200 + send_response(200, State); + PartList -> + {RangeHeaders, RangeBody} = + parts_to_body(PartList, State, Size), + RespHdrsRD = wrq:set_resp_headers( + [{"Accept-Ranges", "bytes"} | RangeHeaders], RD0), + RespBodyRD = wrq:set_resp_body( + RangeBody, RespHdrsRD), + NewState = State#state{reqdata=RespBodyRD}, + send_response(206, NewState) + end + end. + +send_response(Code, State=#state{reqdata=RD}) -> + Body0 = wrq:resp_body(RD), + {Body,Length} = case Body0 of + {stream, StreamBody} -> {StreamBody, chunked}; + _ -> {Body0, iolist_size([Body0])} + end, + send(State#state.socket, + [make_version(wrq:version(RD)), + make_code(Code), <<"\r\n">> | + make_headers(Code, Length, RD)]), + FinalLength = case wrq:method(RD) of + 'HEAD' -> Length; + _ -> + case Length of + chunked -> send_stream_body(State#state.socket, Body); + _ -> send(State#state.socket, Body), Length + end + end, + InitLogData = State#state.log_data, + FinalLogData = InitLogData#wm_log_data{response_code=Code, + response_length=FinalLength}, + {ok, State#state{reqdata=wrq:set_response_code(Code, RD), + log_data=FinalLogData}}. + +%% @spec body_length(state()) -> undefined | chunked | unknown_transfer_encoding | integer() +%% @doc Infer body length from transfer-encoding and content-length headers. +body_length(State) -> + case get_header_value("transfer-encoding", State) of + {undefined, _} -> + case get_header_value("content-length", State) of + {undefined, _} -> undefined; + {Length, _} -> list_to_integer(Length) + end; + {"chunked", _} -> chunked; + Unknown -> {unknown_transfer_encoding, Unknown} + end. + +%% @spec do_recv_body(state()) -> binary() +%% @doc Receive the body of the HTTP request (defined by Content-Length). +%% Will only receive up to the default max-body length +do_recv_body(State=#state{reqdata=RD}) -> + MRB = RD#wm_reqdata.max_recv_body, + read_whole_stream(recv_stream_body(State, MRB), [], MRB, 0). + +read_whole_stream({Hunk,_}, _, MaxRecvBody, SizeAcc) + when SizeAcc + byte_size(Hunk) > MaxRecvBody -> + {error, req_body_too_large}; +read_whole_stream({Hunk,Next}, Acc0, MaxRecvBody, SizeAcc) -> + HunkSize = byte_size(Hunk), + if SizeAcc + HunkSize > MaxRecvBody -> + {error, req_body_too_large}; + true -> + Acc = [Hunk|Acc0], + case Next of + done -> iolist_to_binary(lists:reverse(Acc)); + _ -> read_whole_stream(Next(), Acc, + MaxRecvBody, SizeAcc + HunkSize) + end + end. + +recv_stream_body(State = #state{reqdata=RD}, MaxHunkSize) -> + case get_header_value("expect", State) of + {"100-continue", _} -> + send(State#state.socket, + [make_version(wrq:version(RD)), + make_code(100), <<"\r\n">>]); + _Else -> + ok + end, + case body_length(State) of + {unknown_transfer_encoding, X} -> exit({unknown_transfer_encoding, X}); + undefined -> {<<>>, done}; + 0 -> {<<>>, done}; + chunked -> recv_chunked_body(State#state.socket, MaxHunkSize); + Length -> recv_unchunked_body(State#state.socket, MaxHunkSize, Length) + end. + +recv_unchunked_body(Socket, MaxHunk, DataLeft) -> + case MaxHunk >= DataLeft of + true -> + {ok,Data1} = gen_tcp:recv(Socket,DataLeft,?IDLE_TIMEOUT), + {Data1, done}; + false -> + {ok,Data2} = gen_tcp:recv(Socket,MaxHunk,?IDLE_TIMEOUT), + {Data2, + fun() -> recv_unchunked_body( + Socket, MaxHunk, DataLeft-MaxHunk) + end} + end. + +recv_chunked_body(Socket, MaxHunk) -> + case read_chunk_length(Socket) of + 0 -> {<<>>, done}; + ChunkLength -> recv_chunked_body(Socket,MaxHunk,ChunkLength) + end. +recv_chunked_body(Socket, MaxHunk, LeftInChunk) -> + case MaxHunk >= LeftInChunk of + true -> + {ok,Data1} = gen_tcp:recv(Socket,LeftInChunk,?IDLE_TIMEOUT), + {Data1, + fun() -> recv_chunked_body(Socket, MaxHunk) + end}; + false -> + {ok,Data2} = gen_tcp:recv(Socket,MaxHunk,?IDLE_TIMEOUT), + {Data2, + fun() -> recv_chunked_body(Socket, MaxHunk, LeftInChunk-MaxHunk) + end} + end. + +read_chunk_length(Socket) -> + inet:setopts(Socket, [{packet, line}]), + case gen_tcp:recv(Socket, 0, ?IDLE_TIMEOUT) of + {ok, Header} -> + inet:setopts(Socket, [{packet, raw}]), + Splitter = fun (C) -> + C =/= $\r andalso C =/= $\n andalso C =/= $ + end, + {Hex, _Rest} = lists:splitwith(Splitter, binary_to_list(Header)), + case Hex of + [] -> 0; + _ -> erlang:list_to_integer(Hex, 16) + end; + _ -> + exit(normal) + end. + +get_range(State) -> + case get_header_value("range", State) of + {undefined, _} -> + {undefined, State#state{range=undefined}}; + {RawRange, _} -> + Range = parse_range_request(RawRange), + {Range, State#state{range=Range}} + end. + +range_parts(_RD=#wm_reqdata{resp_body={file, IoDevice}}, Ranges) -> + Size = iodevice_size(IoDevice), + F = fun (Spec, Acc) -> + case range_skip_length(Spec, Size) of + invalid_range -> + Acc; + V -> + [V | Acc] + end + end, + LocNums = lists:foldr(F, [], Ranges), + {ok, Data} = file:pread(IoDevice, LocNums), + Bodies = lists:zipwith(fun ({Skip, Length}, PartialBody) -> + {Skip, Skip + Length - 1, PartialBody} + end, + LocNums, Data), + {Bodies, Size}; + +range_parts(RD=#wm_reqdata{resp_body={stream, {Hunk,Next}}}, Ranges) -> + % for now, streamed bodies are read in full for range requests + MRB = RD#wm_reqdata.max_recv_body, + range_parts(read_whole_stream({Hunk,Next}, [], MRB, 0), Ranges); + +range_parts(_RD=#wm_reqdata{resp_body=Body0}, Ranges) -> + Body = iolist_to_binary(Body0), + Size = size(Body), + F = fun(Spec, Acc) -> + case range_skip_length(Spec, Size) of + invalid_range -> + Acc; + {Skip, Length} -> + <<_:Skip/binary, PartialBody:Length/binary, _/binary>> = Body, + [{Skip, Skip + Length - 1, PartialBody} | Acc] + end + end, + {lists:foldr(F, [], Ranges), Size}. + +range_skip_length(Spec, Size) -> + case Spec of + {none, R} when R =< Size, R >= 0 -> + {Size - R, R}; + {none, _OutOfRange} -> + {0, Size}; + {R, none} when R >= 0, R < Size -> + {R, Size - R}; + {_OutOfRange, none} -> + invalid_range; + {Start, End} when 0 =< Start, Start =< End, End < Size -> + {Start, End - Start + 1}; + {_OutOfRange, _End} -> + invalid_range + end. + +parse_range_request(RawRange) when is_list(RawRange) -> + try + "bytes=" ++ RangeString = RawRange, + Ranges = string:tokens(RangeString, ","), + lists:map(fun ("-" ++ V) -> + {none, list_to_integer(V)}; + (R) -> + case string:tokens(R, "-") of + [S1, S2] -> + {list_to_integer(S1), list_to_integer(S2)}; + [S] -> + {list_to_integer(S), none} + end + end, + Ranges) + catch + _:_ -> + fail + end. + +parts_to_body([{Start, End, Body}], State, Size) -> + %% return body for a range reponse with a single body + ContentType = + case get_outheader_value("content-type", State) of + {undefined, _} -> + "text/html"; + {CT, _} -> + CT + end, + HeaderList = [{"Content-Type", ContentType}, + {"Content-Range", + ["bytes ", + make_io(Start), "-", make_io(End), + "/", make_io(Size)]}], + {HeaderList, Body}; +parts_to_body(BodyList, State, Size) when is_list(BodyList) -> + %% return + %% header Content-Type: multipart/byteranges; boundary=441934886133bdee4 + %% and multipart body + ContentType = + case get_outheader_value("content-type", State) of + {undefined, _} -> + "text/html"; + {CT, _} -> + CT + end, + Boundary = mochihex:to_hex(crypto:rand_bytes(8)), + HeaderList = [{"Content-Type", + ["multipart/byteranges; ", + "boundary=", Boundary]}], + MultiPartBody = multipart_body(BodyList, ContentType, Boundary, Size), + {HeaderList, MultiPartBody}. + +multipart_body([], _ContentType, Boundary, _Size) -> + ["--", Boundary, "--\r\n"]; +multipart_body([{Start, End, Body} | BodyList], ContentType, Boundary, Size) -> + ["--", Boundary, "\r\n", + "Content-Type: ", ContentType, "\r\n", + "Content-Range: ", + "bytes ", make_io(Start), "-", make_io(End), + "/", make_io(Size), "\r\n\r\n", + Body, "\r\n" + | multipart_body(BodyList, ContentType, Boundary, Size)]. + +iodevice_size(IoDevice) -> + {ok, Size} = file:position(IoDevice, eof), + {ok, 0} = file:position(IoDevice, bof), + Size. + +make_io(Atom) when is_atom(Atom) -> + atom_to_list(Atom); +make_io(Integer) when is_integer(Integer) -> + integer_to_list(Integer); +make_io(Io) when is_list(Io); is_binary(Io) -> + Io. + +make_code(X) when is_integer(X) -> + [integer_to_list(X), [" " | httpd_util:reason_phrase(X)]]; +make_code(Io) when is_list(Io); is_binary(Io) -> + Io. + +make_version({1, 0}) -> + <<"HTTP/1.0 ">>; +make_version(_) -> + <<"HTTP/1.1 ">>. + +make_headers(Code, Length, RD) -> + Hdrs0 = case Code of + 304 -> + mochiweb_headers:make(wrq:resp_headers(RD)); + _ -> + case Length of + chunked -> + mochiweb_headers:enter( + "Transfer-Encoding","chunked", + mochiweb_headers:make(wrq:resp_headers(RD))); + _ -> + mochiweb_headers:enter( + "Content-Length",integer_to_list(Length), + mochiweb_headers:make(wrq:resp_headers(RD))) + end + end, + ServerHeader = "MochiWeb/1.1 WebMachine/" ++ ?WMVSN ++ " (" ++ ?QUIP ++ ")", + WithSrv = mochiweb_headers:enter("Server", ServerHeader, Hdrs0), + Hdrs = case mochiweb_headers:get_value("date", WithSrv) of + undefined -> + mochiweb_headers:enter("Date", httpd_util:rfc1123_date(), WithSrv); + _ -> + WithSrv + end, + F = fun({K, V}, Acc) -> + [make_io(K), <<": ">>, V, <<"\r\n">> | Acc] + end, + lists:foldl(F, [<<"\r\n">>], mochiweb_headers:to_list(Hdrs)). + diff --git a/web/api/webmachine/src/webmachine_resource.erl b/web/api/webmachine/src/webmachine_resource.erl new file mode 100644 index 0000000..2700e8a --- /dev/null +++ b/web/api/webmachine/src/webmachine_resource.erl @@ -0,0 +1,218 @@ +%% @author Justin Sheehy +%% @author Andy Gross +%% @copyright 2007-2009 Basho Technologies +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. + +-module(webmachine_resource, [R_Mod, R_ModState, R_ModExports, R_Trace]). +-author('Justin Sheehy '). +-author('Andy Gross '). +-export([wrap/2]). +-export([do/2,log_d/1,stop/0]). + +default(ping) -> + no_default; +default(service_available) -> + true; +default(resource_exists) -> + true; +default(auth_required) -> + true; +default(is_authorized) -> + true; +default(forbidden) -> + false; +default(allow_missing_post) -> + false; +default(malformed_request) -> + false; +default(uri_too_long) -> + false; +default(known_content_type) -> + true; +default(valid_content_headers) -> + true; +default(valid_entity_length) -> + true; +default(options) -> + []; +default(allowed_methods) -> + ['GET', 'HEAD']; +default(known_methods) -> + ['GET', 'HEAD', 'POST', 'PUT', 'DELETE', 'TRACE', 'CONNECT', 'OPTIONS']; +default(content_types_provided) -> + [{"text/html", to_html}]; +default(content_types_accepted) -> + []; +default(delete_resource) -> + false; +default(delete_completed) -> + true; +default(post_is_create) -> + false; +default(create_path) -> + undefined; +default(process_post) -> + false; +default(language_available) -> + true; +default(charsets_provided) -> + no_charset; % this atom causes charset-negotation to short-circuit +% the default setting is needed for non-charset responses such as image/png +% an example of how one might do actual negotiation +% [{"iso-8859-1", fun(X) -> X end}, {"utf-8", make_utf8}]; +default(encodings_provided) -> + [{"identity", fun(X) -> X end}]; +% this is handy for auto-gzip of GET-only resources: +% [{"identity", fun(X) -> X end}, {"gzip", fun(X) -> zlib:gzip(X) end}]; +default(variances) -> + []; +default(is_conflict) -> + false; +default(multiple_choices) -> + false; +default(previously_existed) -> + false; +default(moved_permanently) -> + false; +default(moved_temporarily) -> + false; +default(last_modified) -> + undefined; +default(expires) -> + undefined; +default(generate_etag) -> + undefined; +default(finish_request) -> + true; +default(_) -> + no_default. + +wrap(Mod, Args) -> + case Mod:init(Args) of + {ok, ModState} -> + {ok, webmachine_resource:new(Mod, ModState, + dict:from_list(Mod:module_info(exports)), false)}; + {{trace, Dir}, ModState} -> + {ok, File} = open_log_file(Dir, Mod), + log_decision(File, v3b14), + log_call(File, attempt, Mod, init, Args), + log_call(File, result, Mod, init, {{trace, Dir}, ModState}), + {ok, webmachine_resource:new(Mod, ModState, + dict:from_list(Mod:module_info(exports)), File)}; + _ -> + {stop, bad_init_arg} + end. + +do(Fun, ReqProps) when is_atom(Fun) andalso is_list(ReqProps) -> + Self = proplists:get_value(resource, ReqProps), + Req = proplists:get_value(req, ReqProps), + RD0 = Req:get_reqdata(), + {Reply, RD1, NewModState} = handle_wm_call(Fun, RD0), + case Reply of + {error, Err} -> {Err, Self}; + _ -> + Req:set_reqdata(RD1), + {Reply, + webmachine_resource:new(R_Mod, NewModState, R_ModExports, R_Trace)} + end. + +handle_wm_call(Fun, ReqData) -> + case default(Fun) of + no_default -> + resource_call(Fun, ReqData); + Default -> + case dict:is_key(Fun, R_ModExports) of % XXX SLOW PROBABLY + true -> + resource_call(Fun, ReqData); + false -> + if is_pid(R_Trace) -> + log_call(R_Trace, + not_exported, + R_Mod, Fun, [ReqData, R_ModState]); + true -> ok + end, + {Default, ReqData, R_ModState} + end + end. + +resource_call(F, ReqData) -> + case R_Trace of + false -> nop; + _ -> log_call(R_Trace, attempt, R_Mod, F, [ReqData, R_ModState]) + end, + Result = try + apply(R_Mod, F, [ReqData, R_ModState]) + catch C:R -> + Reason = {C, R, erlang:get_stacktrace()}, + {{error, Reason}, ReqData, R_ModState} + end, + case R_Trace of + false -> nop; + _ -> log_call(R_Trace, result, R_Mod, F, Result) + end, + Result. + +log_d(DecisionID) -> + case R_Trace of + false -> nop; + _ -> log_decision(R_Trace, DecisionID) + end. + +stop() -> close_log_file(R_Trace). + +log_call(File, Type, M, F, Data) -> + io:format(File, + "{~p, ~p, ~p,~n ~p}.~n", + [Type, M, F, escape_trace_data(Data)]). + +escape_trace_data(Fun) when is_function(Fun) -> + {'WMTRACE_ESCAPED_FUN', + [erlang:fun_info(Fun, module), + erlang:fun_info(Fun, name), + erlang:fun_info(Fun, arity), + erlang:fun_info(Fun, type)]}; +escape_trace_data(Pid) when is_pid(Pid) -> + {'WMTRACE_ESCAPED_PID', pid_to_list(Pid)}; +escape_trace_data(List) when is_list(List) -> + escape_trace_list(List, []); +escape_trace_data(Tuple) when is_tuple(Tuple) -> + list_to_tuple(escape_trace_data(tuple_to_list(Tuple))); +escape_trace_data(Other) -> + Other. + +escape_trace_list([Head|Tail], Acc) -> + escape_trace_list(Tail, [escape_trace_data(Head)|Acc]); +escape_trace_list([], Acc) -> + %% proper, nil-terminated list + lists:reverse(Acc); +escape_trace_list(Final, Acc) -> + %% non-nil-terminated list, like the dict module uses + lists:reverse(tl(Acc))++[hd(Acc)|escape_trace_data(Final)]. + +log_decision(File, DecisionID) -> + io:format(File, "{decision, ~p}.~n", [DecisionID]). + +open_log_file(Dir, Mod) -> + Now = {_,_,US} = now(), + {{Y,M,D},{H,I,S}} = calendar:now_to_universal_time(Now), + Filename = io_lib:format( + "~s/~p-~4..0B-~2..0B-~2..0B" + "-~2..0B-~2..0B-~2..0B.~6..0B.wmtrace", + [Dir, Mod, Y, M, D, H, I, S, US]), + file:open(Filename, [write]). + +close_log_file(File) when is_pid(File) -> + file:close(File); +close_log_file(_) -> + ok. diff --git a/web/api/webmachine/src/webmachine_skel.erl b/web/api/webmachine/src/webmachine_skel.erl new file mode 100644 index 0000000..bf835c4 --- /dev/null +++ b/web/api/webmachine/src/webmachine_skel.erl @@ -0,0 +1,89 @@ +%% @author Justin Sheehy +%% @author Andy Gross +%% @copyright 2007-2008 Basho Technologies +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. + +-module(webmachine_skel). +-export([skelcopy/2]). + +-include_lib("kernel/include/file.hrl"). + +%% External API + +skelcopy(DestDir, Name) -> + ok = ensuredir(DestDir), + LDst = case length(filename:dirname(DestDir)) of + 1 -> %% handle case when dirname returns "/" + 0; + N -> + N + 1 + end, + skelcopy(src(), DestDir, Name, LDst), + ok = file:make_symlink( + filename:join(filename:dirname(code:which(?MODULE)), ".."), + filename:join([DestDir, Name, "deps", "webmachine"])). + + +%% Internal API + +src() -> + Dir = filename:dirname(code:which(?MODULE)), + filename:join(Dir, "../priv/skel"). + +skel() -> + "skel". + +skelcopy(Src, DestDir, Name, LDst) -> + Dest = re:replace(filename:basename(Src), skel(), Name, + [global, {return, list}]), + case file:read_file_info(Src) of + {ok, #file_info{type=directory, mode=Mode}} -> + Dir = DestDir ++ "/" ++ Dest, + EDst = lists:nthtail(LDst, Dir), + ok = ensuredir(Dir), + ok = file:write_file_info(Dir, #file_info{mode=Mode}), + {ok, Files} = file:list_dir(Src), + io:format("~s/~n", [EDst]), + lists:foreach(fun ("." ++ _) -> ok; + (F) -> + skelcopy(filename:join(Src, F), + Dir, + Name, + LDst) + end, + Files), + ok; + {ok, #file_info{type=regular, mode=Mode}} -> + OutFile = filename:join(DestDir, Dest), + {ok, B} = file:read_file(Src), + S = re:replace(binary_to_list(B), skel(), Name, + [{return, list}, global]), + ok = file:write_file(OutFile, list_to_binary(S)), + ok = file:write_file_info(OutFile, #file_info{mode=Mode}), + io:format(" ~s~n", [filename:basename(Src)]), + ok; + {ok, _} -> + io:format("ignored source file: ~p~n", [Src]), + ok + end. + +ensuredir(Dir) -> + case file:make_dir(Dir) of + ok -> + ok; + {error, eexist} -> + ok; + E -> + E + end. diff --git a/web/api/webmachine/src/webmachine_sup.erl b/web/api/webmachine/src/webmachine_sup.erl new file mode 100644 index 0000000..c488058 --- /dev/null +++ b/web/api/webmachine/src/webmachine_sup.erl @@ -0,0 +1,77 @@ +%% @author Justin Sheehy +%% @author Andy Gross +%% @copyright 2007-2008 Basho Technologies +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. + +%% @doc Supervisor for the webmachine application. + +-module(webmachine_sup). + +-behaviour(supervisor). + +%% External exports +-export([start_link/0, upgrade/0, start_logger/1]). +-export([start_perf_logger/1]). + +%% supervisor callbacks +-export([init/1]). + +%% @spec start_link() -> ServerRet +%% @doc API for starting the supervisor. +start_link() -> + supervisor:start_link({local, ?MODULE}, ?MODULE, []). + +start_logger(BaseDir) -> + LoggerModule = + case application:get_env(webmachine, webmachine_logger_module) of + {ok, Val} -> Val; + _ -> webmachine_logger + end, + ChildSpec = + {webmachine_logger, + {LoggerModule, start_link, [BaseDir]}, + permanent, 5000, worker, dynamic}, + supervisor:start_child(?MODULE, ChildSpec). + +start_perf_logger(BaseDir) -> + ChildSpec = + {webmachine_perf_logger, + {webmachine_perf_logger, start_link, [BaseDir]}, + permanent, 5000, worker, [webmachine_perf_logger]}, + supervisor:start_child(?MODULE, ChildSpec). + +%% @spec upgrade() -> ok +%% @doc Add processes if necessary. +upgrade() -> + {ok, {_, Specs}} = init([]), + + Old = sets:from_list( + [Name || {Name, _, _, _} <- supervisor:which_children(?MODULE)]), + New = sets:from_list([Name || {Name, _, _, _, _, _} <- Specs]), + Kill = sets:subtract(Old, New), + + sets:fold(fun (Id, ok) -> + supervisor:terminate_child(?MODULE, Id), + supervisor:delete_child(?MODULE, Id), + ok + end, ok, Kill), + + [supervisor:start_child(?MODULE, Spec) || Spec <- Specs], + ok. + +%% @spec init([]) -> SupervisorTree +%% @doc supervisor callback. +init([]) -> + Processes = [], + {ok, {{one_for_one, 9, 10}, Processes}}. diff --git a/web/api/webmachine/src/webmachine_util.erl b/web/api/webmachine/src/webmachine_util.erl new file mode 100644 index 0000000..64c70a2 --- /dev/null +++ b/web/api/webmachine/src/webmachine_util.erl @@ -0,0 +1,297 @@ +%% @author Justin Sheehy +%% @author Andy Gross +%% @copyright 2007-2008 Basho Technologies +%% (guess_mime/1 derived from code copyright 2007 Mochi Media, Inc.) +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. + +%% @doc Utilities for parsing, quoting, and negotiation. + +-module(webmachine_util). +-export([guess_mime/1]). +-export([convert_request_date/1, compare_ims_dates/2]). +-export([choose_media_type/2]). +-export([choose_charset/2]). +-export([choose_encoding/2]). +-export([unquote_header/1]). +-export([now_diff_milliseconds/2]). +-export([media_type_to_detail/1]). +-export([test/0]). + +convert_request_date(Date) -> + try + case httpd_util:convert_request_date(Date) of + ReqDate -> ReqDate + end + catch + error:_ -> bad_date + end. + +%% returns true if D1 > D2 +compare_ims_dates(D1, D2) -> + GD1 = calendar:datetime_to_gregorian_seconds(D1), + GD2 = calendar:datetime_to_gregorian_seconds(D2), + GD1 > GD2. + +%% @spec guess_mime(string()) -> string() +%% @doc Guess the mime type of a file by the extension of its filename. +guess_mime(File) -> + case filename:extension(File) of + ".html" -> + "text/html"; + ".xhtml" -> + "application/xhtml+xml"; + ".xml" -> + "application/xml"; + ".css" -> + "text/css"; + ".js" -> + "application/x-javascript"; + ".jpg" -> + "image/jpeg"; + ".jpeg" -> + "image/jpeg"; + ".gif" -> + "image/gif"; + ".png" -> + "image/png"; + ".ico" -> + "image/x-icon"; + ".swf" -> + "application/x-shockwave-flash"; + ".zip" -> + "application/zip"; + ".bz2" -> + "application/x-bzip2"; + ".gz" -> + "application/x-gzip"; + ".tar" -> + "application/x-tar"; + ".tgz" -> + "application/x-gzip"; + ".htc" -> + "text/x-component"; + _ -> + "text/plain" + end. + +choose_media_type(Provided,AcceptHead) -> + % Return the Content-Type we will serve for a request. + % If there is no acceptable/available match, return the atom "none". + % AcceptHead is the value of the request's Accept header + % Provided is a list of media types the resource can provide. + % each is either a string e.g. -- "text/html" + % or a string and parameters e.g. -- {"text/html",[{level,1}]} + % (the plain string case with no parameters is much more common) + Requested = accept_header_to_media_types(AcceptHead), + Prov1 = normalize_provided(Provided), + choose_media_type1(Prov1,Requested). +choose_media_type1(_Provided,[]) -> + none; +choose_media_type1(Provided,[H|T]) -> + {_Pri,Type,Params} = H, + case media_match({Type,Params}, Provided) of + [] -> choose_media_type1(Provided,T); + [{CT_T,CT_P}|_] -> format_content_type(CT_T,CT_P) + end. + +media_match(_,[]) -> []; +media_match({"*/*",[]},[H|_]) -> [H]; +media_match({Type,Params},Provided) -> + [{T1,P1} || {T1,P1} <- Provided, + media_type_match(Type,T1), media_params_match(Params,P1)]. +media_type_match(Req,Prov) -> + case Req of + "*" -> % might as well not break for lame (Gomez) clients + true; + "*/*" -> + true; + Prov -> + true; + _ -> + [R1|R2] = string:tokens(Req,"/"), + [P1,_P2] = string:tokens(Prov,"/"), + case R2 of + ["*"] -> + case R1 of + P1 -> true; + _ -> false + end; + _ -> false + end + end. +media_params_match(Req,Prov) -> + lists:sort(Req) =:= lists:sort(Prov). + +prioritize_media(TyParam) -> + {Type, Params} = TyParam, + prioritize_media(Type,Params,[]). +prioritize_media(Type,Params,Acc) -> + case Params of + [] -> + {1, Type, Acc}; + _ -> + [{Tok,Val}|Rest] = Params, + case Tok of + "q" -> + QVal = case Val of + "1" -> + 1; + _ -> list_to_float(Val) + end, + {QVal, Type, Rest ++ Acc}; + _ -> + prioritize_media(Type,Rest,[{Tok,Val}|Acc]) + end + end. + +media_type_to_detail(MType) -> + [CType|Params] = string:tokens(MType, ";"), + MParams = [list_to_tuple([string:strip(KV) || KV <- string:tokens(X,"=")]) + || X <- Params], + {CType, MParams}. + +accept_header_to_media_types(HeadVal) -> + % given the value of an accept header, produce an ordered list + % based on the q-values. Results are [{Type,Params}] with the + % head of the list being the highest-priority requested type. + try + lists:reverse(lists:keysort(1, + [prioritize_media(media_type_to_detail(MType)) || + MType <- [string:strip(X) || X <- string:tokens(HeadVal, ",")]])) + catch _:_ -> [] + end. + +normalize_provided(Provided) -> + [normalize_provided1(X) || X <- Provided]. +normalize_provided1(Type) when is_list(Type) -> {Type, []}; +normalize_provided1({Type,Params}) -> {Type, Params}. + +format_content_type(Type,[]) -> Type; +format_content_type(Type,[H|T]) -> format_content_type(Type ++ "; " ++ H, T). + +choose_charset(CSets, AccCharHdr) -> do_choose(CSets, AccCharHdr, "ISO-8859-1"). + +choose_encoding(Encs, AccEncHdr) -> do_choose(Encs, AccEncHdr, "identity"). + +do_choose(Choices, Header, Default) -> + Accepted = build_conneg_list(string:tokens(Header, ",")), + DefaultPrio = [P || {P,C} <- Accepted, C =:= Default], + StarPrio = [P || {P,C} <- Accepted, C =:= "*"], + DefaultOkay = case DefaultPrio of + [] -> + case StarPrio of + [0.0] -> no; + _ -> yes + end; + [0.0] -> no; + _ -> yes + end, + AnyOkay = case StarPrio of + [] -> no; + [0.0] -> no; + _ -> yes + end, + do_choose(Default, DefaultOkay, AnyOkay, Choices, Accepted). +do_choose(_Default, _DefaultOkay, _AnyOkay, [], []) -> + none; +do_choose(_Default, _DefaultOkay, _AnyOkay, [], _Accepted) -> + none; +do_choose(Default, DefaultOkay, AnyOkay, Choices, []) -> + case AnyOkay of + yes -> hd(Choices); + no -> + case DefaultOkay of + yes -> + case lists:member(Default, Choices) of + true -> Default; + _ -> none + end; + no -> none + end + end; +do_choose(Default, DefaultOkay, AnyOkay, Choices, [AccPair|AccRest]) -> + {Prio, Acc} = AccPair, + case Prio of + 0.0 -> + do_choose(Default, DefaultOkay, AnyOkay, + lists:delete(Acc, Choices), AccRest); + _ -> + LAcc = string:to_lower(Acc), + LChoices = [string:to_lower(X) || X <- Choices], + % doing this a little more work than needed in + % order to be easily insensitive but preserving + case lists:member(LAcc, LChoices) of + true -> + hd([X || X <- Choices, + string:to_lower(X) =:= LAcc]); + false -> do_choose(Default, DefaultOkay, AnyOkay, + Choices, AccRest) + end + end. + +build_conneg_list(AccList) -> + build_conneg_list(AccList, []). +build_conneg_list([], Result) -> lists:reverse(lists:sort(Result)); +build_conneg_list([Acc|AccRest], Result) -> + XPair = list_to_tuple([string:strip(X) || X <- string:tokens(Acc, ";")]), + Pair = case XPair of + {Choice, "q=" ++ PrioStr} -> + case PrioStr of + "0" -> {0.0, Choice}; + "1" -> {1.0, Choice}; + _ -> {list_to_float(PrioStr), Choice} + end; + {Choice} -> + {1.0, Choice} + end, + build_conneg_list(AccRest,[Pair|Result]). + +% (unquote_header copied from mochiweb_util since they don't export it) +unquote_header("\"" ++ Rest) -> + unquote_header(Rest, []); +unquote_header(S) -> + S. +unquote_header("", Acc) -> + lists:reverse(Acc); +unquote_header("\"", Acc) -> + lists:reverse(Acc); +unquote_header([$\\, C | Rest], Acc) -> + unquote_header(Rest, [C | Acc]); +unquote_header([C | Rest], Acc) -> + unquote_header(Rest, [C | Acc]). + +%% @type now() = {MegaSecs, Secs, MicroSecs} + +%% This is faster than timer:now_diff() because it does not use bignums. +%% But it returns *milliseconds* (timer:now_diff returns microseconds.) +%% From http://www.erlang.org/ml-archive/erlang-questions/200205/msg00027.html + +%% @doc Compute the difference between two now() tuples, in milliseconds. +%% @spec now_diff_milliseconds(now(), now()) -> integer() +now_diff_milliseconds({M,S,U}, {M,S1,U1}) -> + ((S-S1) * 1000) + ((U-U1) div 1000); +now_diff_milliseconds({M,S,U}, {M1,S1,U1}) -> + ((M-M1)*1000000+(S-S1))*1000 + ((U-U1) div 1000). + +test() -> + test_choose_media_type(), + ok. + +test_choose_media_type() -> + Provided = "text/html", + ShouldMatch = ["*", "*/*", "text/*", "text/html"], + WantNone = ["foo", "text/xml", "application/*", "foo/bar/baz"], + [ Provided = choose_media_type([Provided], I) || I <- ShouldMatch ], + [ none = choose_media_type([Provided], I) || I <- WantNone ], + ok. diff --git a/web/api/webmachine/src/wmtrace_resource.erl b/web/api/webmachine/src/wmtrace_resource.erl new file mode 100755 index 0000000..e30f032 --- /dev/null +++ b/web/api/webmachine/src/wmtrace_resource.erl @@ -0,0 +1,363 @@ +%% @author Bryan Fink +%% @doc Webmachine trace file interpretter. +-module(wmtrace_resource). + +-export([add_dispatch_rule/2, + remove_dispatch_rules/0]). + +-export([start_link/1, + ping/2, + init/1, + resource_exists/2, + content_types_provided/2, + produce_html/2, + produce_javascript/2, + produce_map/2, + produce_css/2]). + +-include("include/wm_reqdata.hrl"). + +-record(ctx, {trace_dir, trace}). + +-define(MAP_EXTERNAL, "static/map.png"). +-define(MAP_INTERNAL, "deps/webmachine/docs/http-headers-status-v3.png"). +-define(SCRIPT_EXTERNAL, "static/wmtrace.js"). +-define(SCRIPT_INTERNAL, "deps/webmachine/trace/wmtrace.js"). +-define(STYLE_EXTERNAL, "static/wmtrace.css"). +-define(STYLE_INTERNAL, "deps/webmachine/trace/wmtrace.css"). + +%% +%% Dispatch Modifiers +%% + +%% @spec add_dispatch_rule(string(), string()) -> ok +%% @doc Add a dispatch rule to point at wmtrace_resource. +%% Example: to serve wmtrace_resource from +%% http://yourhost/dev/wmtrace/ +%% with trace files on disk at +%% priv/traces +%% call: +%% add_dispatch_rule("dev/wmtrace", "priv/traces") +add_dispatch_rule(BasePath, TracePath) when is_list(BasePath), + is_list(TracePath) -> + Parts = string:tokens(BasePath, "/"), + set_dispatch_list( + [{Parts++['*'], ?MODULE, [{trace_dir, TracePath}]} + |get_dispatch_list()]). + +%% @spec remove_dispatch_rules() -> ok +%% @doc Remove all dispatch rules pointing to wmtrace_resource. +remove_dispatch_rules() -> + set_dispatch_list( + [ D || D={_,M,_} <- get_dispatch_list(), + M /= ?MODULE ]). + +get_dispatch_list() -> + {ok, Dispatch} = application:get_env(webmachine, dispatch_list), + Dispatch. + +set_dispatch_list(NewList) when is_list(NewList) -> + application:set_env(webmachine, dispatch_list, NewList). + +%% +%% Resource +%% + +start_link(Args) -> + webmachine_resource:start_link(?MODULE, [Args]). + +ping(ReqData, State) -> + {pong, ReqData, State}. + +init(Config) -> + {trace_dir, TraceDir} = proplists:lookup(trace_dir, Config), + {trace_dir_exists, true} = {trace_dir_exists, filelib:is_dir(TraceDir)}, + {ok, #ctx{trace_dir=TraceDir}}. + +resource_exists(RD, Ctx) -> + case wrq:disp_path(RD) of + [] -> + case lists:reverse(wrq:raw_path(RD)) of + [$/|_] -> + {true, RD, Ctx}; + _ -> + {{halt, 303}, + wrq:set_resp_header("Location", + wrq:raw_path(RD)++"/", + RD), + Ctx} + end; + ?MAP_EXTERNAL -> + {filelib:is_file(?MAP_INTERNAL), RD, Ctx}; + ?SCRIPT_EXTERNAL -> + {filelib:is_file(?SCRIPT_INTERNAL), RD, Ctx}; + ?STYLE_EXTERNAL -> + {filelib:is_file(?STYLE_INTERNAL), RD, Ctx}; + TraceName -> + TracePath = filename:join([Ctx#ctx.trace_dir, TraceName]), + {filelib:is_file(TracePath), RD, Ctx#ctx{trace=TracePath}} + end. + +content_types_provided(RD, Ctx) -> + case wrq:disp_path(RD) of + ?MAP_EXTERNAL -> + {[{"image/png", produce_map}], RD, Ctx}; + ?SCRIPT_EXTERNAL -> + {[{"text/javascript", produce_javascript}], RD, Ctx}; + ?STYLE_EXTERNAL -> + {[{"text/css", produce_css}], RD, Ctx}; + _ -> + {[{"text/html", produce_html}], RD, Ctx} + end. + +produce_html(RD, Ctx=#ctx{trace=undefined}) -> + Dir = filename:absname(Ctx#ctx.trace_dir), + Files = lists:reverse( + lists:sort( + filelib:fold_files(Dir, + ".*\.wmtrace", + false, + fun(F, Acc) -> + [filename:basename(F)|Acc] + end, + []))), + {trace_list_html(Dir, Files), RD, Ctx}; +produce_html(RD, Ctx) -> + Filename = filename:absname(Ctx#ctx.trace), + {ok, Data} = file:consult(Filename), + {trace_html(Filename, Data), RD, Ctx}. + +trace_list_html(Dir, Files) -> + html([], + [head([], + title([], ["Webmachine Trace List for ",Dir])), + body([], + [h1([], ["Traces in ",Dir]), + ul([], + [ li([], a([{"href", F}], F)) || F <- Files ]) + ]) + ]). + +trace_html(Filename, Data) -> + {Request, Response, Trace} = encode_trace(Data), + html([], + [head([], + [title([],["Webmachine Trace ",Filename]), + linkblock([{"rel", "stylesheet"}, + {"type", "text/css"}, + {"href", "static/wmtrace.css"}], + []), + script([{"type", "text/javascript"}, + {"src", "static/wmtrace.js"}], + []), + script([{"type", "text/javascript"}], + mochiweb_html:escape( + lists:flatten( + ["var request=",Request,";\n" + "var response=",Response,";\n" + "var trace=",Trace,";"]))) + ]), + body([], + [divblock([{"id", "zoompanel"}], + [button([{"id", "zoomout"}], ["zoom out"]), + button([{"id", "zoomin"}], ["zoom in"]) + ]), + canvas([{"id", "v3map"}, + {"width", "3138"}, + {"height", "2184"}], + []), + divblock([{"id", "sizetest"}], []), + divblock([{"id", "preview"}], + [divblock([{"id", "previewid"}],[]), + ul([{"id", "previewcalls"}], []) + ]), + divblock([{"id", "infopanel"}], + [divblock([{"id", "infocontrols"}], + [divblock([{"id", "requesttab"}, + {"class", "selectedtab"}],"Q"), + divblock([{"id", "responsetab"}], "R"), + divblock([{"id", "decisiontab"}], "D") + ]), + divblock([{"id", "requestdetail"}], + [divblock([], + [span([{"id", "requestmethod"}], []), + " ", + span([{"id", "requestpath"}], [])]), + ul([{"id", "requestheaders"}], []), + divblock([{"id", "requestbody"}], + []) + ]), + divblock([{"id", "responsedetail"}], + [divblock([{"id", "responsecode"}], []), + ul([{"id", "responseheaders"}], []), + divblock([{"id", "responsebody"}], []) + ]), + divblock([{"id", "decisiondetail"}], + [divblock([], + ["Decision: ", + select([{"id", "decisionid"}], []) + ]), + divblock([], + ["Calls:", + select([{"id", "decisioncalls"}], []) + ]), + divblock([], "Input:"), + pre([{"id", "callinput"}], []), + divblock([], "Output:"), + pre([{"id", "calloutput"}], []) + ]) + ]) + ]) + ]). + +produce_javascript(RD, Ctx) -> + {ok, Script} = file:read_file(?SCRIPT_INTERNAL), + {Script, RD, Ctx}. + +produce_map(RD, Ctx) -> + {ok, Map} = file:read_file(?MAP_INTERNAL), + {Map, RD, Ctx}. + +produce_css(RD, Ctx) -> + {ok, Script} = file:read_file(?STYLE_INTERNAL), + {Script, RD, Ctx}. + +%% +%% Trace Encoding +%% + +encode_trace(Data) -> + {Request, Response, Trace} = aggregate_trace(Data), + {mochijson:encode(encode_request(Request)), + mochijson:encode(encode_response(Response)), + mochijson:encode({array, [ encode_trace_part(P) || P <- Trace ]})}. + +aggregate_trace(RawTrace) -> + {Request, Response, Trace} = lists:foldl(fun aggregate_trace_part/2, + {undefined, 500, []}, + RawTrace), + {Request, Response, lists:reverse(Trace)}. + +aggregate_trace_part({decision, Decision}, {Q, R, Acc}) -> + BDN = base_decision_name(Decision), + case Acc of + [{BDN,_}|_] -> {Q, R, Acc}; %% subdecision (ex. v3b13b) + _ -> + {Q, R, [{base_decision_name(Decision), []}|Acc]} + end; +aggregate_trace_part({attempt, Module, Function, Args}, + {Q, R, [{Decision,Calls}|Acc]}) -> + {maybe_extract_request(Function, Args, Q), + R, [{Decision,[{Module, Function, Args, wmtrace_null}|Calls]}|Acc]}; +aggregate_trace_part({result, Module, Function, Result}, + {Q, R, [{Decision,[{Module,Function,Args,_}|Calls]}|Acc]}) -> + {Q, maybe_extract_response(Function, Result, R), + [{Decision,[{Module, Function, Args, Result}|Calls]}|Acc]}; +aggregate_trace_part({not_exported, Module, Function, Args}, + {Q, R, [{Decision,Calls}|Acc]}) -> + {Q, maybe_extract_response(Function, Args, R), + [{Decision,[{Module, Function, Args, wmtrace_not_exported}|Calls]} + |Acc]}. + +maybe_extract_request(ping, [ReqData,_], _) -> + ReqData; +maybe_extract_request(_, _, R) -> + R. + +maybe_extract_response(finish_request, [ReqData,_], _) -> + ReqData; +maybe_extract_response(finish_request, {_, ReqData, _}, _) -> + ReqData; +maybe_extract_response(_, _, R) -> + R. + +base_decision_name(Decision) -> + [$v,$3|D] = atom_to_list(Decision), %% strip 'v3' + case lists:reverse(D) of + [A|RD] when A >= $a, A =< $z -> + lists:reverse(RD); %% strip 'b' off end of some + _ -> + D + end. + +encode_request(ReqData) when is_record(ReqData, wm_reqdata) -> + {struct, [{"method", atom_to_list( + wrq:method(ReqData))}, + {"path", wrq:raw_path(ReqData)}, + {"headers", encode_headers(wrq:req_headers(ReqData))}, + {"body", case ReqData#wm_reqdata.req_body of + undefined -> []; + Body when is_atom(Body) -> + atom_to_list(Body); + Body -> lists:flatten(io_lib:format("~s", [Body])) + end}]}. + +encode_response(ReqData) -> + {struct, [{"code", integer_to_list( + wrq:response_code(ReqData))}, + {"headers", encode_headers(wrq:resp_headers(ReqData))}, + {"body", lists:flatten(io_lib:format("~s", [wrq:resp_body(ReqData)]))}]}. + +encode_headers(Headers) when is_list(Headers) -> + {struct, [ {N, V} || {N, V} <- Headers ]}; +encode_headers(Headers) -> + encode_headers(mochiweb_headers:to_list(Headers)). + +encode_trace_part({Decision, Calls}) -> + {struct, [{"d", Decision}, + {"calls", + {array, [ {struct, + [{"module", Module}, + {"function", Function}, + {"input", encode_trace_io(Input)}, + {"output", encode_trace_io(Output)}]} + || {Module, Function, Input, Output} + <- lists:reverse(Calls) ]}}]}. + +encode_trace_io(wmtrace_null) -> null; +encode_trace_io(wmtrace_not_exported) -> "wmtrace_not_exported"; +encode_trace_io(Data) -> + lists:flatten(io_lib:format("~p", [Data])). + +%% +%% HTML Building +%% + +-define(TAG(T), T(Attrs, Content) -> + tag(??T, Attrs, Content)). + +?TAG(head). +?TAG(script). +?TAG(title). +?TAG(body). +?TAG(h1). +?TAG(ul). +?TAG(li). +?TAG(a). +?TAG(canvas). +?TAG(select). +?TAG(pre). +?TAG(span). +?TAG(button). + +html(_Attrs, Content) -> + [<<"">>, + <<"">>, + Content, + <<"">>]. + +divblock(Attrs, Content) -> + tag("div", Attrs, Content). %% div is a reserved word + +linkblock(Attrs, Content) -> + tag("link", Attrs, Content). %% link is a reserved word + +tag(Name, Attrs, Content) -> + ["<",Name, + [ [" ",K,"=\"",V,"\""] || {K, V} <- Attrs ], + if Content == empty -> "/>"; + true -> + [">", + Content, + ""] + end]. diff --git a/web/api/webmachine/src/wrq.erl b/web/api/webmachine/src/wrq.erl new file mode 100644 index 0000000..f2354ec --- /dev/null +++ b/web/api/webmachine/src/wrq.erl @@ -0,0 +1,196 @@ +%% @author Justin Sheehy +%% @copyright 2007-2009 Basho Technologies +%% +%% Licensed under the Apache License, Version 2.0 (the "License"); +%% you may not use this file except in compliance with the License. +%% You may obtain a copy of the License at +%% +%% http://www.apache.org/licenses/LICENSE-2.0 +%% +%% Unless required by applicable law or agreed to in writing, software +%% distributed under the License is distributed on an "AS IS" BASIS, +%% WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +%% See the License for the specific language governing permissions and +%% limitations under the License. + +-module(wrq). +-author('Justin Sheehy '). + +-export([create/4,load_dispatch_data/8]). +-export([method/1,version/1,peer/1,disp_path/1,path/1,raw_path/1,path_info/1, + response_code/1,req_cookie/1,req_qs/1,req_headers/1,req_body/1, + stream_req_body/2,resp_redirect/1,resp_headers/1,resp_body/1, + app_root/1,path_tokens/1, host_tokens/1, port/1]). +-export([path_info/2,get_req_header/2,do_redirect/2,fresh_resp_headers/2, + get_resp_header/2,set_resp_header/3,set_resp_headers/2, + set_disp_path/2,set_req_body/2,set_resp_body/2,set_response_code/2, + merge_resp_headers/2,remove_resp_header/2, + append_to_resp_body/2,append_to_response_body/2, + max_recv_body/1,set_max_recv_body/2, + get_cookie_value/2,get_qs_value/2,get_qs_value/3,set_peer/2]). + +-include_lib("include/wm_reqdata.hrl"). + +create(Method,Version,RawPath,Headers) -> + create(#wm_reqdata{method=Method,version=Version, + raw_path=RawPath,req_headers=Headers, + wmreq=defined_in_load_dispatch_data, + path="defined_in_create", + req_cookie=defined_in_create, + req_qs=defined_in_create, + peer="defined_in_wm_req_srv_init", + req_body=not_fetched_yet, + max_recv_body=(50*(1024*1024)), + app_root="defined_in_load_dispatch_data", + path_info=dict:new(), + path_tokens=defined_in_load_dispatch_data, + disp_path=defined_in_load_dispatch_data, + resp_redirect=false, resp_headers=mochiweb_headers:empty(), + resp_body = <<>>, response_code=500}). +create(RD = #wm_reqdata{raw_path=RawPath}) -> + {Path, _, _} = mochiweb_util:urlsplit_path(RawPath), + Cookie = case get_req_header("cookie", RD) of + undefined -> []; + Value -> mochiweb_cookies:parse_cookie(Value) + end, + {_, QueryString, _} = mochiweb_util:urlsplit_path(RawPath), + ReqQS = mochiweb_util:parse_qs(QueryString), + RD#wm_reqdata{path=Path,req_cookie=Cookie,req_qs=ReqQS}. +load_dispatch_data(PathInfo, HostTokens, Port, PathTokens, AppRoot, + DispPath, WMReq, RD) -> + RD#wm_reqdata{path_info=PathInfo,host_tokens=HostTokens, + port=Port,path_tokens=PathTokens, + app_root=AppRoot,disp_path=DispPath,wmreq=WMReq}. + +method(_RD = #wm_reqdata{method=Method}) -> Method. + +version(_RD = #wm_reqdata{version=Version}) + when is_tuple(Version), size(Version) == 2, + is_integer(element(1,Version)), is_integer(element(2,Version)) -> Version. + +peer(_RD = #wm_reqdata{peer=Peer}) when is_list(Peer) -> Peer. + +app_root(_RD = #wm_reqdata{app_root=AR}) when is_list(AR) -> AR. + +% all three paths below are strings +disp_path(_RD = #wm_reqdata{disp_path=DP}) when is_list(DP) -> DP. + +path(_RD = #wm_reqdata{path=Path}) when is_list(Path) -> Path. + +raw_path(_RD = #wm_reqdata{raw_path=RawPath}) when is_list(RawPath) -> RawPath. + +path_info(_RD = #wm_reqdata{path_info=PathInfo}) -> PathInfo. % dict + +path_tokens(_RD = #wm_reqdata{path_tokens=PathT}) -> PathT. % list of strings + +host_tokens(_RD = #wm_reqdata{host_tokens=HostT}) -> HostT. % list of strings + +port(_RD = #wm_reqdata{port=Port}) -> Port. % integer + +response_code(_RD = #wm_reqdata{response_code=C}) when is_integer(C) -> C. + +req_cookie(_RD = #wm_reqdata{req_cookie=C}) when is_list(C) -> C. % string + +req_qs(_RD = #wm_reqdata{req_qs=QS}) when is_list(QS) -> QS. % string + +req_headers(_RD = #wm_reqdata{req_headers=ReqH}) -> ReqH. % mochiheaders + +req_body(_RD = #wm_reqdata{wmreq=WMReq,max_recv_body=MRB}) -> + maybe_conflict_body(WMReq:req_body(MRB)). + +stream_req_body(_RD = #wm_reqdata{wmreq=WMReq}, MaxHunk) -> + maybe_conflict_body(WMReq:stream_req_body(MaxHunk)). + +max_recv_body(_RD = #wm_reqdata{max_recv_body=X}) when is_integer(X) -> X. + +set_max_recv_body(X, RD) when is_integer(X) -> RD#wm_reqdata{max_recv_body=X}. + +maybe_conflict_body(BodyResponse) -> + case BodyResponse of + stream_conflict -> + exit("wrq:req_body and wrq:stream_req_body conflict"); + {error, req_body_too_large} -> + exit("request body too large"); + _ -> + BodyResponse + end. + +resp_redirect(_RD = #wm_reqdata{resp_redirect=true}) -> true; +resp_redirect(_RD = #wm_reqdata{resp_redirect=false}) -> false. + +resp_headers(_RD = #wm_reqdata{resp_headers=RespH}) -> RespH. % mochiheaders + +resp_body(_RD = #wm_reqdata{resp_body=undefined}) -> undefined; +resp_body(_RD = #wm_reqdata{resp_body={stream,X}}) -> {stream,X}; +resp_body(_RD = #wm_reqdata{resp_body=RespB}) when is_binary(RespB) -> RespB; +resp_body(_RD = #wm_reqdata{resp_body=RespB}) -> iolist_to_binary(RespB). + +%% -- + +path_info(Key, RD) when is_atom(Key) -> + case dict:find(Key, path_info(RD)) of + {ok, Value} when is_list(Value); is_integer(Value) -> + Value; % string (for host or path match) + % or integer (for port match) + error -> undefined + end. + +get_req_header(HdrName, RD) -> % string->string + mochiweb_headers:get_value(HdrName, req_headers(RD)). + +do_redirect(true, RD) -> RD#wm_reqdata{resp_redirect=true}; +do_redirect(false, RD) -> RD#wm_reqdata{resp_redirect=false}. + +set_peer(P, RD) when is_list(P) -> RD#wm_reqdata{peer=P}. % string + +set_disp_path(P, RD) when is_list(P) -> RD#wm_reqdata{disp_path=P}. % string + +set_req_body(Body, RD) -> RD#wm_reqdata{req_body=Body}. + +set_resp_body(Body, RD) -> RD#wm_reqdata{resp_body=Body}. + +set_response_code(Code, RD) when is_integer(Code) -> + RD#wm_reqdata{response_code=Code}. + +get_resp_header(HdrName, _RD=#wm_reqdata{resp_headers=RespH}) -> + mochiweb_headers:get_value(HdrName, RespH). +set_resp_header(K, V, RD=#wm_reqdata{resp_headers=RespH}) + when is_list(K),is_list(V) -> + RD#wm_reqdata{resp_headers=mochiweb_headers:enter(K, V, RespH)}. +set_resp_headers(Hdrs, RD=#wm_reqdata{resp_headers=RespH}) -> + F = fun({K, V}, Acc) -> mochiweb_headers:enter(K, V, Acc) end, + RD#wm_reqdata{resp_headers=lists:foldl(F, RespH, Hdrs)}. +fresh_resp_headers(Hdrs, RD) -> + F = fun({K, V}, Acc) -> mochiweb_headers:enter(K, V, Acc) end, + RD#wm_reqdata{resp_headers=lists:foldl(F, mochiweb_headers:empty(), Hdrs)}. +remove_resp_header(K, RD=#wm_reqdata{resp_headers=RespH}) when is_list(K) -> + RD#wm_reqdata{resp_headers=mochiweb_headers:from_list( + proplists:delete(K, + mochiweb_headers:to_list(RespH)))}. + +merge_resp_headers(Hdrs, RD=#wm_reqdata{resp_headers=RespH}) -> + F = fun({K, V}, Acc) -> mochiweb_headers:insert(K, V, Acc) end, + NewHdrs = lists:foldl(F, RespH, Hdrs), + RD#wm_reqdata{resp_headers=NewHdrs}. + +append_to_resp_body(Data, RD) -> append_to_response_body(Data, RD). +append_to_response_body(Data, RD=#wm_reqdata{resp_body=RespB}) -> + case is_binary(Data) of + true -> + Data0 = RespB, + Data1 = <>, + RD#wm_reqdata{resp_body=Data1}; + false -> % MUST BE an iolist! else, fail. + append_to_response_body(iolist_to_binary(Data), RD) + end. + +get_cookie_value(Key, RD) when is_list(Key) -> % string + proplists:get_value(Key, req_cookie(RD)). + +get_qs_value(Key, RD) when is_list(Key) -> % string + proplists:get_value(Key, req_qs(RD)). + +get_qs_value(Key, Default, RD) when is_list(Key) -> + proplists:get_value(Key, req_qs(RD), Default). + + diff --git a/web/api/webmachine/start-dev.sh b/web/api/webmachine/start-dev.sh new file mode 100755 index 0000000..3e64436 --- /dev/null +++ b/web/api/webmachine/start-dev.sh @@ -0,0 +1,3 @@ +#!/bin/sh +cd `dirname $0` +exec erl -smp auto +K true -name api@flukso.net -pa $PWD/ebin $PWD/deps/*/ebin -boot start_sasl -s reloader -s webmachine -s erlrrd diff --git a/web/api/webmachine/start.sh b/web/api/webmachine/start.sh new file mode 100755 index 0000000..ae27a05 --- /dev/null +++ b/web/api/webmachine/start.sh @@ -0,0 +1,3 @@ +#!/bin/sh +cd `dirname $0` +exec erl -pa $PWD/ebin $PWD/deps/*/ebin -boot start_sasl -s webmachine diff --git a/web/api/webmachine/trace/wmtrace.css b/web/api/webmachine/trace/wmtrace.css new file mode 100755 index 0000000..06bf99f --- /dev/null +++ b/web/api/webmachine/trace/wmtrace.css @@ -0,0 +1,107 @@ +body { + margin:0px; + padding:0px; +} + +canvas#v3map { + margin-top:2em; + z-index: 1; +} + +div#sizetest { + width:100%; +} + +div#zoompanel { + height:2em; + position:fixed; + z-index:10; +} + +div#preview { + position:absolute; + display:none; + background:#dddddd; + border:1px solid #999999; +} + +div#preview ul { + padding: 0px 0px 0px 0.5em; + margin: 0px; + list-style: none; +} + +div#infopanel { + z-index:20; + background:#dddddd; + position:fixed; + top:0px; + right:0px; + bottom:0px; + left:75%; + min-width:30em; + padding:5px; +} + +div#infocontrols { + position:absolute; + top:0px; + bottom:0px; + left:-5px; + width:5px; + background:#999999; + cursor:ew-resize; +} + +div#infocontrols div { + position:absolute; + left:-15px; + width:20px; + height:49px; + background:#999999; + cursor:pointer; +} + +div#infocontrols div.selectedtab { + background:#dddddd; + border-top: 1px solid #999999; + border-left: 1px solid #999999; + border-bottom: 1px solid #999999; +} + +div#requesttab { + top:2px; +} + +div#responsetab { + top:54px; +} + +div#decisiontab { + top:106px; +} + +div#requestdetail, div#responsedetail, div#decisiondetail { + height:100%; +} + +div#responsedetail, div#decisiondetail { + display:none; +} + +div#infopanel ul { + list-style:none; + padding-left:0px; + height:5em; + overflow-y:scroll; +} + +pre { + height:40%; + overflow:scroll; +} + +div#responsebody, div#requestbody { + height:70%; + overflow-y:scroll; +} diff --git a/web/api/webmachine/trace/wmtrace.js b/web/api/webmachine/trace/wmtrace.js new file mode 100755 index 0000000..b1a9da2 --- /dev/null +++ b/web/api/webmachine/trace/wmtrace.js @@ -0,0 +1,713 @@ +var HIGHLIGHT = '#cc00cc'; +var REGULAR = '#666666'; + +var cols = { + 'a':173, + 'b':325, + 'c':589, + 'd':797, + 'e':1005, + 'f':1195, + 'g':1402, + 'gg':1515, + 'h':1572, + 'i':1799, + 'j':1893, + 'k':1988, + 'l':2157, + 'll':2346, + 'm':2403, + 'mm':2535, + 'n':2554, + 'o':2649, + 'oo':2781, + 'ooo':2801, + 'p':2894, + 'q':3007 +}; + +var rows = { + '1':221, + '2':298, + '3':373, + '4':448, + '5':524, + '6':599, + '7':675, + '8':751, + '9':826, + '10':902, + '11':977, + '12':1053, + '13':1129, + '14':1204, + '15':1280, + '16':1355, + '17':1431, + '18':1506, + '19':1583, + '20':1658, + '21':1734, + '22':1809, + '23':1885, + '24':1961, + '25':2036, + '26':2112 +}; + +var edges = { + 'b14b13':['b14','b13'], + + 'b13b12':['b13','b12'], + 'b13503':['b13','503'], + + 'b12b11':['b12','b11'], + 'b12501':['b12','501'], + + 'b11b10':['b11','b10'], + 'b11414':['b11','414'], + + 'b10b9':['b10','b9'], + 'b10405':['b10','405'], + + 'b9b8':['b9','b8'], + 'b9400':['b9','400'], + + 'b8b7':['b8','b7'], + 'b8401':['b8','401'], + + 'b7b6':['b7','b6'], + 'b7403':['b7','403'], + + 'b6b5':['b6','b5'], + 'b6501':['b6','501a'], + + 'b5b4':['b5','b4'], + 'b5415':['b5','415'], + + 'b4b3':['b4','b3'], + 'b4413':['b4','b4'], + + 'b3c3':['b3','c3'], + 'b3200':['b3','200'], + + 'c3c4':['c3','c4'], + 'c3d4':['c3','d3','d4'], + + 'c4d4':['c4','d4'], + 'c4406':['c4','406'], + + 'd4d5':['d4','d5'], + 'd4e5':['d4','e4','e5'], + + 'd5e5':['d5','e5'], + 'd5406':['d5','d7','406'], + + 'e5e6':['e5','e6'], + 'e5f6':['e5','f5','f6'], + + 'e6f6':['e6','f6'], + 'e6406':['e6','e7','406'], + + 'f6f7':['f6','f7'], + 'f6g7':['f6','g6','g7'], + + 'f7g7':['f7','g7'], + 'f7406':['f7','406'], + + 'g7g8':['g7','g8'], + 'g7h7':['g7','h7'], + + 'g8g9':['g8','g9'], + 'g8h10':['g8','h8','h10'], + + 'g9g11':['g9','g11'], + 'g9h10':['g9','gg9','gg10','h10'], + + 'g11h10':['g11','gg11','gg10','h10'], + 'g11412':['g11','g18','412a'], + + 'h7i7':['h7','i7'], + 'h7412':['h7','412'], + + 'h10h11':['h10','h11'], + 'h10i12':['h10','i10','i12'], + + 'h11h12':['h11','h12'], + 'h11i12':['h11','i11','i12'], + + 'h12i12':['h12','i12'], + 'h12412':['h12','412a'], + + 'i4p3':['i4','i3','p3'], + 'i4301':['i4','301'], + + 'i7i4':['i7','i4'], + 'i7k7':['i7','k7'], + + 'i12l13':['i12','l12','l13'], + 'i12i13':['i12','i13'], + + 'i13k13':['i13','k13'], + 'i13j18':['i13','i17','j17','j18'], + + 'j18412':['j18','412a'], + 'j18304':['j18','304'], + + 'k5l5':['k5','l5'], + 'k5301':['k5','301'], + + 'k7k5':['k7','k5'], + 'k7l7':['k7','l7'], + + 'k13j18':['k13','k17','j17','j18'], + 'k13l13':['k13','l13'], + + 'l5m5':['l5','m5'], + 'l5307':['l5','307'], + + 'l7m7':['l7','m7'], + 'l7404':['l7','l8','404'], + + 'l13l14':['l13','l14'], + 'l13m16':['l13','m13','m16'], + + 'l14l15':['l14','l15'], + 'l14m16':['l14','m14','m16'], + + 'l15l17':['l15','l17'], + 'l15m16':['l15','ll15','ll16','m16'], + + 'l17m16':['l17','ll17','ll16','m16'], + 'l17304':['l17','304'], + + 'm5n5':['m5','n5'], + 'm5410':['m5','m4','410'], + + 'm7n11':['m7','n7','n11'], + 'm7404':['m7','404'], + + 'm16m20':['m16','m20'], + 'm16n16':['m16','n16'], + + 'm20o20':['m20','o20'], + 'm20202':['m20','202'], + + 'n5n11':['n5','n11'], + 'n5410':['n5','410'], + + 'n11p11':['n11','p11'], + 'n11303':['n11','303'], + + 'n16n11':['n16','n11'], + 'n16o16':['n16','o16'], + + 'o14p11':['o14','o11','p11'], + 'o14409':['o14','409a'], + + 'o16o14':['o16','o14'], + 'o16o18':['o16','o18'], + + 'o18200':['o18','200a'], + 'o18300':['o18','oo18','300'], + + 'o20o18':['o20','o18'], + 'o20204':['o20','204'], + + 'p3p11':['p3','p11'], + 'p3409':['p3','409'], + + 'p11o20':['p11','p20','o20'], + 'p11201':['p11','q11','201'] +}; + +var ends = { + '200': {col:'a', row:'3', width:190}, + '200a': {col:'mm', row:'18', width:116}, + '201': {col:'q', row:'12', width:154}, + '202': {col:'m', row:'21', width:116}, + '204': {col:'o', row:'21', width:152}, + + '300': {col:'oo', row:'19', width:152}, + '301': {col:'k', row:'4', width:154}, + '303': {col:'m', row:'11', width:116}, + '304': {col:'l', row:'18', width:116}, + '307': {col:'l', row:'4', width:154}, + + '400': {col:'a', row:'9', width:190}, + '401': {col:'a', row:'8', width:190}, + '403': {col:'a', row:'7', width:190}, + '404': {col:'m', row:'8', width:116}, + '405': {col:'a', row:'10', width:190}, + '406': {col:'c', row:'7', width:152}, + '409': {col:'p', row:'2', width:116}, + '409a': {col:'oo', row:'14', width:116}, + '410': {col:'n', row:'4', width:116}, + '412': {col:'h', row:'6', width:152}, + '412a': {col:'h', row:'18', width:152}, + '413': {col:'a', row:'4', width:190}, + '414': {col:'a', row:'11', width:190}, + '415': {col:'a', row:'5', width:190}, + + '501a': {col:'a', row:'6', width:190}, + '501': {col:'a', row:'12', width:190}, + '503': {col:'a', row:'13', width:190} +}; + +var canvas; + +function decorateTrace() { + trace[0].x = cols[trace[0].d[0]]; + trace[0].y = rows[trace[0].d.slice(1)]; + trace[0].previewCalls = previewCalls(trace[0]); + + for (var i = 1; i < trace.length; i++) { + trace[i].x = cols[trace[i].d[0]]; + trace[i].y = rows[trace[i].d.slice(1)]; + trace[i].previewCalls = previewCalls(trace[i]); + + var path = edges[trace[i-1].d+trace[i].d]; + if (path) { + trace[i].path = [path.length-1]; + for (var p = 1; p < path.length; p++) { + trace[i].path[p-1] = getSeg(path[p-1], path[p], p == path.length-1); + } + } else { + trace[i].path = []; + } + } + + var path = edges[trace[i-1].d+response.code]; + if (path) { + var end = ends[path[path.length-1]]; + response.x = cols[end.col]; + response.y = rows[end.row]; + response.width = end.width; + response.type = 'normal'; + + response.path = [path.length-1]; + for (var p = 1; p < path.length; p++) { + response.path[p-1] = getSeg(path[p-1], path[p], p == path.length-1); + } + } else { + var ld = trace[trace.length-1]; + response.x = ld.x+50; + response.y = ld.y-50; + response.width = 38; + response.type = 'other'; + + response.path = [ + {x1: ld.x+10, y1: ld.y-10, + x2: ld.x+36, y2: ld.y-36} + ]; + } +}; + +function previewCalls(dec) { + var prev = ''; + for (var i = 0; i < dec.calls.length; i++) { + if (dec.calls[i].output != "wmtrace_not_exported") + prev += '
  • '+dec.calls[i].module+':'+dec.calls[i]['function']+'
  • '; + } + return prev; +}; + +function drawTrace() { + drawDecision(trace[0]); + for (var i = 1; i < trace.length; i++) { + drawPath(trace[i].path); + drawDecision(trace[i]); + } + + drawPath(response.path); + drawResponse(); +}; + +function drawResponse() { + if (response.type == 'normal') { + var context = canvas.getContext('2d'); + context.strokeStyle=HIGHLIGHT; + context.lineWidth=4; + + context.beginPath(); + context.rect(response.x-(response.width/2), + response.y-19, + response.width, + 38); + context.stroke(); + } else { + var context = canvas.getContext('2d'); + context.strokeStyle='#ff0000'; + context.lineWidth=4; + + context.beginPath(); + context.arc(response.x, response.y, 19, + 0, 2*3.14159, false); + context.stroke(); + + } +}; + +function drawDecision(dec) { + var context = canvas.getContext('2d'); + + if (dec.previewCalls == '') + context.strokeStyle=REGULAR; + else + context.strokeStyle=HIGHLIGHT; + context.lineWidth=4; + + context.beginPath(); + context.moveTo(dec.x, dec.y-19); + context.lineTo(dec.x+19, dec.y); + context.lineTo(dec.x, dec.y+19); + context.lineTo(dec.x-19, dec.y); + context.closePath(); + context.stroke(); +}; + +function drawPath(path) { + var context = canvas.getContext('2d'); + context.strokeStyle=REGULAR; + context.lineWidth=4; + + context.beginPath(); + context.moveTo(path[0].x1, path[0].y1); + for (var p = 0; p < path.length; p++) { + context.lineTo(path[p].x2, path[p].y2); + } + context.stroke(); +}; + +function getSeg(p1, p2, last) { + var seg = { + x1:cols[p1[0]], + y1:rows[p1.slice(1)] + }; + if (ends[p2]) { + seg.x2 = cols[ends[p2].col]; + seg.y2 = rows[ends[p2].row]; + } else { + seg.x2 = cols[p2[0]]; + seg.y2 = rows[p2.slice(1)]; + } + + if (seg.x1 == seg.x2) { + if (seg.y1 < seg.y2) { + seg.y1 = seg.y1+19; + if (last) seg.y2 = seg.y2-19; + } else { + seg.y1 = seg.y1-19; + if (last) seg.y2 = seg.y2+19; + } + } else { + //assume seg.y1 == seg.y2 + if (seg.x1 < seg.x2) { + seg.x1 = seg.x1+19; + if (last) seg.x2 = seg.x2-(ends[p2] ? (ends[p2].width/2) : 19); + } else { + seg.x1 = seg.x1-19; + if (last) seg.x2 = seg.x2+(ends[p2] ? (ends[p2].width/2) : 19); + } + } + return seg; +}; + +function traceDecision(name) { + for (var i = trace.length-1; i >= 0; i--) + if (trace[i].d == name) return trace[i]; +}; + +var detailPanels = {}; +function initDetailPanels() { + var windowWidth = document.getElementById('sizetest').clientWidth; + var infoPanel = document.getElementById('infopanel'); + var panelWidth = windowWidth-infoPanel.offsetLeft; + + var panels = { + 'request': document.getElementById('requestdetail'), + 'response': document.getElementById('responsedetail'), + 'decision': document.getElementById('decisiondetail') + }; + + var tabs = { + 'request': document.getElementById('requesttab'), + 'response': document.getElementById('responsetab'), + 'decision': document.getElementById('decisiontab') + }; + + var decisionId = document.getElementById('decisionid'); + var decisionCalls = document.getElementById('decisioncalls'); + var callInput = document.getElementById('callinput'); + var callOutput = document.getElementById('calloutput'); + + var lastUsedPanelWidth = windowWidth-infoPanel.offsetLeft; + + var setPanelWidth = function(width) { + infoPanel.style.left = (windowWidth-width)+'px'; + canvas.style.marginRight = (width+20)+'px'; + panelWidth = width; + }; + setPanelWidth(panelWidth); + + var ensureVisible = function() { + if (windowWidth-infoPanel.offsetLeft < 10) + setPanelWidth(lastUsedPanelWidth); + }; + + var decChoices = ''; + for (var i = 0; i < trace.length; i++) { + decChoices += ''; + } + decisionId.innerHTML = decChoices; + decisionId.selectedIndex = -1; + + decisionId.onchange = function() { + detailPanels.setDecision(traceDecision(decisionId.value)); + } + + detailPanels.setDecision = function(dec) { + decisionId.value = dec.d; + + var calls = []; + for (var i = 0; i < dec.calls.length; i++) { + calls.push(''); + } + decisionCalls.innerHTML = calls.join(''); + decisionCalls.selectedIndex = 0; + + decisionCalls.onchange(); + }; + + detailPanels.show = function(name) { + for (p in panels) { + if (p == name) { + panels[p].style.display = 'block'; + tabs[p].className = 'selectedtab'; + } + else { + panels[p].style.display = 'none'; + tabs[p].className = ''; + } + } + ensureVisible(); + }; + + detailPanels.hide = function() { + setPanelWidth(0); + } + + decisionCalls.onchange = function() { + var val = decisionCalls.value; + if (val) { + var dec = traceDecision(val.substring(0, val.indexOf('-'))); + var call = dec.calls[parseInt(val.substring(val.indexOf('-')+1, val.length))]; + + if (call.output != "wmtrace_not_exported") { + callInput.style.color='#000000'; + callInput.innerHTML = call.input; + if (call.output != null) { + callOutput.style.color = '#000000'; + callOutput.innerHTML = call.output; + } else { + callOutput.style.color = '#ff0000'; + callOutput.textContent = 'Error: '+call.module+':'+call['function']+' never returned'; + } + } else { + callInput.style.color='#999999'; + callInput.textContent = call.module+':'+call['function']+' was not exported'; + callOutput.textContent = ''; + } + } else { + callInput.textContent = ''; + callOutput.textContent = ''; + } + }; + + var headersList = function(headers) { + var h = ''; + for (n in headers) h += '
  • '+n+': '+headers[n]; + return h; + }; + + document.getElementById('requestmethod').innerHTML = request.method; + document.getElementById('requestpath').innerHTML = request.path; + document.getElementById('requestheaders').innerHTML = headersList(request.headers); + document.getElementById('requestbody').innerHTML = request.body; + + document.getElementById('responsecode').innerHTML = response.code; + document.getElementById('responseheaders').innerHTML = headersList(response.headers); + document.getElementById('responsebody').innerHTML = response.body; + + + var infoControls = document.getElementById('infocontrols'); + var md = false; + var dragged = false; + var msoff = 0; + infoControls.onmousedown = function(ev) { + md = true; + dragged = false; + msoff = ev.clientX-infoPanel.offsetLeft; + }; + + infoControls.onclick = function(ev) { + if (dragged) { + lastUsedPanelWidth = panelWidth; + } + else if (panelWidth < 10) { + switch(ev.target.id) { + case 'requesttab': detailPanels.show('request'); break; + case 'responsetab': detailPanels.show('response'); break; + case 'decisiontab': detailPanels.show('decision'); break; + default: ensureVisible(); + } + } else { + var name = 'none'; + switch(ev.target.id) { + case 'requesttab': name = 'request'; break; + case 'responsetab': name = 'response'; break; + case 'decisiontab': name = 'decision'; break; + } + + if (panels[name] && panels[name].style.display != 'block') + detailPanels.show(name); + else + detailPanels.hide(); + } + + return false; + }; + + document.onmousemove = function(ev) { + if (md) { + dragged = true; + panelWidth = windowWidth-(ev.clientX-msoff); + if (panelWidth < 0) { + panelWidth = 0; + infoPanel.style.left = windowWidth+"px"; + } + else if (panelWidth > windowWidth-21) { + panelWidth = windowWidth-21; + infoPanel.style.left = '21px'; + } + else + infoPanel.style.left = (ev.clientX-msoff)+"px"; + + canvas.style.marginRight = panelWidth+20+"px"; + return false; + } + }; + + document.onmouseup = function() { md = false; }; + + window.onresize = function() { + windowWidth = document.getElementById('sizetest').clientWidth; + infoPanel.style.left = windowWidth-panelWidth+'px'; + }; +}; + +window.onload = function() { + canvas = document.getElementById('v3map'); + + initDetailPanels(); + + var scale = 0.25; + var coy = canvas.offsetTop; + function findDecision(ev) { + var x = (ev.clientX+window.pageXOffset)/scale; + var y = (ev.clientY+window.pageYOffset-coy)/scale; + + for (var i = trace.length-1; i >= 0; i--) { + if (x >= trace[i].x-19 && x <= trace[i].x+19 && + y >= trace[i].y-19 && y <= trace[i].y+19) + return trace[i]; + } + }; + + var preview = document.getElementById('preview'); + var previewId = document.getElementById('previewid'); + var previewCalls = document.getElementById('previewcalls'); + function previewDecision(dec) { + preview.style.left = (dec.x*scale)+'px'; + preview.style.top = (dec.y*scale+coy+15)+'px'; + preview.style.display = 'block'; + previewId.textContent = dec.d; + + previewCalls.innerHTML = dec.previewCalls; + }; + + function overResponse(ev) { + var x = (ev.clientX+window.pageXOffset)/scale; + var y = (ev.clientY+window.pageYOffset-coy)/scale; + + return (x >= response.x-(response.width/2) + && x <= response.x+(response.width/2) + && y >= response.y-19 && y <= response.y+19); + }; + + decorateTrace(); + + var bg = new Image(3138, 2184); + + function drawMap() { + var ctx = canvas.getContext("2d"); + + ctx.save(); + ctx.scale(1/scale, 1/scale); + ctx.fillStyle = '#ffffff'; + ctx.fillRect(0, 0, 3138, 2184); + ctx.restore(); + + ctx.drawImage(bg, 0, 0); + drawTrace(); + }; + + bg.onload = function() { + canvas.getContext("2d").scale(scale, scale); + drawMap(scale); + + canvas.onmousemove = function(ev) { + if (findDecision(ev)) { + canvas.style.cursor = 'pointer'; + previewDecision(findDecision(ev)); + } + else { + preview.style.display = 'none'; + if (overResponse(ev)) + canvas.style.cursor = 'pointer'; + else + canvas.style.cursor = 'default'; + } + }; + + canvas.onclick = function(ev) { + var dec = findDecision(ev); + if (dec) { + detailPanels.setDecision(dec); + detailPanels.show('decision'); + } else if (overResponse(ev)) { + detailPanels.show('response'); + } + }; + + document.getElementById('zoomin').onclick = function() { + scale = scale*2; + canvas.getContext("2d").scale(2, 2); + drawMap(); + }; + + document.getElementById('zoomout').onclick = function() { + scale = scale/2; + canvas.getContext("2d").scale(0.5, 0.5); + drawMap(); + }; + }; + + bg.onerror = function() { + alert('Failed to load background image.'); + }; + + bg.src = 'static/map.png'; +}; diff --git a/web/api/webmachine/www/blogs.html b/web/api/webmachine/www/blogs.html new file mode 100644 index 0000000..a45106e --- /dev/null +++ b/web/api/webmachine/www/blogs.html @@ -0,0 +1,67 @@ + + + + + + + + + other writing about Webmachine + + +
    +

    webmachine

    + +
    +

    What else can I read?

    + +

    +In addition to the documentation on this site and the source code on bitbucket, some useful Webmachine writing can be found on blogs and elsewhere. A small sampling of these is here: +

    +

    +The restful way blog is almost entirely dedicated to Webmachine. +

    +

    +The BeerRiot blog contains multiple topics, including a wealth of examples and discussion of Webmachine. +

    +

    +The blog of Andy Gross has some +useful hints and tutorials. +

    +

    +Paul Mineiro posted about +dynamically loading webmachine resources. +

    +

    +Kevin Smith +teaches an excellent Erlang training class which often culminates with +development of applications in Webmachine. +

    +

    +There is a fairly low traffic +mailing list +for technical discussion of Webmachine. +

    +
    + +
    + + + + + + + diff --git a/web/api/webmachine/www/contact.html b/web/api/webmachine/www/contact.html new file mode 100644 index 0000000..24d0b05 --- /dev/null +++ b/web/api/webmachine/www/contact.html @@ -0,0 +1,48 @@ + + + + + + + + + Webmachine contact information + + +
    +

    webmachine

    + +
    +

    Webmachine contact information

    + +

    +Have questions about Webmachine that aren't answered here? We'd love +to hear from you. +

    +

    +Subscribers to the Webmachine mailing list include both users and the core development team. +

    + +
    + +
    + + + + + + + diff --git a/web/api/webmachine/www/css/style-1c.css b/web/api/webmachine/www/css/style-1c.css new file mode 100644 index 0000000..80ea59b --- /dev/null +++ b/web/api/webmachine/www/css/style-1c.css @@ -0,0 +1,103 @@ +/* global reset */ +*{ margin: 0; padding: 0; }* +:focus, :active { outline: 0; } + +body { font: .9em Georgia, "Times New Roman", Sans-Serif; background: #fff url(../images/bg.gif) repeat-x; color: #333; } +a { color: #3333FF; text-decoration: none; } +img { border: 0; } +h1 { float: left; margin: 20px 0 50px; font-size: 4em; color: #fff; } +h2 { font-size: 2.4em; font-weight: normal; margin: 0 0 20px; } +h2 a:hover { background: #3333FF; color: #fff; } +.hr { color: #ccc; } +p { margin: 5px 0 15px; line-height: 1.6em; } +#content { margin: 0 auto; width: 900px; } +#top { float: right; margin: 38px 0 30px 0;} + #top li { list-style: none; display: inline; } + #top li a { float: left; padding: 6px 20px; margin: 3px 2px 0 0; color: #3333FF; } + #top li a.current { color: #fff; background: #3333FF; } + #top li a:hover { background: #808080; color: #fff; } +#intro { clear: both; padding: 15px 0 1px 20px; border: 1px solid #dedede; font-size: 1.3em; background: #eee; margin: 0 0 30px; } +#left { float: left; width: 830px; margin: 0 0 15px; } +#right { float: right; width: 0px; } +#right h3 { border-bottom: 1px solid #ccc; margin: 0 0 10px; } +#right img { margin: 0 3px 3px 0; border: 2px solid #eee; padding: 2px; } +#right li { list-style: none; } + #right li a { display: block; border-bottom: 1px solid #ccc; padding: 5px 5px; } +#footer { clear: both; padding: 15px 0; border-top: 1px solid #ccc; } + #r { float: right; } +dt { font-weight:bold; } +dd { margin: 0.5em 0 0.5em 1em; } + +/* table */ + +.fwf { + font: 12px "Courier"; + color: #111; +} + +.lhcol { + width: 200px; +} + +.x_check { + padding-left:3px; + font: 12px "Courier"; +} + +table { + width: 100%; + padding: 0; + border-spacing: 0px; + border-collapse: collapse; + margin: 5px; + margin-bottom: 15px; +} + +th { + font: bold 12px "Georgia", Verdana, Arial, Helvetica, sans-serif; + /* color: #4f6b72; */ + border: 1px solid #999; + letter-spacing: 2px; + text-transform: uppercase; + text-align: left; + padding: 6px 6px 6px 12px; + background: #ddd; +} + +th.nobg { + border-top: 0; + border-left: 0; + border-right: 1px solid #999; + background: none; +} + +td { + border: 1px solid #999; + background: #fff; + padding: 6px 6px 6px 12px; + vertical-align: top; + font: inherit; + color: #222; +} + + +td.alt { + background: #F5FAFA; + color: #797268; +} + +th.spec { + border-left: 1px solid #999; + border-top: 0; + background: #fff url(images/bullet1.gif) no-repeat; + font: bold 10px "Trebuchet MS", Verdana, Arial, Helvetica, sans-serif; +} + +th.specalt { + border-left: 1px solid #999; + border-top: 0; + background: #f5fafa url(images/bullet2.gif) no-repeat; + font: bold 10px "Trebuchet MS", Verdana, Arial, Helvetica, sans-serif; + color: #797268; +} + diff --git a/web/api/webmachine/www/css/style.css b/web/api/webmachine/www/css/style.css new file mode 100755 index 0000000..43eda64 --- /dev/null +++ b/web/api/webmachine/www/css/style.css @@ -0,0 +1,103 @@ +/* global reset */ +*{ margin: 0; padding: 0; }* +:focus, :active { outline: 0; } + +body { font: .9em Georgia, "Times New Roman", Sans-Serif; background: #fff url(../images/bg.gif) repeat-x; color: #333; } +a { color: #3333FF; text-decoration: none; } +img { border: 0; } +h1 { float: left; margin: 20px 0 50px; font-size: 4em; color: #fff; } +h2 { font-size: 2.4em; font-weight: normal; margin: 0 0 20px; } +h2 a:hover { background: #3333FF; color: #fff; } +.hr { color: #ccc; } +p { margin: 5px 0 15px; line-height: 1.6em; } +#content { margin: 0 auto; width: 900px; } +#top { float: right; margin: 38px 0 30px 0;} + #top li { list-style: none; display: inline; } + #top li a { float: left; padding: 6px 20px; margin: 3px 2px 0 0; color: #3333FF; } + #top li a.current { color: #fff; background: #3333FF; } + #top li a:hover { background: #808080; color: #fff; } +#intro { clear: both; padding: 15px 0 1px 20px; border: 1px solid #dedede; font-size: 1.3em; background: #eee; margin: 0 0 30px; } +#left { float: left; width: 550px; margin: 0 0 15px; } +#right { float: right; width: 280px; } +#right h3 { border-bottom: 1px solid #ccc; margin: 0 0 10px; } +#right img { margin: 0 3px 3px 0; border: 0 solid #eee; padding: 2px; } +#right li { list-style: none; } + #right li a { display: block; border-bottom: 1px solid #ccc; padding: 5px 5px; } +#footer { clear: both; padding: 15px 0; border-top: 1px solid #ccc; } + #r { float: right; } +dt { font-weight:bold; } +dd { margin: 0.5em 0 0.5em 1em; } + +/* table */ + +.fwf { + font: 12px "Courier"; + color: #111; +} + +.lhcol { + width: 200px; +} + +.x_check { + padding-left:3px; + font: 12px "Courier"; +} + +table { + width: 100%; + padding: 0; + border-spacing: 0px; + border-collapse: collapse; + margin: 5px; + margin-bottom: 15px; +} + +th { + font: bold 12px "Georgia", Verdana, Arial, Helvetica, sans-serif; + /* color: #4f6b72; */ + border: 1px solid #999; + letter-spacing: 2px; + text-transform: uppercase; + text-align: left; + padding: 6px 6px 6px 12px; + background: #ddd; +} + +th.nobg { + border-top: 0; + border-left: 0; + border-right: 1px solid #999; + background: none; +} + +td { + border: 1px solid #999; + background: #fff; + padding: 6px 6px 6px 12px; + vertical-align: top; + font: inherit; + color: #222; +} + + +td.alt { + background: #F5FAFA; + color: #797268; +} + +th.spec { + border-left: 1px solid #999; + border-top: 0; + background: #fff url(images/bullet1.gif) no-repeat; + font: bold 10px "Trebuchet MS", Verdana, Arial, Helvetica, sans-serif; +} + +th.specalt { + border-left: 1px solid #999; + border-top: 0; + background: #f5fafa url(images/bullet2.gif) no-repeat; + font: bold 10px "Trebuchet MS", Verdana, Arial, Helvetica, sans-serif; + color: #797268; +} + diff --git a/web/api/webmachine/www/debugging.html b/web/api/webmachine/www/debugging.html new file mode 100644 index 0000000..0762f3d --- /dev/null +++ b/web/api/webmachine/www/debugging.html @@ -0,0 +1,292 @@ + + + + + + + + + Webmachine debugging + + +
    +

    webmachine

    + +
    +

    Webmachine debugging

    + +

    +Having trouble with your webmachine resource? Try debugging it with +the webmachine trace utility! +

    + +

    Basic Use

    + +

    +To get started, first change your resource's init/1 +function to return {trace, Path} instead of +ok. For example: +

    + +
    +init(Config) ->
    +   {{trace, "/tmp"}, Config}.  %% debugging code
    +   %%{ok, Config}.             %% regular code
    +
    + +

    +Rebuild and reload the module, then in your webmachine application's +shell, type: +

    + +
    +wmtrace_resource:add_dispatch_rule("wmtrace", "/tmp").
    +
    + +

    +Now issue the HTTP request that you're trying to debug. Once it has +finished, point your browser at http://YOUR_HOST/wmtrace/. +You'll see one or more trace files available for inspection. +Click on one of them to navigate to the trace inspection utility, +which will look something like this: +

    + +

    Basic Trace

    + +

    +The example above is a trace of a resource that responded to a GET of +the root path (as you can see in the Detail Panel), and ran +all the way to a 200 response code (as you can see in the Decision +Graph). +

    + +

    +The graph starts small, so you can get a quick view of the path your +resource took through it. You can zoom in and out of the Decision +Graph by using the Zoom Controls. +

    + +

    +The path your resource took through the graph is highlighted with a +dark grey line. Hovering your mouse over the outlined decision points +along that line will pop up a tool tip with the name of the decision, +and the names of the functions in your resource that were called at +that point. Clicking on a decision will flip the Detail +Panel to the Decision Tab, where information about that +decision will be displayed. +

    +

    +If your resource traversed the graph all the way to one of the +standard, non-error return codes, the box for that return code will be +outlined. If your resource instead returned a non-standard or error +code, a red circle will be drawn next to the last decision point your +resource reached. Clicking on either of these types of endpoints will +flip the Detail Panel to the Response Tab, where +information about the response will be displayed. +

    +

    +The Detail Panel has three tabs: Request (labeled +Q), Response (labeled R), and Decision (labeled +D). Clicking each of these will show information about the +request, response, or current decision, respectively. +

    + +

    Detail Panel Request Tab

    + +

    +The Request Tab shows details about what the client +requested. The method and path are displayed at the top, headers +below that, and body (if available) at the bottom. +

    + +

    Detail Panel Response Tab

    + +

    +The Response Tab shows details about how your resource +responded. The response code is displayed at the top, headers below +that, and body (if available) at the bottom. +

    + +

    Detail Panel Decision Tab

    + +

    +The Decision Tab shows details about the currently selected +decision. The decision's name is displayed in a dropdown at the top +(changing this dropdown or clicking on a decision in the graph will +change which decision is displayed). The list of the functions called +in the resource's module is displayed in a dropdown below the +decision. The arguments with which the function was called are +displayed just below the function's name, and the return value of the +function is displayed at the bottom of the panel. +

    +

    +The Detail Panel can be resized by clicking and dragging the +tabs or the dark grey border to the left or right. Clicking the +border will toggle the panel's visibility. +

    + +

    Configuration Details

    + +

    +The Webmachine trace tool is divided into two parts: one produces the +trace logs, while the other produces the visualization. +

    + +

    Trace Log Production Configuration

    + +

    +You may configure the path under which trace files are stored by +specifying that path as the Path part of your resource +module's init/1 return value. Every time a request is +made to that resource, a new trace file will be created in the +specified directory. +

    +

    +Warning: Trace files can be large. It is +advised that you do not leave tracing enabled on a +large-content-producing or high-hit-rate resource. +

    +

    +The path may be either absolute: +

    +
    +init(Config) ->
    +   {{trace, "/tmp/traces"}, Config}. %% absolute path /tmp/traces
    +
    + +

    or relative to your application's root:

    + +

    +

    +init(Config) ->
    +   {{trace, "traces"}, Config}. %% "traces" directory in application's root
    +
    +

    + +

    Trace Viewer Configuration

    + +

    +The viewer is configured by its entry in the +dispatch list. +Two functions make modifying that entry easy: +wmtrace_resource:add_dispatch_rule/2 +and wmtrace_resource:remove_dispatch_rules/0. +

    +

    +Call add_dispatch_rule/2 with the HTTP-exported path, and +the path to the trace files. For example, to expose the viewer at +http://YOUR_HOST/dev/wmtrace/ and point it at the trace +files in /tmp/traces, type in your application's erlang +shell: +

    + +
    +wmtrace_resource:add_dispatch_rule("dev/wmtrace", "/tmp/traces").
    +
    + +

    +If you know that you always want the viewer enabled and configured in +a specific way, you can also add a line like the following to your +application's dispatch list: +

    + +
    +{["dev", "wmtrace", '*'], wmtrace_resource, [{trace_dir, "/tmp/traces"}]}
    +
    + +

    +To disable all trace viewer resources at any point, just execute +wmtrace_resource:remove_dispatch_rules/0 in your +application's erlang shell. +

    + +

    Trace Log Format

    + +

    +The trace file is fairly straightforward, if you want to read it with +less: +

    + +
    • {decision, X}. indicates that a decision point was reached + +
    • {attempt, Module, Function, Args}. indicates that a call to Module:Function(Args) was made. +
    • {result, Module, Function, Result}. indicates that the call to Module:Function(Args) returned Result . + +
    • {not_expored, Module, Function, Args}. indicates that Module:Function(Args) would have been called, but it was not exported (or not defined) by the module +
    + +

    +The format should be such that a file:consult/1 will +give you a list of the lines as erlang terms. +

    + +

    Special Handling for Funs and Pids

    + +

    +Funs and Pids don't roundtrip through file serialization very well +(file:consult/1 will blow up on a fun or pid written to a +file with io:format("~p", [FunOrPid])). To deal with +this, the trace logger prints a recognizable tuple translation instead +of the fun or pid. +

    + +

    Fun Translation

    + +

    +Funs you might see in Erlang source as fun io:format/2 +will appear in a trace log as: +

    + +
    +{'WMTRACE_ESCAPED_FUN',[{module,io},
    +                        {name,format},
    +                        {arity,2},
    +                        {type,external}]}
    +
    + +

    +Those that would be in Erlang source as fun() -> ok end + will appear in a trace log as: +

    + +
    +{'WMTRACE_ESCAPED_FUN',[{module,sampletrace_resource},
    +                        {name,'-to_html/2-fun-0-'},
    +                        {arity,0},
    +                        {type,local}]}
    +
    + +

    Pid Translation

    + +

    +Pids are simply logged in a marked tuple, after being run through +erlang:pid_to_list/1: +

    + +
    +{'WMTRACE_ESCAPED_PID',"<0.74.0>"}
    +
    + +
    + +
    + + + + + + + diff --git a/web/api/webmachine/www/diagram.html b/web/api/webmachine/www/diagram.html new file mode 100644 index 0000000..f704d14 --- /dev/null +++ b/web/api/webmachine/www/diagram.html @@ -0,0 +1,57 @@ + + + + + + + + + Webmachine decision flow + + +
    +

    webmachine

    + +
    +

    Webmachine decision flow

    + +

    +This diagram is illustrative of the flow of processing that a +webmachine resource goes through +from inception to response. +

    +

    +Version 1 of this diagram, from Alan Dean, was the inspiration for +webmachine_decision_core. Versions 2 and 3 were created in +collaboration between Alan Dean and Justin Sheehy. +

    +

    +A copy of v3 is found in the webmachine source tree for convenience. +

    +

    +http diagram v3 +

    + +
    + +
    + + + + + + + diff --git a/web/api/webmachine/www/dispatcher.html b/web/api/webmachine/www/dispatcher.html new file mode 100644 index 0000000..8e338bd --- /dev/null +++ b/web/api/webmachine/www/dispatcher.html @@ -0,0 +1,121 @@ + + + + + + + + + Webmachine request dispatching + + +
    +

    webmachine

    + +
    +

    Webmachine request dispatching

    + +

    +This page describes the configuration of URI dispatch to resources +in a webmachine application. The dispatch map data structure is a list +of 3-tuples, where each entry is of the form {pathspec, resource, +args}. The first pathspec in the list that matches the URI for a +request will cause the corresponding resource to be used in handling +that request. + +

    +

    + +A pathspec is a list of pathterms. A pathterm is any of +[string,atom,star] where star is just the atom of "*". The +pathspec-matching is done by breaking up the request URI into tokens +via the "/" separator and matching those tokens against the +pathterms. A string pathterm will match a token if the token is equal +to that string. A non-star atom will match any single token. The star +atom (* in single quotes) will match any number of tokens, but may +only be present as the last pathterm in a pathspec. If all tokens are +matched and all pathterms are used, then the pathspec matches. The +tokens used are available in wrq:path_tokens(ReqData) +in the resource functions. + +

    +

    + +Any atom pathterms that were used in a match will cause a binding in +the path_info element of the request's +ReqData. If +there was a foo atom that matched the token +"bar", then wrq:path_info(foo, ReqData) will +return "bar" inside the resource calls, and in any case +wrq:path_info(ReqData) will return a Dict term with all +the bindings, accessible via the dict standard library +module. If there was a star pathterm in the pathspec, then +wrq:disp_path(ReqData) in a resource function will return +the URI portion that was matched by the star. + +

    +

    + +The resource is an atom identifying a +resource that +should handle a matching request. It will have the args +(which must be a list) passed to its init function before request +handling begins. + +

    +

    + +In the default directory structure for a new webmachine application, +the dispatch terms will be in file:consult form in +"priv/dispatch.conf" under the application root. + +

    +

    Examples

    + +

    + +The examples below are taken from +Justin Sheehy's slide at Erlang Factory 2009 +

    + + + + + + + + + + +
    Dispatch RuleURLwrq:disp_pathwrq:pathwrq:path_infowrq:path_tokens
    {["a"], some_resource, []}/a"""/a"[][]
    {["a", '*'], some_resource, []}/a"""/a"[][]
    {["a", '*'], some_resource, []}/a/b/c"b/c""/a/b/c"[]["b", "c"]
    {["a", foo], some_resource, []}/a/b"""/a/b"[{foo, "b"}][]
    {["a", foo, '*'], some_resource, []}/a/b"""/a/b"[{foo, "b"}][]
    {["a", foo, '*'], some_resource, []}/a/b/c/d"c/d""/a/b/c/d"[{foo, "b"}]["c", "d"]
    + +

    Query strings are easy too:

    + +
    • Given rule: {["a", foo, '*'], some_resource, []} +
    • And URL: /a/b/c/d?fee=ah&fie=ha +
    • Then wrq:get_qs_value("fie",ReqData) -> "ha" +
    + +
    + +
    + + + + + + + diff --git a/web/api/webmachine/www/docs.html b/web/api/webmachine/www/docs.html new file mode 100644 index 0000000..8d1eb11 --- /dev/null +++ b/web/api/webmachine/www/docs.html @@ -0,0 +1,58 @@ + + + + + + + + + Webmachine documentation + + + + + + + + + + diff --git a/web/api/webmachine/www/example_resources.html b/web/api/webmachine/www/example_resources.html new file mode 100644 index 0000000..5602202 --- /dev/null +++ b/web/api/webmachine/www/example_resources.html @@ -0,0 +1,250 @@ + + + + + + + + + Webmachine examples + + +
    +

    webmachine

    + +
    +

    Webmachine examples

    + +

    +The simplest possible example is the one produced via the +quickstart. +

    +

    +For an example of a read/write filesystem server showing several +interesting features and supporting GET, PUT, DELETE, and POST, see +demo_fs_resource. +

    +

    +For a very simple resource demonstrating content negotiation, basic +auth, and some caching headers, see +webmachine_demo_resource. +

    +

    +Some example code based on webmachine_demo_resource follows. +

    +

    +The simplest working resource could export only one function in +addition to init/1: +

    + +
    +-module(webmachine_demo_resource).
    +-export([init/1, to_html/2]).
    +-include_lib("webmachine/include/webmachine.hrl").
    +
    +init([]) -> {ok, undefined}.
    +
    +to_html(ReqData, Context) -> {"Hello, new world", ReqData, Context}.
    +
    + +

    +That's really it -- a working webmachine resource. That resource will +respond to all valid GET requests with the exact same response. +

    +

    +Many interesting bits of HTTP are handled automatically by +Webmachine. For instance, if a client sends a request to that trivial +resource with an Accept header that does not allow for a text/html +response, they will receive a 406 Not Acceptable. +

    +

    +Suppose I wanted to serve a plaintext client as well. I could note +that I provide more than just HTML: +

    + +
    +content_types_provided(ReqData, Context) ->
    +   {[{"text/html", to_html},{"text/plain",to_text}], ReqData, Context}.
    +
    + +

    +I already have my HTML representation produced, so I add a text one: +(and while I'm at it, I'll show that it's trivial to produce dynamic content as well) +

    + +
    +to_text(ReqData, Context) ->
    +    Path = wrq:disp_path(ReqData),
    +    Body = io_lib:format("Hello ~s from webmachine.~n", [Path]),
    +    {Body, ReqData, Context}.
    +
    + +

    +Now that this resource provides multiple media types, it automatically performs conneg: +

    + +
    +$ telnet localhost 8000
    +Trying 127.0.0.1...
    +Connected to localhost.
    +Escape character is '^]'.
    +GET /demo/a/resource/path HTTP/1.1
    +Accept: text/plain
    +
    +HTTP/1.1 200 OK
    +Vary: Accept
    +Server: MochiWeb/1.1 WebMachine/0.97
    +Date: Sun, 15 Mar 2009 02:54:02 GMT
    +Content-Type: text/plain
    +Content-Length: 39
    +
    +Hello a/resource/path from webmachine.
    +
    + +

    +What about authorization? Webmachine resources default to assuming the +client is authorized, but that can easily be overridden. Here's an +overly simplistic but illustrative example: +

    + +
    +is_authorized(ReqData, Context) ->
    +    case wrq:disp_path(ReqData) of
    +        "authdemo" -> 
    +            case wrq:get_req_header("authorization", ReqData) of
    +                "Basic "++Base64 ->
    +                    Str = base64:mime_decode_to_string(Base64),
    +                    case string:tokens(Str, ":") of
    +                        ["authdemo", "demo1"] ->
    +                            {true, ReqData, Context};
    +                        _ ->
    +                            {"Basic realm=webmachine", ReqData, Context}
    +                    end;
    +                _ ->
    +                    {"Basic realm=webmachine", ReqData, Context}
    +            end;
    +        _ -> {true, ReqData, Context}
    +    end.
    +
    + +

    +With that function in the resource, all paths except +/authdemo from this resource's root are authorized. +For that one path, +the UA will be asked to do basic authorization with the user/pass of +authdemo/demo1. It should go without saying that this isn't quite the +same function that we use in our real apps, but it is nice and simple. +

    +

    +If you've generated the application from the +quickstart, make sure +you've added this line to your dispatch.conf file: +

    +
    +{["demo", '*'], mywebdemo_resource, []}.
    +
    +

    +Now you can point your browser at +http://localhost:8000/demo/authdemo with the demo app running: +

    + +
    +$ curl -v http://localhost:8000/demo/authdemo
    +> GET /demo/authdemo HTTP/1.1
    +> Host: localhost:8000
    +> Accept: */*
    +> 
    +< HTTP/1.1 401 Unauthorized
    +< WWW-Authenticate: Basic realm=webmachine
    +< Server: MochiWeb/1.1 WebMachine/0.97
    +< Date: Sun, 15 Mar 2009 02:57:43 GMT
    +< Content-Length: 0
    +
    +< 
    +
    +

    +
    +$ curl -v -u authdemo:demo1 http://localhost:8000/demo/authdemo 
    +* Server auth using Basic with user 'authdemo'
    +> GET /demo/authdemo HTTP/1.1
    +> Authorization: Basic YXV0aGRlbW86ZGVtbzE=
    +> Host: localhost:8000
    +> Accept: */*
    +> 
    +< HTTP/1.1 200 OK
    +< Vary: Accept
    +< Server: MochiWeb/1.1 WebMachine/0.97
    +
    +< Date: Sun, 15 Mar 2009 02:59:02 GMT
    +< Content-Type: text/html
    +< Content-Length: 59
    +< 
    +<html><body>Hello authdemo from webmachine.
    +</body></html>
    +
    + +

    +HTTP caching support is also quite easy, with functions allowing +resources to define (e.g.) last_modified, +expires, and generate_etag. For instance, since +representations of this resource vary only by URI Path, I could use an +extremely simple entity tag unfit for most real applications but +sufficient for this example: +

    + +
    +generate_etag(ReqData, Context) -> {wrq:raw_path(ReqData), ReqData, Context}.
    +
    + +

    Similarly, here's a trivial expires rule:

    + +
    +expires(ReqData, Context) -> {{{2021,1,1},{0,0,0}}, ReqData, Context}.
    +
    + +

    +And now the response from our earlier request is appropriately tagged: +

    + +
    +HTTP/1.1 200 OK
    +Vary: Accept
    +Server: MochiWeb/1.1 WebMachine/0.97
    +Expires: Fri, 01 Jan 2021 00:00:00 GMT
    +ETag: /demo/authdemo
    +Date: Sun, 15 Mar 2009 02:59:02 GMT
    +Content-Type: text/html
    +Content-Length: 59
    +
    +<html><body>Hello authdemo from webmachine.
    +</body></html>
    +
    + +

    +For more details, read the source of the resources linked at the top +of this page. +

    + +
    + +
    + + + + + + + diff --git a/web/api/webmachine/www/favicon.ico b/web/api/webmachine/www/favicon.ico new file mode 100644 index 0000000..3fef9cd Binary files /dev/null and b/web/api/webmachine/www/favicon.ico differ diff --git a/web/api/webmachine/www/images/WM200-crop.png b/web/api/webmachine/www/images/WM200-crop.png new file mode 100644 index 0000000..a53495a Binary files /dev/null and b/web/api/webmachine/www/images/WM200-crop.png differ diff --git a/web/api/webmachine/www/images/basho-landscape.gif b/web/api/webmachine/www/images/basho-landscape.gif new file mode 100644 index 0000000..19fb79d Binary files /dev/null and b/web/api/webmachine/www/images/basho-landscape.gif differ diff --git a/web/api/webmachine/www/images/basic-trace-decision-tab.png b/web/api/webmachine/www/images/basic-trace-decision-tab.png new file mode 100644 index 0000000..d385f0a Binary files /dev/null and b/web/api/webmachine/www/images/basic-trace-decision-tab.png differ diff --git a/web/api/webmachine/www/images/basic-trace-labeled.png b/web/api/webmachine/www/images/basic-trace-labeled.png new file mode 100644 index 0000000..3079e3e Binary files /dev/null and b/web/api/webmachine/www/images/basic-trace-labeled.png differ diff --git a/web/api/webmachine/www/images/basic-trace-request-tab.png b/web/api/webmachine/www/images/basic-trace-request-tab.png new file mode 100644 index 0000000..38d7999 Binary files /dev/null and b/web/api/webmachine/www/images/basic-trace-request-tab.png differ diff --git a/web/api/webmachine/www/images/basic-trace-response-tab.png b/web/api/webmachine/www/images/basic-trace-response-tab.png new file mode 100644 index 0000000..7d21f54 Binary files /dev/null and b/web/api/webmachine/www/images/basic-trace-response-tab.png differ diff --git a/web/api/webmachine/www/images/bg.gif b/web/api/webmachine/www/images/bg.gif new file mode 100755 index 0000000..41e5559 Binary files /dev/null and b/web/api/webmachine/www/images/bg.gif differ diff --git a/web/api/webmachine/www/images/blankbox.gif b/web/api/webmachine/www/images/blankbox.gif new file mode 100644 index 0000000..77d42b6 Binary files /dev/null and b/web/api/webmachine/www/images/blankbox.gif differ diff --git a/web/api/webmachine/www/images/chash.gif b/web/api/webmachine/www/images/chash.gif new file mode 100644 index 0000000..a9e7224 Binary files /dev/null and b/web/api/webmachine/www/images/chash.gif differ diff --git a/web/api/webmachine/www/images/easy-ops.gif b/web/api/webmachine/www/images/easy-ops.gif new file mode 100644 index 0000000..34f0d84 Binary files /dev/null and b/web/api/webmachine/www/images/easy-ops.gif differ diff --git a/web/api/webmachine/www/images/gossip4.gif b/web/api/webmachine/www/images/gossip4.gif new file mode 100644 index 0000000..24adcf5 Binary files /dev/null and b/web/api/webmachine/www/images/gossip4.gif differ diff --git a/web/api/webmachine/www/images/halfblankbox.gif b/web/api/webmachine/www/images/halfblankbox.gif new file mode 100644 index 0000000..d8e18e6 Binary files /dev/null and b/web/api/webmachine/www/images/halfblankbox.gif differ diff --git a/web/api/webmachine/www/images/http-headers-status-v3.png b/web/api/webmachine/www/images/http-headers-status-v3.png new file mode 100644 index 0000000..2313160 Binary files /dev/null and b/web/api/webmachine/www/images/http-headers-status-v3.png differ diff --git a/web/api/webmachine/www/images/more.gif b/web/api/webmachine/www/images/more.gif new file mode 100644 index 0000000..11b31d7 Binary files /dev/null and b/web/api/webmachine/www/images/more.gif differ diff --git a/web/api/webmachine/www/images/site.gif b/web/api/webmachine/www/images/site.gif new file mode 100755 index 0000000..9fc631c Binary files /dev/null and b/web/api/webmachine/www/images/site.gif differ diff --git a/web/api/webmachine/www/images/splash250.gif b/web/api/webmachine/www/images/splash250.gif new file mode 100644 index 0000000..380dc2e Binary files /dev/null and b/web/api/webmachine/www/images/splash250.gif differ diff --git a/web/api/webmachine/www/images/vclock.gif b/web/api/webmachine/www/images/vclock.gif new file mode 100644 index 0000000..90bba97 Binary files /dev/null and b/web/api/webmachine/www/images/vclock.gif differ diff --git a/web/api/webmachine/www/index.html b/web/api/webmachine/www/index.html new file mode 100755 index 0000000..41cfe80 --- /dev/null +++ b/web/api/webmachine/www/index.html @@ -0,0 +1,73 @@ + + + + + + + + + Webmachine - software shaped like the Web + + +
    +

    webmachine

    + + +
    +

    Webmachine is not much like the Web frameworks you're used to. You can call Webmachine a REST toolkit if you like, and we won't argue with you.

    +
    +
    +

    +Webmachine is an application layer that adds HTTP semantic awareness on top of the excellent bit-pushing and HTTP syntax-management provided by +mochiweb, +and provides a simple and clean way to connect that to your +application's behavior. +

    +

    +A Webmachine application is a set of resources, each of which is a set of +functions over the state of the resource. We really mean functions here, not object-methods, infinite-server-loops, or any other such construction. This aspect of Webmachine is one of the reasons why Webmachine applications are relatively easy to understand and extend. +

    +

    +These functions give you a place to define the representations and other Web-relevant properties of your application's resources -- with the emphasis that the first-class things on the Web are resources and that their essential properties of interaction are already quite well defined and usefully constrained. +

    +

    +For most Webmachine applications, most of the functions are quite small and isolated. One of the nice effects of this is that a quick reading of a resource will give you an understanding of the application, its Web behavior, and the relationship between them. Since these functions are usually referentially transparent, Webmachine applications can be quite easy to test. There's no need for mock objects, fake database connections, or any other wastes of time when you can write tests against each component of your application in terms of the input and output to various functions. +

    +

    +We believe that by giving Web developers a +system with conventions that +directly map to HTTP +and REST, we help them to write and extend Web applications quickly while not dictating the shape of the rest of their application. The resulting applications are straightforward to examine and maintain, and have very easily understood HTTP semantics. +

    +
    + + +
    + + + + + + diff --git a/web/api/webmachine/www/intros.html b/web/api/webmachine/www/intros.html new file mode 100644 index 0000000..d0ddb2c --- /dev/null +++ b/web/api/webmachine/www/intros.html @@ -0,0 +1,62 @@ + + + + + + + + + introductions to Webmachine + + +
    +

    webmachine

    + +
    +

    How do I get started?

    + +

    +If you want to jump in and start coding right away, the +quickstart is the way to go. +

    +

    +If you would prefer to watch a narrated slideshow introduction, this is roughly similar to the presentation that was given at +Bay Area Erlang Factory 2009: +

    + + + +

    +Some blogs also have posts that can serve as +useful introductions to Webmachine, if you prefer to start that way. +

    + +

    +No matter how you get started, you'll probably want to come back and +read more documentation once you get up and running. +

    + + +

    + +
    + + + + + + + diff --git a/web/api/webmachine/www/mechanics.html b/web/api/webmachine/www/mechanics.html new file mode 100644 index 0000000..cec82ba --- /dev/null +++ b/web/api/webmachine/www/mechanics.html @@ -0,0 +1,108 @@ + + + + + + + + + Webmachine mechanics + + +
    +

    webmachine

    + +
    +

    How does this Webmachine thing work, anyway?

    + +

    +This page describes the basic mechanics of Webmachine from the point +of view of a single incoming HTTP Request, documenting the behavior of +Webmachine through to the HTTP Response. +

    +

    +(This is a bit different from what you might get with a "Web +Framework" as we're not going to talk about MVC, ORMs, or anything +else about the rest of the shape of your application. We believe that +you know better than we do how to structure your own app -- +Webmachine's job is to help you make sure that your app's presence on +the Web is well-behaved and well-structured.) +

    +

    +When a request is initially received by Webmachine it is handled by the +dispatcher. +If the dispatcher does not find a matching resource then it will +immediately respond with a 404 Not Found. If a match is found then a +request data record +is created and the matching +resource is +kicked off via its init/1 function. +

    +

    +The resource then flows through the decision core, which is +effectively just running the request through the +HTTP flowchart. At +each decision point (diamond) in the diagram, Webmachine will +determine which path to follow. In some cases it can determine the +path purely from the request data -- for instance, the path from +decision C3 depends purely on whether the client sent +an Accept header. In many cases, however, the decision +is application-specific -- the path from B10 depends on +the value the +resource module +returns from allowed_methods. Eventually the chosen path +will terminate at one of the rectangles on the diagram. At that point +Webmachine will send an appropriate HTTP response, with the headers +and body dependent on the path through the diagram and the values +returned by the resource's functions. +

    +

    +Most of the time you don't need to worry about this big diagram, +though -- just define the +resource functions +relevant to your app and Webmachine will do the rest. A +good understanding of this central mechanism in Webmachine is most +useful when +debugging your resources. +

    +

    +From the way that webmachine's decision core works, it follows that +Webmachine's HTTP behavior is transactional. Each HTTP Request is +fully received, and the resulting HTTP Response is then fully +constructed before being returned. This means that while Webmachine +is suitable for a great many Web applications it is not a good fit for +an application that will gradually or continuously stream responses +back to clients inside the context of a single HTTP Request. +

    +

    +A useful way to build Webmachine applications is often just to write a +single function such as to_html to provide the most +basic of stubs; when that function exists (or any other returned by +content_types_provided) you can produce 200 OK +responses. After that, you can easily extend your +application's Web behavior simply by filling in the other +resource functions as desired. +

    +
    + +
    + + + + + + + diff --git a/web/api/webmachine/www/quickstart.html b/web/api/webmachine/www/quickstart.html new file mode 100644 index 0000000..573faa9 --- /dev/null +++ b/web/api/webmachine/www/quickstart.html @@ -0,0 +1,77 @@ + + + + + + + + + getting started quickly with Webmachine + + +
    +

    webmachine

    + +
    +

    getting started quickly with Webmachine

    + + +

    Make sure that you have a working Erlang/OTP release, R12B5 or later.

    + +

    Get the webmachine code:

    + +

    +hg clone http://bitbucket.org/justin/webmachine/ webmachine-read-only
    +

    + +

    Build webmachine:

    + +

    +cd webmachine-read-only
    +make
    +

    + +

    Create, build, and start the skeleton resource:

    + +

    +./scripts/new_webmachine.erl mywebdemo /tmp
    +cd /tmp/mywebdemo
    +make
    +./start.sh
    +

    + +

    Take a look! Point a web browser at http://localhost:8000/

    + +

    To make this resource handle URI paths other than /, add more +dispatch terms in +/tmp/mywebdemo/priv/dispatch.conf; to make that resource to more +interesting things, modify the +resource itself +at /tmp/mywebdemo/src/mywebdemo_resource.erl.

    + +

    To learn how to do more interesting things, check out some examples or read more documentation.

    + + +
    + +
    + + + + + + + diff --git a/web/api/webmachine/www/reftrans.html b/web/api/webmachine/www/reftrans.html new file mode 100644 index 0000000..f82d7cd --- /dev/null +++ b/web/api/webmachine/www/reftrans.html @@ -0,0 +1,52 @@ + + + + + + + + + Webmachine's approach to resource functions and referential transparency + + +
    +

    webmachine

    + +
    +

    Webmachine's approach to resource functions and referential transparency

    + +

    +Webmachine goes to great lengths to help your resource functions to be as referentially transparent as possible. By "referentially transparent" we mean that given the same input {ReqData, Context} the function will return the same output {Result, ReqData, Context} and that side effects will be insignificant from the point of view of Webmachine's execution. +

    +

    +We don't try to force you into pure referential transparency; we give you as big a hole as you want via Context. As that term is application-specific, you can put database handles, server process identifiers, or anything else you like in there and we won't try to stop you. +

    +

    +However, all Webmachine really cares about is the rest of the terms. Since resource functions are generally referentially transparent at least with regard to those terms, many things are easier -- testing, debugging, and even static analysis and reasoning about your Web application. +

    +

    +Note that there is one important exception to this. The streamed body feature exists to allow resources to consume or produce request/response bodies a hunk at a time without ever having the whole thing in memory. While the continuation-passing style used in the streaming API is friendly to general functional analysis, due to the necessary side-effect of reading or writing to sockets the stream bodies cannot be treated in quite the same way as other uses of the ReqData interface. Luckily, it is easy to inspect a ReqData to see if this is the case in any individual resource or request instance. +

    +
    + +
    + + + + + + + diff --git a/web/api/webmachine/www/reqdata.html b/web/api/webmachine/www/reqdata.html new file mode 100644 index 0000000..d97d275 --- /dev/null +++ b/web/api/webmachine/www/reqdata.html @@ -0,0 +1,143 @@ + + + + + + + + + Webmachine request/response data + + +
    +

    webmachine

    + +
    +

    Webmachine request/response data

    + +

    + +This is documentation of the Webmachine Request Data API as embodied +by the "wrq" module. This module is the means by which +resources access and manipulate the state of the request they are +handling. + +

    +

    + +Given that all webmachine resource functions have this signature: + +

    + +f(ReqData, Context) -> {Result, ReqData, Context} + +

    + +we should explain in detail the ReqData input and output +parameter. This is a data structure used to represent the request +sent by the client as well as the response being built by the +resource. The wrq module is used to access the values in +the input parameter. Most functions in most resources have no need to +modify the output ReqData and can simply pass along the +one received as input. However, in some cases a resource will need to +make some update to the response other than that implied by +Result and in those cases it should use the +wrq module to build a modified ReqData from +the original one for the return value. + +

    +

    + +A couple of nonstandard types are assumed here: + +

    + + + + + + +
    TypeDescription
    string()a list() with all elements in the ASCII range
    rd()opaque record, used as the input/output ReqData
    streambody()A webmachine streamed body format
    mochiheaders()a structure used in mochiweb for HTTP header storage
    + +

    The accessors are:

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    FunctionDescription
    method(rd()) -> 'DELETE' | 'GET' | 'HEAD' | 'OPTIONS' | 'POST' | 'PUT' | 'TRACE' The HTTP method used by the client. (note that it is an atom() )
    version(rd()) -> {integer(),integer()} The HTTP version used by the client. Most often {1,1} .
    peer(rd()) -> string() The IP address of the client.
    disp_path(rd()) -> string() The "local" path of the resource URI; the part after any prefix used in dispatch configuration. Of the three path accessors, this is the one you usually want. This is also the one that will change after create_path is called in your resource.
    path(rd()) -> string() The path part of the URI -- after the host and port, but not including any query string.
    raw_path(rd()) -> string() The entire path part of the URI, including any query string present.
    path_info(atom(),rd()) -> 'undefined' | string() Looks up a binding as described in dispatch configuration.
    path_info(rd()) -> any() The dictionary of bindings as described in dispatch configuration.
    path_tokens(rd()) -> list() This is a list of string() terms, the disp_path components split by "/".
    get_req_header(string(),rd()) -> 'undefined' | string() Look up the value of an incoming request header.
    req_headers(rd()) -> mochiheaders() The incoming HTTP headers. Generally, get_req_header is more useful.
    req_body(rd()) -> 'undefined' | binary() The incoming request body, if any.
    stream_req_body(rd(),integer()) -> streambody() The incoming request body in streamed form, with hunks no bigger than the integer argument.
    get_cookie_value(string(),rd()) -> string() Look up the named value in the incoming request cookie header.
    req_cookie(rd()) -> string() The raw value of the cookie header. Note that get_cookie_value is often more useful.
    get_qs_value(string(),rd()) -> 'undefined' | string() Given the name of a key, look up the corresponding value in the query string.
    get_qs_value(string(),string(),rd()) -> string() Given the name of a key and a default value if not present, look up the corresponding value in the query string.
    req_qs(rd()) -> [{string(), string()}] The parsed query string, if any. Note that get_qs_value is often more useful.
    get_resp_header(string(),rd()) -> string() Look up the current value of an outgoing request header.
    resp_redirect(rd()) -> bool() the last value passed to do_redirect, false otherwise -- if true, then some responses will be 303 instead of 2xx where applicable
    resp_headers(rd()) -> mochiheaders() The outgoing HTTP headers. Generally, get_resp_header is more useful.
    resp_body(rd()) -> 'undefined' | binary() The outgoing response body, if one has been set. Usually, append_to_response_body is the best way to set this.
    app_root(rd()) -> string() Indicates the "height" above the requested URI that this resource is dispatched from. Typical values are "." , ".." , "../.." and so on.
    + + +

    The functions for (nondestructive) modification of rd() terms are: +

    + + + + + + + + + + + + + + + +
    FunctionDescription
    set_resp_header(string(),string(),rd()) -> rd() Given a header name and value, set an outgoing request header to that value.
    append_to_response_body(binary(),rd()) -> rd() Append the given value to the body of the outgoing response.
    do_redirect(bool(),rd()) -> rd() see resp_redirect; this sets that value.
    set_disp_path(string(),rd()) -> rd() The disp_path is the only path that can be changed during a request. This function will do so.
    set_req_body(binary(),rd()) -> rd() Replace the incoming request body with this for the rest of the processing.
    set_resp_body(binary(),rd()) -> rd() Set the outgoing response body to this value.
    set_resp_body(streambody(),rd()) -> rd() Use this streamed body to produce the outgoing response body on demand.
    set_resp_headers([{string(),string()}],rd()) -> rd() Given a list of two-tuples of {headername,value}, set those outgoing response headers.
    remove_resp_header(string(),rd()) -> rd() Remove the named outgoing response header.
    + + +
    + +
    + + + + + + + diff --git a/web/api/webmachine/www/resources.html b/web/api/webmachine/www/resources.html new file mode 100644 index 0000000..ee4c36a --- /dev/null +++ b/web/api/webmachine/www/resources.html @@ -0,0 +1,141 @@ + + + + + + + + + Webmachine resource functions + + +
    +

    webmachine

    + +
    +

    Webmachine resource functions

    + +

    All webmachine resources should include the webmachine resource library:

    + +

    + +-include_lib("webmachine/include/webmachine.hrl"). + +

    + +

    +All webmachine resources should define and export init/1, which will receive a configuration property list from the dispatcher as its argument. This function should, if successful, return {ok, Context}. Context is any term, and will be threaded through all of the other webmachine resource functions. Alternately, the resource can go into debugging mode by returning {{trace, Dir}, Context} instead -- see the tracing documentation for more information.

    + +

    All webmachine resource functions are of the signature:

    + +

    + +f(ReqData, Context) -> {Result, ReqData, Context} + +

    + + +

    +Context is an arbitrary term() that is specific to your application. Webmachine will never do anything with this term other than threading it through the various functions of your resource. This is the means by which transient application-specific request state is passed along between functions. +

    +

    +ReqData is a #wm_reqdata{} term, and is manipulated via the wrq interface. A resource function may access request data (such as header values) from the input value. If a resource function wishes to affect the response data in some way other than that implied by its return value (e.g. adding an X-Header) then it should modify the returned ReqData term accordingly. +

    +

    +The rest of this document is about the effects produced by different values in the Result term. +

    +

    +There are over 30 resource functions you can define, but any of them can be omitted as they have reasonable defaults. +

    +

    +Each function is described below, showing the default and allowed values that may be in the Result term. The default will be used if a resource does not export the function. If a function has an "X" in the "Halt" column, it also has the option of returning either of the two following special values for Result: + +

    + + + + +
    ResultEffect
    {error, Err::term()}Immediately end processing of this request, returning a 500 response code. The response body will contain the Err term.
    {halt, Code::integer()}Immediately end processing of this request, returning response code Code. It is the responsibility of the resource to ensure that all necessary response header and body elements are filled in ReqData in order to make that reponse code valid.
    + +

    +Any function which has no description is optional and the effect of its return value should be evident from examining the diagram. +

    + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +
    FunctionDefaultHaltAllowedDescription
    resource_existstrueXtrue | falseReturning non-true values will result in 404 Not Found.
    service_availabletrueXtrue | false
    is_authorizedtrueXtrue | AuthHeadIf this returns anything other than true, the response will be 401 Unauthorized. The AuthHead return value will be used as the value in the WWW-Authenticate header.
    forbiddenfalseXtrue | false
    allow_missing_postfalseXtrue | falseIf the resource accepts POST requests to nonexistent resources, then this should return true.
    malformed_requestfalseXtrue | false
    uri_too_longfalseXtrue | false
    known_content_typetrueXtrue | false
    valid_content_headerstrueXtrue | false
    valid_entity_lengthtrueXtrue | false
    options[][Header]If the OPTIONS method is supported and is used, the return value of this function is expected to be a list of pairs representing header names and values that should appear in the response.
    allowed_methods['GET', 'HEAD'][Method]If a Method not in this list is requested, then a 405 Method Not Allowed will be sent. Note that these are all-caps and are atoms. (single-quoted)
    delete_resourcefalseXtrue | falseThis is called when a DELETE request should be enacted, and should return true if the deletion succeeded.
    delete_completedtrueXtrue | falseThis is only called after a successful delete_resource call, and should return false if the deletion was accepted but cannot yet be guaranteed to have finished.
    post_is_createfalsetrue | falseIf POST requests should be treated as a request to put content into a (potentially new) resource as opposed to being a generic submission for processing, then this function should return true. If it does return true, then create_path will be called and the rest of the request will be treated much like a PUT to the Path entry returned by that call.
    create_pathundefinedPathThis will be called on a POST request if post_is_create returns true. It is an error for this function to not produce a Path if post_is_create returns true. The Path returned should be a valid URI part following the dispatcher prefix. That Path will replace the previous one in the return value of wrq:disp_path(ReqData) for all subsequent resource function calls in the course of this request.
    process_postfalseXtrue | falseIf post_is_create returns false, then this will be called to process any POST requests. If it succeeds, it should return true.
    content_types_provided [{"text/html", to_html}] [{Mediatype, Handler}] This should return a list of pairs where each pair is of the form {Mediatype, Handler} where Mediatype is a string of content-type format and the Handler is an atom naming the function which can provide a resource representation in that media type. Content negotiation is driven by this return value. For example, if a client request includes an Accept header with a value that does not appear as a first element in any of the return tuples, then a 406 Not Acceptable will be sent.
    content_types_accepted [] [{Mediatype, Handler}] This is used similarly to content_types_provided, except that it is for incoming resource representations -- for example, PUT requests. Handler functions usually want to use wrq:req_body(ReqData) to access the incoming request body.
    charsets_providedno_charsetno_charset | [{Charset, CharsetConverter}] If this is anything other than the atom no_charset, it must be a list of pairs where each pair is of the form Charset, Converter where Charset is a string naming a charset and Converter is a callable function in the resource which will be called on the produced body in a GET and ensure that it is in Charset.
    encodings_provided [{"identity", fun(X) -> X end}] [{Encoding, Encoder}] This must be a list of pairs where in each pair Encoding is a string naming a valid content encoding and Encoder is a callable function in the resource which will be called on the produced body in a GET and ensure that it is so encoded. One useful setting is to have the function check on method, and on GET requests return [{"identity", fun(X) -> X end}, {"gzip", fun(X) -> zlib:gzip(X) end}] as this is all that is needed to support gzip content encoding.
    variances [] [HeaderName] If this function is implemented, it should return a list of strings with header names that should be included in a given response's Vary header. The standard conneg headers (Accept, Accept-Encoding, Accept-Charset, Accept-Language) do not need to be specified here as Webmachine will add the correct elements of those automatically depending on resource behavior.
    is_conflictfalsetrue | falseIf this returns true, the client will receive a 409 Conflict.
    multiple_choicesfalseXtrue | falseIf this returns true, then it is assumed that multiple representations of the response are possible and a single one cannot be automatically chosen, so a 300 Multiple Choices will be sent instead of a 200.
    previously_existedfalseXtrue | false
    moved_permanentlyfalseX {true, MovedURI} | false
    moved_temporarilyfalseX {true, MovedURI} | false
    last_modifiedundefinedundefined | {{YYYY,MM,DD}, {Hour,Min,Sec}}
    expiresundefinedundefined | {{YYYY,MM,DD}, {Hour,Min,Sec}}
    generate_etagundefinedundefined | ETagIf this returns a value, it will be used as the value of the ETag header and for comparison in conditional requests.
    finish_requesttruetrue | falseThis function, if exported, is called just before the final response is constructed and sent. The Result is ignored, so any effect of this function must be by returning a modified ReqData .
    body-producing function named as a Handler by content_types_providedX Body The Body should be either an iolist() or {stream,streambody()}
    POST-processing function named as a Handler by content_types_acceptedX true
    + +

    +The above are all of the supported predefined resource functions. In addition to whichever of these a resource wishes to use, it also must export all of the functions named in the return values of the content_types_provided and content_types_accepted functions with behavior as described in the bottom two rows of the table. +

    +
    + +
    + + + + + + + diff --git a/web/api/webmachine/www/streambody.html b/web/api/webmachine/www/streambody.html new file mode 100644 index 0000000..5903d98 --- /dev/null +++ b/web/api/webmachine/www/streambody.html @@ -0,0 +1,176 @@ + + + + + + + + + Webmachine streamed bodies + + +
    +

    webmachine

    + +
    +

    Webmachine streamed bodies

    + +

    + +Webmachine allows the resource developer to handle request and +response bodies as either whole units (binary or iolist) to be handed +around at once, or else to choose to "stream" the body. + +

    +

    + +The body-handling functions are: + +

    + +
    • wrq:req_body/1 +
    • wrq:stream_req_body/2 +
    • wrq:set_resp_body/2 +
    + +

    + +The last of these, wrq:set_resp_body/2, is also called +implicitly with the return value of any content-producing function +such as to_html/2. + +

    +

    + +The first of these (req_body) is the simplest. It will +provide the whole incoming request body as a binary. (Unless the body +is too large, as set by wrq:set_max_recv_body/2 or +defaulting to 50M) For the majority of resources, this is the easiest +way to handle the incoming request body. + +

    +

    + +If a resource wants to handle the incoming request body a hunk at a +time, it may call wrq:stream_req_body/2 instead. Instead +of a binary, this produces a StreamBody structure. + +

    +

    + +(It is an error to call both wrq:req_body/1 and +wrq:stream_req_body/2 in the execution of a single +resource.) + +

    +

    + +A StreamBody is a pair of the form +{Data,Next} where Data is a binary and +Next is either the atom done signifying the +end of the body or else a 0-arity function that, when called, will +produce the "next" StreamBody structure. + +

    +

    + +The integer parameter to wrq:stream_req_body/2 indicates +the maximum size in bytes of any Hunk from the resulting +StreamBody. + +

    +

    + +When a resource provides a body to be sent in the response, it should +use wrq:set_resp_body/2. The parameter to this function +may be either an iolist, representing the entire body, or else a pair +of the form {stream, StreamBody}. + +

    +

    + +An example may make the usage of this API clearer. A complete and +working resource module using this API in both directions: + +

    +

    + +

    +-module(mywebdemo_resource).
    +-export([init/1, allowed_methods/2, process_post/2]).
    +
    +-include_lib("webmachine/include/webmachine.hrl").
    +
    +init([]) -> {ok, undefined}.
    +
    +allowed_methods(ReqData, State) -> {['POST'], ReqData, State}.
    +
    +process_post(ReqData, State) ->
    +    Body = get_streamed_body(wrq:stream_req_body(ReqData, 3), []),
    +    {true, wrq:set_resp_body({stream, send_streamed_body(Body,4)},ReqData), State}.
    +
    +send_streamed_body(Body, Max) ->
    +    HunkLen=8*Max,
    +    case Body of        
    +        <> ->
    +            io:format("SENT ~p~n",[<>]),
    +            {<>, fun() -> send_streamed_body(Rest,Max) end};
    +        _ ->
    +            io:format("SENT ~p~n",[Body]),
    +            {Body, done}
    +    end.
    +
    +get_streamed_body({Hunk,done},Acc) ->
    +    io:format("RECEIVED ~p~n",[Hunk]),
    +    iolist_to_binary(lists:reverse([Hunk|Acc]));
    +get_streamed_body({Hunk,Next},Acc) ->
    +    io:format("RECEIVED ~p~n",[Hunk]),
    +    get_streamed_body(Next(),[Hunk|Acc]).
    +
    + +

    +

    + +If you use this resource in place of the file +/tmp/mywebdemo/src/mywebdemo_resource.erl in the +quickstart setup, you should then be able +to issue curl -d '1234567890' http://127.0.0.1:8000/ on +the command line and the io:format calls will show you +what is going on. + +

    +

    + +Obviously, a realistic resource wouldn't use this API just to collect +the whole binary into memory or break one up that is already present +-- you'd use req_body and put a simple iolist into +set_resp_body instead. Also, the choices of 3 and 4 +bytes as hunk size are far from optimal for most reasonable uses. +This resource is intended only as a demonstration of the API, not as a +real-world use of streaming request/response bodies. + +

    + +
    + +
    + + + + + + +