Commits

Yuriy Netesov  committed e8c33a6 Draft

+mochi

  • Participants
  • Parent commits 41a75a6

Comments (0)

Files changed (127)

 	./rebar -v generate	
 
 protocol:
-	ads
+	ad
 ###	make run

File apps/kissbang/src/gateway_srv.erl

          terminate/2, code_change/3]).
 
 -define(SERVER, ?MODULE). 
--define(PORT, 8080).
 
 -record(state, {}).
 
 %% @end
 %%--------------------------------------------------------------------
 init([]) ->
-    client_acceptor:start_link(?PORT), 
+    {ok, Ports} = application:get_env(kissbang, gateway_ports),
+    lists:foreach(fun(Port) ->client_acceptor:start_link(Port) end, Ports), 
     {ok, #state{}}.
 
 %%--------------------------------------------------------------------

File apps/kissbang/src/kissbang.app.src

                   crypto,
                   mnesia
                  ]},
-  {mod, { kissbang_app, []}},
-  {env, []}
+  {mod, {kissbang_app, []}},
+  {env, [{gateway_ports, [8080, 8081]}]}
  ]}.

File extern/meck/ebin/meck.beam

Binary file modified.

File extern/meck/ebin/meck_cover.beam

Binary file modified.

File extern/meck/ebin/meck_mod.beam

Binary file modified.

File extern/mochiweb/.git/HEAD

+ref: refs/heads/master

File extern/mochiweb/.git/config

+[core]
+	repositoryformatversion = 0
+	filemode = true
+	bare = false
+	logallrefupdates = true
+[remote "origin"]
+	fetch = +refs/heads/*:refs/remotes/origin/*
+	url = https://github.com/mochi/mochiweb.git
+[branch "master"]
+	remote = origin
+	merge = refs/heads/master

File extern/mochiweb/.git/description

+Unnamed repository; edit this file 'description' to name the repository.

File extern/mochiweb/.git/hooks/applypatch-msg.sample

+#!/bin/sh
+#
+# An example hook script to check the commit log message taken by
+# applypatch from an e-mail message.
+#
+# The hook should exit with non-zero status after issuing an
+# appropriate message if it wants to stop the commit.  The hook is
+# allowed to edit the commit message file.
+#
+# To enable this hook, rename this file to "applypatch-msg".
+
+. git-sh-setup
+test -x "$GIT_DIR/hooks/commit-msg" &&
+	exec "$GIT_DIR/hooks/commit-msg" ${1+"$@"}
+:

File extern/mochiweb/.git/hooks/commit-msg.sample

+#!/bin/sh
+#
+# An example hook script to check the commit log message.
+# Called by "git commit" with one argument, the name of the file
+# that has the commit message.  The hook should exit with non-zero
+# status after issuing an appropriate message if it wants to stop the
+# commit.  The hook is allowed to edit the commit message file.
+#
+# To enable this hook, rename this file to "commit-msg".
+
+# Uncomment the below to add a Signed-off-by line to the message.
+# Doing this in a hook is a bad idea in general, but the prepare-commit-msg
+# hook is more suited to it.
+#
+# SOB=$(git var GIT_AUTHOR_IDENT | sed -n 's/^\(.*>\).*$/Signed-off-by: \1/p')
+# grep -qs "^$SOB" "$1" || echo "$SOB" >> "$1"
+
+# This example catches duplicate Signed-off-by lines.
+
+test "" = "$(grep '^Signed-off-by: ' "$1" |
+	 sort | uniq -c | sed -e '/^[ 	]*1[ 	]/d')" || {
+	echo >&2 Duplicate Signed-off-by lines.
+	exit 1
+}

File extern/mochiweb/.git/hooks/post-commit.sample

+#!/bin/sh
+#
+# An example hook script that is called after a successful
+# commit is made.
+#
+# To enable this hook, rename this file to "post-commit".
+
+: Nothing

File extern/mochiweb/.git/hooks/post-receive.sample

+#!/bin/sh
+#
+# An example hook script for the "post-receive" event.
+#
+# The "post-receive" script is run after receive-pack has accepted a pack
+# and the repository has been updated.  It is passed arguments in through
+# stdin in the form
+#  <oldrev> <newrev> <refname>
+# For example:
+#  aa453216d1b3e49e7f6f98441fa56946ddcd6a20 68f7abf4e6f922807889f52bc043ecd31b79f814 refs/heads/master
+#
+# see contrib/hooks/ for a sample, or uncomment the next line and
+# rename the file to "post-receive".
+
+#. /usr/share/doc/git-core/contrib/hooks/post-receive-email

File extern/mochiweb/.git/hooks/post-update.sample

+#!/bin/sh
+#
+# An example hook script to prepare a packed repository for use over
+# dumb transports.
+#
+# To enable this hook, rename this file to "post-update".
+
+exec git update-server-info

File extern/mochiweb/.git/hooks/pre-applypatch.sample

+#!/bin/sh
+#
+# An example hook script to verify what is about to be committed
+# by applypatch from an e-mail message.
+#
+# The hook should exit with non-zero status after issuing an
+# appropriate message if it wants to stop the commit.
+#
+# To enable this hook, rename this file to "pre-applypatch".
+
+. git-sh-setup
+test -x "$GIT_DIR/hooks/pre-commit" &&
+	exec "$GIT_DIR/hooks/pre-commit" ${1+"$@"}
+:

File extern/mochiweb/.git/hooks/pre-commit.sample

+#!/bin/sh
+#
+# An example hook script to verify what is about to be committed.
+# Called by "git commit" with no arguments.  The hook should
+# exit with non-zero status after issuing an appropriate message if
+# it wants to stop the commit.
+#
+# To enable this hook, rename this file to "pre-commit".
+
+if git rev-parse --verify HEAD >/dev/null 2>&1
+then
+	against=HEAD
+else
+	# Initial commit: diff against an empty tree object
+	against=4b825dc642cb6eb9a060e54bf8d69288fbee4904
+fi
+
+# If you want to allow non-ascii filenames set this variable to true.
+allownonascii=$(git config hooks.allownonascii)
+
+# Cross platform projects tend to avoid non-ascii filenames; prevent
+# them from being added to the repository. We exploit the fact that the
+# printable range starts at the space character and ends with tilde.
+if [ "$allownonascii" != "true" ] &&
+	# Note that the use of brackets around a tr range is ok here, (it's
+	# even required, for portability to Solaris 10's /usr/bin/tr), since
+	# the square bracket bytes happen to fall in the designated range.
+	test "$(git diff --cached --name-only --diff-filter=A -z $against |
+	  LC_ALL=C tr -d '[ -~]\0')"
+then
+	echo "Error: Attempt to add a non-ascii file name."
+	echo
+	echo "This can cause problems if you want to work"
+	echo "with people on other platforms."
+	echo
+	echo "To be portable it is advisable to rename the file ..."
+	echo
+	echo "If you know what you are doing you can disable this"
+	echo "check using:"
+	echo
+	echo "  git config hooks.allownonascii true"
+	echo
+	exit 1
+fi
+
+exec git diff-index --check --cached $against --

File extern/mochiweb/.git/hooks/pre-rebase.sample

+#!/bin/sh
+#
+# Copyright (c) 2006, 2008 Junio C Hamano
+#
+# The "pre-rebase" hook is run just before "git rebase" starts doing
+# its job, and can prevent the command from running by exiting with
+# non-zero status.
+#
+# The hook is called with the following parameters:
+#
+# $1 -- the upstream the series was forked from.
+# $2 -- the branch being rebased (or empty when rebasing the current branch).
+#
+# This sample shows how to prevent topic branches that are already
+# merged to 'next' branch from getting rebased, because allowing it
+# would result in rebasing already published history.
+
+publish=next
+basebranch="$1"
+if test "$#" = 2
+then
+	topic="refs/heads/$2"
+else
+	topic=`git symbolic-ref HEAD` ||
+	exit 0 ;# we do not interrupt rebasing detached HEAD
+fi
+
+case "$topic" in
+refs/heads/??/*)
+	;;
+*)
+	exit 0 ;# we do not interrupt others.
+	;;
+esac
+
+# Now we are dealing with a topic branch being rebased
+# on top of master.  Is it OK to rebase it?
+
+# Does the topic really exist?
+git show-ref -q "$topic" || {
+	echo >&2 "No such branch $topic"
+	exit 1
+}
+
+# Is topic fully merged to master?
+not_in_master=`git rev-list --pretty=oneline ^master "$topic"`
+if test -z "$not_in_master"
+then
+	echo >&2 "$topic is fully merged to master; better remove it."
+	exit 1 ;# we could allow it, but there is no point.
+fi
+
+# Is topic ever merged to next?  If so you should not be rebasing it.
+only_next_1=`git rev-list ^master "^$topic" ${publish} | sort`
+only_next_2=`git rev-list ^master           ${publish} | sort`
+if test "$only_next_1" = "$only_next_2"
+then
+	not_in_topic=`git rev-list "^$topic" master`
+	if test -z "$not_in_topic"
+	then
+		echo >&2 "$topic is already up-to-date with master"
+		exit 1 ;# we could allow it, but there is no point.
+	else
+		exit 0
+	fi
+else
+	not_in_next=`git rev-list --pretty=oneline ^${publish} "$topic"`
+	/usr/bin/env perl -e '
+		my $topic = $ARGV[0];
+		my $msg = "* $topic has commits already merged to public branch:\n";
+		my (%not_in_next) = map {
+			/^([0-9a-f]+) /;
+			($1 => 1);
+		} split(/\n/, $ARGV[1]);
+		for my $elem (map {
+				/^([0-9a-f]+) (.*)$/;
+				[$1 => $2];
+			} split(/\n/, $ARGV[2])) {
+			if (!exists $not_in_next{$elem->[0]}) {
+				if ($msg) {
+					print STDERR $msg;
+					undef $msg;
+				}
+				print STDERR " $elem->[1]\n";
+			}
+		}
+	' "$topic" "$not_in_next" "$not_in_master"
+	exit 1
+fi
+
+exit 0
+
+################################################################
+
+This sample hook safeguards topic branches that have been
+published from being rewound.
+
+The workflow assumed here is:
+
+ * Once a topic branch forks from "master", "master" is never
+   merged into it again (either directly or indirectly).
+
+ * Once a topic branch is fully cooked and merged into "master",
+   it is deleted.  If you need to build on top of it to correct
+   earlier mistakes, a new topic branch is created by forking at
+   the tip of the "master".  This is not strictly necessary, but
+   it makes it easier to keep your history simple.
+
+ * Whenever you need to test or publish your changes to topic
+   branches, merge them into "next" branch.
+
+The script, being an example, hardcodes the publish branch name
+to be "next", but it is trivial to make it configurable via
+$GIT_DIR/config mechanism.
+
+With this workflow, you would want to know:
+
+(1) ... if a topic branch has ever been merged to "next".  Young
+    topic branches can have stupid mistakes you would rather
+    clean up before publishing, and things that have not been
+    merged into other branches can be easily rebased without
+    affecting other people.  But once it is published, you would
+    not want to rewind it.
+
+(2) ... if a topic branch has been fully merged to "master".
+    Then you can delete it.  More importantly, you should not
+    build on top of it -- other people may already want to
+    change things related to the topic as patches against your
+    "master", so if you need further changes, it is better to
+    fork the topic (perhaps with the same name) afresh from the
+    tip of "master".
+
+Let's look at this example:
+
+		   o---o---o---o---o---o---o---o---o---o "next"
+		  /       /           /           /
+		 /   a---a---b A     /           /
+		/   /               /           /
+	       /   /   c---c---c---c B         /
+	      /   /   /             \         /
+	     /   /   /   b---b C     \       /
+	    /   /   /   /             \     /
+    ---o---o---o---o---o---o---o---o---o---o---o "master"
+
+
+A, B and C are topic branches.
+
+ * A has one fix since it was merged up to "next".
+
+ * B has finished.  It has been fully merged up to "master" and "next",
+   and is ready to be deleted.
+
+ * C has not merged to "next" at all.
+
+We would want to allow C to be rebased, refuse A, and encourage
+B to be deleted.
+
+To compute (1):
+
+	git rev-list ^master ^topic next
+	git rev-list ^master        next
+
+	if these match, topic has not merged in next at all.
+
+To compute (2):
+
+	git rev-list master..topic
+
+	if this is empty, it is fully merged to "master".

File extern/mochiweb/.git/hooks/prepare-commit-msg.sample

+#!/bin/sh
+#
+# An example hook script to prepare the commit log message.
+# Called by "git commit" with the name of the file that has the
+# commit message, followed by the description of the commit
+# message's source.  The hook's purpose is to edit the commit
+# message file.  If the hook fails with a non-zero status,
+# the commit is aborted.
+#
+# To enable this hook, rename this file to "prepare-commit-msg".
+
+# This hook includes three examples.  The first comments out the
+# "Conflicts:" part of a merge commit.
+#
+# The second includes the output of "git diff --name-status -r"
+# into the message, just before the "git status" output.  It is
+# commented because it doesn't cope with --amend or with squashed
+# commits.
+#
+# The third example adds a Signed-off-by line to the message, that can
+# still be edited.  This is rarely a good idea.
+
+case "$2,$3" in
+  merge,)
+    /usr/bin/env perl -i.bak -ne 's/^/# /, s/^# #/#/ if /^Conflicts/ .. /#/; print' "$1" ;;
+
+# ,|template,)
+#   /usr/bin/env perl -i.bak -pe '
+#      print "\n" . `git diff --cached --name-status -r`
+#	 if /^#/ && $first++ == 0' "$1" ;;
+
+  *) ;;
+esac
+
+# SOB=$(git var GIT_AUTHOR_IDENT | sed -n 's/^\(.*>\).*$/Signed-off-by: \1/p')
+# grep -qs "^$SOB" "$1" || echo "$SOB" >> "$1"

File extern/mochiweb/.git/hooks/update.sample

+#!/bin/sh
+#
+# An example hook script to blocks unannotated tags from entering.
+# Called by "git receive-pack" with arguments: refname sha1-old sha1-new
+#
+# To enable this hook, rename this file to "update".
+#
+# Config
+# ------
+# hooks.allowunannotated
+#   This boolean sets whether unannotated tags will be allowed into the
+#   repository.  By default they won't be.
+# hooks.allowdeletetag
+#   This boolean sets whether deleting tags will be allowed in the
+#   repository.  By default they won't be.
+# hooks.allowmodifytag
+#   This boolean sets whether a tag may be modified after creation. By default
+#   it won't be.
+# hooks.allowdeletebranch
+#   This boolean sets whether deleting branches will be allowed in the
+#   repository.  By default they won't be.
+# hooks.denycreatebranch
+#   This boolean sets whether remotely creating branches will be denied
+#   in the repository.  By default this is allowed.
+#
+
+# --- Command line
+refname="$1"
+oldrev="$2"
+newrev="$3"
+
+# --- Safety check
+if [ -z "$GIT_DIR" ]; then
+	echo "Don't run this script from the command line." >&2
+	echo " (if you want, you could supply GIT_DIR then run" >&2
+	echo "  $0 <ref> <oldrev> <newrev>)" >&2
+	exit 1
+fi
+
+if [ -z "$refname" -o -z "$oldrev" -o -z "$newrev" ]; then
+	echo "Usage: $0 <ref> <oldrev> <newrev>" >&2
+	exit 1
+fi
+
+# --- Config
+allowunannotated=$(git config --bool hooks.allowunannotated)
+allowdeletebranch=$(git config --bool hooks.allowdeletebranch)
+denycreatebranch=$(git config --bool hooks.denycreatebranch)
+allowdeletetag=$(git config --bool hooks.allowdeletetag)
+allowmodifytag=$(git config --bool hooks.allowmodifytag)
+
+# check for no description
+projectdesc=$(sed -e '1q' "$GIT_DIR/description")
+case "$projectdesc" in
+"Unnamed repository"* | "")
+	echo "*** Project description file hasn't been set" >&2
+	exit 1
+	;;
+esac
+
+# --- Check types
+# if $newrev is 0000...0000, it's a commit to delete a ref.
+zero="0000000000000000000000000000000000000000"
+if [ "$newrev" = "$zero" ]; then
+	newrev_type=delete
+else
+	newrev_type=$(git cat-file -t $newrev)
+fi
+
+case "$refname","$newrev_type" in
+	refs/tags/*,commit)
+		# un-annotated tag
+		short_refname=${refname##refs/tags/}
+		if [ "$allowunannotated" != "true" ]; then
+			echo "*** The un-annotated tag, $short_refname, is not allowed in this repository" >&2
+			echo "*** Use 'git tag [ -a | -s ]' for tags you want to propagate." >&2
+			exit 1
+		fi
+		;;
+	refs/tags/*,delete)
+		# delete tag
+		if [ "$allowdeletetag" != "true" ]; then
+			echo "*** Deleting a tag is not allowed in this repository" >&2
+			exit 1
+		fi
+		;;
+	refs/tags/*,tag)
+		# annotated tag
+		if [ "$allowmodifytag" != "true" ] && git rev-parse $refname > /dev/null 2>&1
+		then
+			echo "*** Tag '$refname' already exists." >&2
+			echo "*** Modifying a tag is not allowed in this repository." >&2
+			exit 1
+		fi
+		;;
+	refs/heads/*,commit)
+		# branch
+		if [ "$oldrev" = "$zero" -a "$denycreatebranch" = "true" ]; then
+			echo "*** Creating a branch is not allowed in this repository" >&2
+			exit 1
+		fi
+		;;
+	refs/heads/*,delete)
+		# delete branch
+		if [ "$allowdeletebranch" != "true" ]; then
+			echo "*** Deleting a branch is not allowed in this repository" >&2
+			exit 1
+		fi
+		;;
+	refs/remotes/*,commit)
+		# tracking branch
+		;;
+	refs/remotes/*,delete)
+		# delete tracking branch
+		if [ "$allowdeletebranch" != "true" ]; then
+			echo "*** Deleting a tracking branch is not allowed in this repository" >&2
+			exit 1
+		fi
+		;;
+	*)
+		# Anything else (is there anything else?)
+		echo "*** Update hook: unknown type of update to ref $refname of type $newrev_type" >&2
+		exit 1
+		;;
+esac
+
+# --- Finished
+exit 0

File extern/mochiweb/.git/index

Binary file added.

File extern/mochiweb/.git/info/exclude

+# git ls-files --others --exclude-from=.git/info/exclude
+# Lines that start with '#' are comments.
+# For a project mostly in C, the following would be a good set of
+# exclude patterns (uncomment them if you want to use them):
+# *.[oa]
+# *~

File extern/mochiweb/.git/logs/HEAD

+0000000000000000000000000000000000000000 d541e9a0f36c00dcadc2e589f20e47fbf46fc76f dehun <dehun@localhost.(none)> 1338985544 +0000	clone: from https://github.com/mochi/mochiweb.git

File extern/mochiweb/.git/logs/refs/heads/master

+0000000000000000000000000000000000000000 d541e9a0f36c00dcadc2e589f20e47fbf46fc76f dehun <dehun@localhost.(none)> 1338985544 +0000	clone: from https://github.com/mochi/mochiweb.git

File extern/mochiweb/.git/objects/pack/pack-2c285acef6fd20551d4ed4410a2e5f1b4b2b75e2.idx

Binary file added.

File extern/mochiweb/.git/objects/pack/pack-2c285acef6fd20551d4ed4410a2e5f1b4b2b75e2.pack

Binary file added.

File extern/mochiweb/.git/packed-refs

+# pack-refs with: peeled 
+5305a4b6476c66eff3c3146eccf5d03e7a496fc8 refs/tags/v2.3.1
+e6d1870200802f32e17e334272191397e15aec53 refs/tags/v2.3.0
+e92a71d47b55b801b60d0c07a8e09418c4b21044 refs/tags/v2.2.1
+497f1fc02f513a4d6488820025384e0dcf8fa7ab refs/tags/v2.1.0
+ecc6f85090c91c59e6628adb393f0c3bd25cc61d refs/tags/active-passive
+b81e5047a44d55e1b42ddbc62a801940a1a693c4 refs/tags/1.5.2
+bed36252198ae7159e3d2b3a9ca9a9bb9817e1de refs/tags/1.5.1
+86e2b0388737ef79e4c443e62cf8f86e680b887b refs/tags/1.5.0
+80571b9683cbc75cafddd4c428aa3cb2bbd66a7d refs/tags/1.4.1
+32dc8be582d6e8287f7b241990a1d55faec26d43 refs/tags/1.4.0
+cfa1740358aad9cd5df3ac51b4f920299009ec15 refs/remotes/origin/start-vs-start_link-gh58
+501c3d40574641ea84c1b32184280c0090ae7cb4 refs/remotes/origin/pu
+7a4e78d225ba1b83595bb35c2d62b0f905e4dd60 refs/remotes/origin/proper
+32f2b0da67f12e8d3268bcbcdb4e2ef153ee8000 refs/remotes/origin/perf
+d541e9a0f36c00dcadc2e589f20e47fbf46fc76f refs/remotes/origin/master
+d65bce6308dc530c61950ccca05bf16669d81224 refs/remotes/origin/long-http-headers-fix
+f425e5eabe300b20f69fe4d8027d319e2413e95b refs/remotes/origin/html5-gh64
+d163f12caa60f6bf61a8b3392091dc3c2781c53e refs/remotes/origin/html-amp-gh69
+d4cba1ebcbf4b3b601a9fc58346c058e14a24a9e refs/remotes/origin/hmac-example-53
+f49d0b2a9287041f9163d7e863995cf5de5bbf3f refs/remotes/origin/flv-mime-type
+98ac050c7eac180641ca6363187b07acc3b7ec90 refs/remotes/origin/fdsrv-removal
+29937a6d694a9002e9a068e291d80bcdf0a22609 refs/remotes/origin/edoc-warning-gh63
+af9fa1ba5ff8a2055728ba9ffd9da3659f5863c5 refs/remotes/origin/dst-cookie-crash-73
+044e70b9efae2f7e47fe829bb93898758880ae7f refs/remotes/origin/dr-remove-mochiweb-app
+e53571308a781fc9f9edc2d25193ff9ff98664a6 refs/remotes/origin/dr-dialyzer-more-warnings
+a9d59e61c7fd69453ef30bfdc18f631b94454942 refs/remotes/origin/afrobeard-pull-45
+0b6e3ffcdb617945fbfb74787b57b95b0198f8b2 refs/remotes/origin/R12B

File extern/mochiweb/.git/refs/heads/master

+d541e9a0f36c00dcadc2e589f20e47fbf46fc76f

File extern/mochiweb/.git/refs/remotes/origin/HEAD

+ref: refs/remotes/origin/master

File extern/mochiweb/.gitignore

+/ebin
+/doc
+/_test
+/.eunit
+/docs
+.DS_Store
+/TEST-*.xml
+/deps
+*.swp

File extern/mochiweb/.travis.yml

+language: erlang
+notifications:
+  email: false
+otp_release:
+  - R14B03
+  - R14B02
+  - R14B01

File extern/mochiweb/CHANGES.md

+Version 2.3.1 released 2012-03-31
+
+* Fix edoc warnings (#63)
+* Fix mochiweb_html handling of invalid charref sequences (unescaped &) (#69).
+* Add a manual garbage collection between requests to avoid worst case behavior
+  on keep-alive sockets.
+* Fix dst cookie bug (#73)
+* Removed unnecessary template_dir option, see
+  https://github.com/basho/rebar/issues/203
+
+Version 2.3.0 released 2011-10-14
+
+* Handle ssl_closed message in mochiweb_http (#59)
+* Added support for new MIME types (otf, eot, m4v, svg, svgz, ttc, ttf,
+  vcf, webm, webp, woff) (#61)
+* Updated mochiweb_charref to support all HTML5 entities. Note that
+  if you are using this module directly, the spec has changed to return
+  `[integer()]` for some entities. (#64)
+
+Version 2.2.1 released 2011-08-31
+
+* Removed `mochiweb_skel` module from the pre-rebar era
+
+Version 2.2.0 released 2011-08-29
+
+* Added new `mochiweb_http:start_link/1` and
+  `mochiweb_socket_server:start_link/1` APIs to explicitly start linked
+  servers. Also added `{link, false}` option to the `start/1` variants
+  to explicitly start unlinked. This is in expectation that we will
+  eventually change the default behavior of `start/1` to be unlinked as you
+  would expect it to. See https://github.com/mochi/mochiweb/issues/58 for
+  discussion.
+
+Version 2.1.0 released 2011-08-29
+
+* Added new `mochijson2:decode/2` with `{format, struct | proplist | eep18}`
+  options for easy decoding to various proplist formats. Also added encoding
+  support for eep18 style objects.

File extern/mochiweb/LICENSE

+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.

File extern/mochiweb/Makefile

+
+PREFIX:=../
+DEST:=$(PREFIX)$(PROJECT)
+
+REBAR=./rebar
+
+all:
+	@$(REBAR) get-deps compile
+
+edoc:
+	@$(REBAR) doc
+
+test:
+	@rm -rf .eunit
+	@mkdir -p .eunit
+	@$(REBAR) skip_deps=true eunit
+
+clean:
+	@$(REBAR) clean
+
+build_plt:
+	@$(REBAR) build-plt
+
+dialyzer:
+	@$(REBAR) dialyze
+
+app:
+	@$(REBAR) create template=mochiwebapp dest=$(DEST) appid=$(PROJECT)
+

File extern/mochiweb/README

+MochiWeb is an Erlang library for building lightweight HTTP servers.
+
+The latest version of MochiWeb is available at http://github.com/mochi/mochiweb
+
+The mailing list for MochiWeb is at http://groups.google.com/group/mochiweb/
+
+R12B compatibility:
+The master of MochiWeb is tested with R14A and later. A branch compatible
+with R12B is maintained separately at http://github.com/lemenkov/mochiweb
+The R12B branch of that repository is mirrored in the official repository
+occasionally for convenience.
+
+To create a new mochiweb using project:
+   make app PROJECT=project_name
+
+To create a new mochiweb using project in a specific directory:
+   make app PROJECT=project_name PREFIX=$HOME/projects/

File extern/mochiweb/ebin/mochifmt.beam

Binary file added.

File extern/mochiweb/ebin/mochifmt_records.beam

Binary file added.

File extern/mochiweb/ebin/mochifmt_std.beam

Binary file added.

File extern/mochiweb/ebin/mochiglobal.beam

Binary file added.

File extern/mochiweb/ebin/mochihex.beam

Binary file added.

File extern/mochiweb/ebin/mochijson.beam

Binary file added.

File extern/mochiweb/ebin/mochijson2.beam

Binary file added.

File extern/mochiweb/ebin/mochilists.beam

Binary file added.

File extern/mochiweb/ebin/mochilogfile2.beam

Binary file added.

File extern/mochiweb/ebin/mochinum.beam

Binary file added.

File extern/mochiweb/ebin/mochitemp.beam

Binary file added.

File extern/mochiweb/ebin/mochiutf8.beam

Binary file added.

File extern/mochiweb/ebin/mochiweb.app

+{application,mochiweb,
+             [{description,"MochiMedia Web Server"},
+              {vsn,"2.3.1"},
+              {modules,[mochifmt,mochifmt_records,mochifmt_std,mochiglobal,
+                        mochihex,mochijson,mochijson2,mochilists,
+                        mochilogfile2,mochinum,mochitemp,mochiutf8,mochiweb,
+                        mochiweb_acceptor,mochiweb_charref,mochiweb_cookies,
+                        mochiweb_cover,mochiweb_echo,mochiweb_headers,
+                        mochiweb_html,mochiweb_http,mochiweb_io,mochiweb_mime,
+                        mochiweb_multipart,mochiweb_request,
+                        mochiweb_request_tests,mochiweb_response,
+                        mochiweb_socket,mochiweb_socket_server,mochiweb_util,
+                        reloader]},
+              {registered,[]},
+              {env,[]},
+              {applications,[kernel,stdlib,crypto,inets,ssl,xmerl,compiler,
+                             syntax_tools]}]}.

File extern/mochiweb/ebin/mochiweb.beam

Binary file added.

File extern/mochiweb/ebin/mochiweb_acceptor.beam

Binary file added.

File extern/mochiweb/ebin/mochiweb_charref.beam

Binary file added.

File extern/mochiweb/ebin/mochiweb_cookies.beam

Binary file added.

File extern/mochiweb/ebin/mochiweb_cover.beam

Binary file added.

File extern/mochiweb/ebin/mochiweb_echo.beam

Binary file added.

File extern/mochiweb/ebin/mochiweb_headers.beam

Binary file added.

File extern/mochiweb/ebin/mochiweb_html.beam

Binary file added.

File extern/mochiweb/ebin/mochiweb_http.beam

Binary file added.

File extern/mochiweb/ebin/mochiweb_io.beam

Binary file added.

File extern/mochiweb/ebin/mochiweb_mime.beam

Binary file added.

File extern/mochiweb/ebin/mochiweb_multipart.beam

Binary file added.

File extern/mochiweb/ebin/mochiweb_request.beam

Binary file added.

File extern/mochiweb/ebin/mochiweb_request_tests.beam

Binary file added.

File extern/mochiweb/ebin/mochiweb_response.beam

Binary file added.

File extern/mochiweb/ebin/mochiweb_socket.beam

Binary file added.

File extern/mochiweb/ebin/mochiweb_socket_server.beam

Binary file added.

File extern/mochiweb/ebin/mochiweb_util.beam

Binary file added.

File extern/mochiweb/ebin/reloader.beam

Binary file added.

File extern/mochiweb/examples/hmac_api/README

+Introduction
+------------
+
+This example shows how to make an Amazon-style HMAC authentication system for an API with mochiweb.
+
+Purpose
+-------
+
+The purpose of this example is to:
+* make it easy to implement an API in mochiweb
+  - using a proven approach so that 'amateurs' don't have to reinvent crypto
+* make it easy to generate client libraries for that API so that client-side implementers can:
+  - reuse closely related code examples
+  - build compatibility unit tests instead of fiddling around debugging their library against live implementations of the system
+
+Scope
+-----
+
+The scope of this document is:
+* a description of the client-server exchange
+* a reference implementation of
+  - the server-side implementation of the exchange
+  - the client-side implementation of the exchange
+* developing a custom implementation of an API
+* deploying that implementation to new client-side users to build their client libraries
+
+Contents
+--------
+
+Subsequent sections of this document are:
+* the client-server exchange
+* the reference implementation in this example
+* building a custom implementation
+* deploying a custom implementation
+
+The Client-Server Exchange
+--------------------------
+
+OVERVIEW
+
+This section describes the client-server exchange for an Amazon-style API authentication schema. It has the following characteristics:
+* based on a public key/private key
+* used to authenticate non-SSL api requests
+* not a full once-use schema and is vulnerable to replay attacks within a short time window
+
+TYPE OF API
+
+The api described in this document is:
+* suitable for machine-machine communication
+
+The api described in this document is NOT:
+* an implementation of 2-legged OAUTH
+  - see https://github.com/tim/erlang-oauth
+* an implementation of 3-legged OAUTH
+
+It is not suitable for use in applications where an end user has to log into a service and piggy-back on top of a keypair security system.
+
+THE CLIENT LIBRARY HERE IS **NOT** AN AMAZON CLIENT LIBRARY. AMAZON DOES FUNKY STUFF WITH HOSTNAMES AND PUSHES THEM ONTO THE URL IN CANONICALIZATION! THE CLIENT LIBRARY IS AMAZON-A-LIKE ENOUGH TO USE THE AMAZON DOCOS TO BUILD A TEST SUITE.
+
+STEP 1
+
+The client is issued with a pair of keys, one public, one private, for example:
+* public:  "bcaa49f2a4f7d4f92ac36c8bf66d5bb6"
+* private: "92bc93d6b8aaec1cde772f903e06daf5"
+
+In the Amazon docs these are referred to as:
+* AWSAccessKeyId     (public)
+* AWSSecretAccessKey (private)
+
+These can be generated by the function:
+hmac_api_lib:get_api_keypair/0
+
+This function returns cryptographically strong random numbers using the openSSL crypto library under the covers.
+
+The public key is used as a declaration of identity, "I am bcaa49..."
+
+The private key is never passed over the wire and is used to construct the same hash on both the client- and the server-side.
+
+STEP 2
+
+The client prepares their request:
+* url
+* time of request
+* action (GET, POST, etc)
+* type of request (application/json, etc)
+* contents of request
+* etc, etc
+
+These components are then turned into a string called the canonical form.
+
+The HTTP protocol is permissive; it treats different requests as if they were the same. For instance it doesn't care about the order in which headers are sent, and allows the same header to contain multiple values as a list or be specified multiple times as a key-value pair.
+
+Intermediate machines between the client and server MAY pack and repack the HTTP request as long as they don't alter its meaning in a narrow sense. This means that the format of the HTTP request is not guaranteed to be maintained.
+
+The canonical form simply ensures that all the valid ways of making the same request are represented by the same string - irrespective of how this is done.
+
+The canonical form handles POST bodies and query parameters and silently discards anchors in URL's.
+
+A hash of this string is made with the private key.
+
+STEP 3
+
+The client makes the request to the server:
+* the signature is included in the request in the standard HTTPAuthorization header. (As the Amazon documentation points out this is infelicitous as it is being used for Authentication not Authorization, but hey!).
+
+The Authorization header constructed has the form:
+<schema name><space><public key><colon><signature>
+
+An Amazon one looks like:
+Authorization: AWS 0PN5J17HBGZHT7JJ3X82:frJIUN8DYpKDtOLCwo//yllqDzg=
+               --- -------------------- ----------------------------
+               sch    public key                 signature
+
+The HTTP request is made.
+
+STEP 4
+
+The request is processed:
+* the server receives the request
+* the server constructs the canonical form from the attributes of the request:
+  - url
+  - date header
+  - action (GET, POST, etc)
+  - content type of request (application/json, etc)
+  - some custom headers
+  - etc, etc
+* the server takes the client's public key from the HTTPAuthorization header and looks up the client's private key
+* the server signs the canonical form with the private key
+* the server compares:
+  - the signature in the request to the signature it has just generated
+  - the time encoded in the request with the server time
+* the request is accepted or denied
+
+The time comparison is 'fuzzy'. Different server's clocks will be out of sync to a degree, the request may have acquired a time from an intermediate machine along the way, etc, etc. Normally a 'clock skew' time is allowed - in Amazon's case this is 15 minutes.
+
+NOTA BENE: THIS CLOCK SKEW TIME ALLOWS FOR REPLAY ATTACKS WHERE A BAD GUY SIMPLY CAPTURES AND REPLAYS TRAFFIC.
+
+EXTENSION
+
+It is possible to extend this schema to prevent replay attacks. The server issues a nonce token (a random string) which is included in the signature. When the server authorizes the request it stores the token and prevents any request with that token (ie a replay) being authorized again.
+
+The client receives its next nonce token in the response to a successful request.
+
+The Reference Implementation In This Example
+--------------------------------------------
+
+The reference implementation used in this example is that described in the Amazon documentation here:
+http://docs.amazonwebservices.com/AmazonS3/latest/dev/index.html?RESTAuthentication.html
+
+The try out the reference implementation:
+* create a new mochiweb project as per the mochiweb README
+  - make app PROJECT=project_name
+* copy hmac_api_lib.erl and hmac_api_client.erl into project_name/src
+* copy hmac_api.hrl into project_name/include
+* edit project_name_web.erl and add a call to hmac_api_lib:authorize_request/1
+
+authorize/request/1 should be called in the loop of project_name_web.erl as per:
+
+    loop(Req, DocRoot) ->
+        Auth = hmac_api_lib:authorize_request(Req),
+        io:format("Auth is ~p~n", [Auth]),
+        "/" ++ Path = Req:get(path),
+        ...
+
+When this is done you are ready to test the api:
+* run 'make' in project_name/ to build the Erlang
+* start the web server with 'start-dev.sh' in project_name/ (this will also open an Erlang shell to the Erlang VM)
+
+To test the api run this command in the Erlang shell:
+* hmac_api_client:fire().
+
+The reference implementation uses 5 constants defined in hmac_api.hrl.
+* schema
+* headerprefix
+* dateheader
+* publickey
+* privatekey
+
+Building A Custom Implementation
+--------------------------------
+
+The simplest custom implementation is to simply take the existing code and change the values of the following constants:
+* schema
+* headerprefix
+* dateheader
+
+If the API is to be used 'as is', please use the values which are commented out in hmac_api.hrl. This will make easier for software developers to work out which version of which client-side libraries they can use.
+
+Client libraries written in other languages than Erlang can reemployment the test suite in hmac_api_lib.erl.
+
+More sophisticated changes will involve changes to the canonicalization functions.
+
+Use of a generic schema should make reuse of client libraries easier across different platforms.
+
+If you develop an ‘as-is’ client-side library in another language please consider submitting its code to this example.
+
+Deploying A Custom Implementation
+---------------------------------
+
+When deploying a custom implementation, the server-side code should be released with unit tests so the client-side developer can easily build a robust client.
+
+In addition to that you will need to specify:
+* description of how the API works:
+  - ie the acceptable methods and urls
+  - custom headers and their usage (if appropriate)
+

File extern/mochiweb/examples/hmac_api/hmac_api.hrl

+-author("Hypernumbers Ltd <gordon@hypernumbers.com>").
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%%                                                                          %%%
+%%% Reference values for testing against Amazon documents                    %%%
+%%%                                                                          %%%
+%%% These need to be changed in production!                                  %%%
+%%%                                                                          %%%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+-define(schema, "AWS").
+%% defines the prefix for headers to be included in the signature
+-define(headerprefix, "x-amz-").
+%% defines the date header
+-define(dateheader, "x-amz-date").
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%%                                                                          %%%
+%%% Default values for defining a generic API                                %%%
+%%%                                                                          %%%
+%%% Only change these if you alter the canonicalisation                      %%%
+%%%                                                                          %%%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%-define(schema, "MOCHIAPI").
+%%-define(headerprefix, "x-mochiapi-").
+%%-define(dateheader, "x-mochiapi-date").
+
+%% a couple of keys for testing
+%% these are taken from the document
+%% % http://docs.amazonwebservices.com/AmazonS3/latest/dev/index.html?RESTAuthentication.html
+%% they are not valid keys!
+-define(publickey,  "0PN5J17HBGZHT7JJ3X82").
+-define(privatekey, "uV3F3YluFJax1cknvbcGwgjvx4QpvB+leU8dUj2o").
+
+
+-record(hmac_signature,
+        {
+          method,
+          contentmd5,
+          contenttype,
+          date,
+          headers,
+          resource
+        }).

File extern/mochiweb/examples/hmac_api/hmac_api_client.erl

+-module(hmac_api_client).
+
+-export([
+         fire/0
+        ]).
+
+-include("hmac_api.hrl").
+-author("Hypernumbers Ltd <gordon@hypernumbers.com>").
+
+fire() ->
+    URL = "http://127.0.0.1:8080/some/page/yeah/",
+    %% Dates SHOULD conform to Section 3.3 of RFC2616
+    %% the examples from the RFC are:
+    %% Sun, 06 Nov 1994 08:49:37 GMT  ; RFC 822, updated by RFC 1123
+    %% Sunday, 06-Nov-94 08:49:37 GMT ; RFC 850, obsoleted by RFC 1036
+    %% Sun Nov  6 08:49:37 1994       ; ANSI C's asctime() format
+
+    %% Dates can be conveniently generated using dh_date.erl
+    %% https://github.com/daleharvey/dh_date
+    %% which is largely compatible with
+    %% http://uk.php.net/date
+
+    %% You MIGHT find it convenient to insist on times in UTC only
+    %% as it reduces the errors caused by summer time and other
+    %% conversion issues
+    Method = post,
+    Headers = [{"content-type", "application/json"},
+               {"date", "Sun, 10 Jul 2011 05:07:19"}],
+    ContentType = "application/json",
+    Body = "blah",
+    HTTPAuthHeader = hmac_api_lib:sign(?privatekey, Method, URL,
+                                       Headers, ContentType),
+    httpc:request(Method, {URL, [HTTPAuthHeader | Headers],
+                           ContentType, Body}, [], []).

File extern/mochiweb/examples/hmac_api/hmac_api_lib.erl

+-module(hmac_api_lib).
+
+-include("hmac_api.hrl").
+-include_lib("eunit/include/eunit.hrl").
+
+-author("Hypernumbers Ltd <gordon@hypernumbers.com>").
+
+%%% this library supports the hmac_sha api on both the client-side
+%%% AND the server-side
+%%%
+%%% sign/5 is used client-side to sign a request
+%%% - it returns an HTTPAuthorization header
+%%%
+%%% authorize_request/1 takes a mochiweb Request as an arguement
+%%% and checks that the request matches the signature
+%%%
+%%% get_api_keypair/0 creates a pair of public/private keys
+%%%
+%%% THIS LIB DOESN'T IMPLEMENT THE AMAZON API IT ONLY IMPLEMENTS
+%%% ENOUGH OF IT TO GENERATE A TEST SUITE.
+%%%
+%%% THE AMAZON API MUNGES HOSTNAME AND PATHS IN A CUSTOM WAY
+%%% THIS IMPLEMENTATION DOESN'T
+-export([
+         authorize_request/1,
+         sign/5,
+         get_api_keypair/0
+        ]).
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%%                                                                          %%%
+%%% API                                                                      %%%
+%%%                                                                          %%%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+authorize_request(Req) ->
+    Method      = Req:get(method),
+    Path        = Req:get(path),
+    Headers     = normalise(mochiweb_headers:to_list(Req:get(headers))),
+    ContentMD5  = get_header(Headers, "content-md5"),
+    ContentType = get_header(Headers, "content-type"),
+    Date        = get_header(Headers, "date"),
+    IncAuth     = get_header(Headers, "authorization"),
+    {_Schema, _PublicKey, _Sig} = breakout(IncAuth),
+    %% normally you would use the public key to look up the private key
+    PrivateKey  = ?privatekey,
+    Signature = #hmac_signature{method = Method,
+                                contentmd5 = ContentMD5,
+                                contenttype = ContentType,
+                                date = Date,
+                                headers = Headers,
+                                resource = Path},
+    Signed = sign_data(PrivateKey, Signature),
+    {_, AuthHeader} = make_HTTPAuth_header(Signed),
+    case AuthHeader of
+        IncAuth -> "match";
+        _       -> "no_match"
+    end.
+
+sign(PrivateKey, Method, URL, Headers, ContentType) ->
+    Headers2 = normalise(Headers),
+    ContentMD5 = get_header(Headers2, "content-md5"),
+    Date = get_header(Headers2, "date"),
+    Signature = #hmac_signature{method = Method,
+                                contentmd5 = ContentMD5,
+                                contenttype = ContentType,
+                                date = Date,
+                                headers = Headers,
+                                resource = URL},
+    SignedSig = sign_data(PrivateKey, Signature),
+    make_HTTPAuth_header(SignedSig).
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%%                                                                          %%%
+%%% Internal Functions                                                       %%%
+%%%                                                                          %%%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+breakout(Header) ->
+    [Schema, Tail] = string:tokens(Header, " "),
+    [PublicKey, Signature] = string:tokens(Tail, ":"),
+    {Schema, PublicKey, Signature}.
+
+get_api_keypair() ->
+    Public  = mochihex:to_hex(binary_to_list(crypto:strong_rand_bytes(16))),
+    Private = mochihex:to_hex(binary_to_list(crypto:strong_rand_bytes(16))),
+    {Public, Private}.
+
+make_HTTPAuth_header(Signature) ->
+    {"Authorization", ?schema ++ " "
+     ++ ?publickey ++ ":" ++ Signature}.
+
+make_signature_string(#hmac_signature{} = S) ->
+    Date = get_date(S#hmac_signature.headers, S#hmac_signature.date),
+    string:to_upper(atom_to_list(S#hmac_signature.method)) ++ "\n"
+        ++ S#hmac_signature.contentmd5 ++ "\n"
+        ++ S#hmac_signature.contenttype ++ "\n"
+        ++ Date ++ "\n"
+        ++ canonicalise_headers(S#hmac_signature.headers)
+        ++ canonicalise_resource(S#hmac_signature.resource).
+
+sign_data(PrivateKey, #hmac_signature{} = Signature) ->
+    Str = make_signature_string(Signature),
+    sign2(PrivateKey, Str).
+
+%% this fn is the entry point for a unit test which is why it is broken out...
+%% if yer encryption and utf8 and base45 doo-dahs don't work then
+%% yer Donald is well and truly Ducked so ye may as weel test it...
+sign2(PrivateKey, Str) ->
+    Sign = xmerl_ucs:to_utf8(Str),
+    binary_to_list(base64:encode(crypto:sha_mac(PrivateKey, Sign))).
+
+canonicalise_headers([]) -> "\n";
+canonicalise_headers(List) when is_list(List) ->
+    List2 = [{string:to_lower(K), V} || {K, V} <- lists:sort(List)],
+    c_headers2(consolidate(List2, []), []).
+
+c_headers2([], Acc)       -> string:join(Acc, "\n") ++ "\n";
+c_headers2([{?headerprefix ++ Rest, Key} | T], Acc) ->
+    Hd = string:strip(?headerprefix ++ Rest) ++ ":" ++ string:strip(Key),
+    c_headers2(T, [Hd | Acc]);
+c_headers2([_H | T], Acc) -> c_headers2(T, Acc).
+
+consolidate([H | []], Acc) -> [H | Acc];
+consolidate([{H, K1}, {H, K2} | Rest], Acc) ->
+    consolidate([{H, join(K1, K2)} | Rest], Acc);
+consolidate([{H1, K1}, {H2, K2} | Rest], Acc) ->
+    consolidate([{rectify(H2), rectify(K2)} | Rest], [{H1, K1} | Acc]).
+
+join(A, B) -> string:strip(A) ++ ";" ++ string:strip(B).
+
+%% removes line spacing as per RFC 2616 Section 4.2
+rectify(String) ->
+    Re = "[\x20* | \t*]+",
+    re:replace(String, Re, " ", [{return, list}, global]).
+
+canonicalise_resource("http://"  ++ Rest) -> c_res2(Rest);
+canonicalise_resource("https://" ++ Rest) -> c_res2(Rest);
+canonicalise_resource(X)                  -> c_res3(X).
+
+c_res2(Rest) ->
+    N = string:str(Rest, "/"),
+    {_, Tail} = lists:split(N, Rest),
+    c_res3("/" ++ Tail).
+
+c_res3(Tail) ->
+    URL = case string:str(Tail, "#") of
+              0 -> Tail;
+              N -> {U, _Anchor} = lists:split(N, Tail),
+                   U
+          end,
+    U3 = case string:str(URL, "?") of
+             0  -> URL;
+             N2 -> {U2, Q} = lists:split(N2, URL),
+                   U2 ++ canonicalise_query(Q)
+         end,
+    string:to_lower(U3).
+
+canonicalise_query(List) ->
+    List1 = string:to_lower(List),
+    List2 = string:tokens(List1, "&"),
+    string:join(lists:sort(List2), "&").
+
+%% if there's a header date take it and ditch the date
+get_date([], Date)            -> Date;
+get_date([{K, _V} | T], Date) -> case string:to_lower(K) of
+                                     ?dateheader -> [];
+                                     _           ->  get_date(T, Date)
+                                 end.
+
+normalise(List) -> norm2(List, []).
+
+norm2([], Acc) -> Acc;
+norm2([{K, V} | T], Acc) when is_atom(K) ->
+    norm2(T, [{string:to_lower(atom_to_list(K)), V} | Acc]);
+norm2([H | T], Acc) -> norm2(T, [H | Acc]).
+
+get_header(Headers, Type) ->
+    case lists:keyfind(Type, 1, Headers) of
+        false   -> [];
+        {_K, V} -> V
+    end.
+
+
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+%%%                                                                          %%%
+%%% Unit Tests                                                               %%%
+%%%                                                                          %%%
+%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
+
+                                                % taken from Amazon docs
+%% http://docs.amazonwebservices.com/AmazonS3/latest/dev/index.html?RESTAuthentication.html
+hash_test1(_) ->
+    Sig = "DELETE\n\n\n\nx-amz-date:Tue, 27 Mar 2007 21:20:26 +0000\n/johnsmith/photos/puppy.jpg",
+    Key = ?privatekey,
+    Hash = sign2(Key, Sig),
+    Expected = "k3nL7gH3+PadhTEVn5Ip83xlYzk=",
+    ?assertEqual(Expected, Hash).
+
+%% taken from Amazon docs
+%% http://docs.amazonwebservices.com/AmazonS3/latest/dev/index.html?RESTAuthentication.html
+hash_test2(_) ->
+    Sig = "GET\n\n\nTue, 27 Mar 2007 19:44:46 +0000\n/johnsmith/?acl",
+    Key = "uV3F3YluFJax1cknvbcGwgjvx4QpvB+leU8dUj2o",
+    Hash = sign2(Key, Sig),
+    Expected = "thdUi9VAkzhkniLj96JIrOPGi0g=",
+    ?assertEqual(Expected, Hash).
+
+%% taken from Amazon docs
+%% http://docs.amazonwebservices.com/AmazonS3/latest/dev/index.html?RESTAuthentication.html
+hash_test3(_) ->
+    Sig = "GET\n\n\nWed, 28 Mar 2007 01:49:49 +0000\n/dictionary/"
+        ++ "fran%C3%A7ais/pr%c3%a9f%c3%a8re",
+    Key = "uV3F3YluFJax1cknvbcGwgjvx4QpvB+leU8dUj2o",
+    Hash = sign2(Key, Sig),
+    Expected = "dxhSBHoI6eVSPcXJqEghlUzZMnY=",
+    ?assertEqual(Expected, Hash).
+
+signature_test1(_) ->
+    URL = "http://example.com:90/tongs/ya/bas",
+    Method = post,
+    ContentMD5 = "",
+    ContentType = "",
+    Date = "Sun, 10 Jul 2011 05:07:19 UTC",
+    Headers = [],
+    Signature = #hmac_signature{method = Method,
+                                contentmd5 = ContentMD5,
+                                contenttype = ContentType,
+                                date = Date,
+                                headers = Headers,
+                                resource = URL},
+    Sig = make_signature_string(Signature),
+    Expected = "POST\n\n\nSun, 10 Jul 2011 05:07:19 UTC\n\n/tongs/ya/bas",
+    ?assertEqual(Expected, Sig).
+
+signature_test2(_) ->
+    URL = "http://example.com:90/tongs/ya/bas",
+    Method = get,
+    ContentMD5 = "",
+    ContentType = "",
+    Date = "Sun, 10 Jul 2011 05:07:19 UTC",
+    Headers = [{"x-amz-acl", "public-read"}],
+    Signature = #hmac_signature{method = Method,
+                                contentmd5 = ContentMD5,
+                                contenttype = ContentType,
+                                date = Date,
+                                headers = Headers,
+                                resource = URL},
+    Sig = make_signature_string(Signature),
+    Expected = "GET\n\n\nSun, 10 Jul 2011 05:07:19 UTC\nx-amz-acl:public-read\n/tongs/ya/bas",
+    ?assertEqual(Expected, Sig).
+
+signature_test3(_) ->
+    URL = "http://example.com:90/tongs/ya/bas",
+    Method = get,
+    ContentMD5 = "",
+    ContentType = "",
+    Date = "Sun, 10 Jul 2011 05:07:19 UTC",
+    Headers = [{"x-amz-acl", "public-read"},
+               {"yantze", "blast-off"},
+               {"x-amz-doobie", "bongwater"},
+               {"x-amz-acl", "public-write"}],
+    Signature = #hmac_signature{method = Method,
+                                contentmd5 = ContentMD5,
+                                contenttype = ContentType,
+                                date = Date,
+                                headers = Headers,
+                                resource = URL},
+    Sig = make_signature_string(Signature),
+    Expected = "GET\n\n\nSun, 10 Jul 2011 05:07:19 UTC\nx-amz-acl:public-read;public-write\nx-amz-doobie:bongwater\n/tongs/ya/bas",
+    ?assertEqual(Expected, Sig).
+
+signature_test4(_) ->
+    URL = "http://example.com:90/tongs/ya/bas",
+    Method = get,
+    ContentMD5 = "",
+    ContentType = "",
+    Date = "Sun, 10 Jul 2011 05:07:19 UTC",
+    Headers = [{"x-amz-acl", "public-read"},
+               {"yantze", "blast-off"},
+               {"x-amz-doobie  oobie \t boobie ", "bongwater"},
+               {"x-amz-acl", "public-write"}],
+    Signature = #hmac_signature{method = Method,
+                                contentmd5 = ContentMD5,
+                                contenttype = ContentType,
+                                date = Date,
+                                headers = Headers,
+                                resource = URL},
+    Sig = make_signature_string(Signature),
+    Expected = "GET\n\n\nSun, 10 Jul 2011 05:07:19 UTC\nx-amz-acl:public-read;public-write\nx-amz-doobie oobie boobie:bongwater\n/tongs/ya/bas",
+    ?assertEqual(Expected, Sig).
+
+signature_test5(_) ->
+    URL = "http://example.com:90/tongs/ya/bas",
+    Method = get,
+    ContentMD5 = "",
+    ContentType = "",
+    Date = "Sun, 10 Jul 2011 05:07:19 UTC",
+    Headers = [{"x-amz-acl", "public-Read"},
+               {"yantze", "Blast-Off"},
+               {"x-amz-doobie  Oobie \t boobie ", "bongwater"},
+               {"x-amz-acl", "public-write"}],
+    Signature = #hmac_signature{method = Method,
+                                contentmd5 = ContentMD5,
+                                contenttype = ContentType,
+                                date = Date,
+                                headers = Headers,
+                                resource = URL},
+    Sig = make_signature_string(Signature),
+    Expected = "GET\n\n\nSun, 10 Jul 2011 05:07:19 UTC\nx-amz-acl:public-Read;public-write\nx-amz-doobie oobie boobie:bongwater\n/tongs/ya/bas",
+    ?assertEqual(Expected, Sig).
+
+signature_test6(_) ->
+    URL = "http://example.com:90/tongs/ya/bas/?andy&zbish=bash&bosh=burp",
+    Method = get,
+    ContentMD5 = "",
+    ContentType = "",
+    Date = "Sun, 10 Jul 2011 05:07:19 UTC",
+    Headers = [],
+    Signature = #hmac_signature{method = Method,
+                                contentmd5 = ContentMD5,
+                                contenttype = ContentType,
+                                date = Date,
+                                headers = Headers,
+                                resource = URL},
+    Sig = make_signature_string(Signature),
+    Expected = "GET\n\n\nSun, 10 Jul 2011 05:07:19 UTC\n\n"
+        ++ "/tongs/ya/bas/?andy&bosh=burp&zbish=bash",
+    ?assertEqual(Expected, Sig).
+
+signature_test7(_) ->
+    URL = "http://exAMPLE.Com:90/tONgs/ya/bas/?ANdy&ZBish=Bash&bOsh=burp",
+    Method = get,
+    ContentMD5 = "",
+    ContentType = "",
+    Date = "Sun, 10 Jul 2011 05:07:19 UTC",
+    Headers = [],
+    Signature = #hmac_signature{method = Method,
+                                contentmd5 = ContentMD5,
+                                contenttype = ContentType,
+                                date = Date,
+                                headers = Headers,
+                                resource = URL},
+    Sig = make_signature_string(Signature),
+    Expected = "GET\n\n\nSun, 10 Jul 2011 05:07:19 UTC\n\n"
+        ++"/tongs/ya/bas/?andy&bosh=burp&zbish=bash",
+    ?assertEqual(Expected, Sig).
+
+signature_test8(_) ->
+    URL = "http://exAMPLE.Com:90/tONgs/ya/bas/?ANdy&ZBish=Bash&bOsh=burp",
+    Method = get,
+    ContentMD5 = "",
+    ContentType = "",
+    Date = "",
+    Headers = [{"x-aMz-daTe", "Tue, 27 Mar 2007 21:20:26 +0000"}],
+    Signature = #hmac_signature{method = Method,
+                                contentmd5 = ContentMD5,
+                                contenttype = ContentType,
+                                date = Date,
+                                headers = Headers,
+                                resource = URL},
+    Sig = make_signature_string(Signature),
+    Expected = "GET\n\n\n\n"
+        ++"x-amz-date:Tue, 27 Mar 2007 21:20:26 +0000\n"
+        ++"/tongs/ya/bas/?andy&bosh=burp&zbish=bash",
+    ?assertEqual(Expected, Sig).
+
+signature_test9(_) ->
+    URL = "http://exAMPLE.Com:90/tONgs/ya/bas/?ANdy&ZBish=Bash&bOsh=burp",
+    Method = get,
+    ContentMD5 = "",
+    ContentType = "",
+    Date = "Sun, 10 Jul 2011 05:07:19 UTC",
+    Headers = [{"x-amz-date", "Tue, 27 Mar 2007 21:20:26 +0000"}],
+    Signature = #hmac_signature{method = Method,
+                                contentmd5 = ContentMD5,
+                                contenttype = ContentType,
+                                date = Date,
+                                headers = Headers,
+                                resource = URL},
+    Sig = make_signature_string(Signature),
+    Expected = "GET\n\n\n\n"
+        ++"x-amz-date:Tue, 27 Mar 2007 21:20:26 +0000\n"
+        ++"/tongs/ya/bas/?andy&bosh=burp&zbish=bash",
+    ?assertEqual(Expected, Sig).
+
+amazon_test1(_) ->
+    URL = "http://exAMPLE.Com:90/johnsmith/photos/puppy.jpg",
+    Method = delete,
+    ContentMD5 = "",
+    ContentType = "",
+    Date = "",
+    Headers = [{"x-amz-date", "Tue, 27 Mar 2007 21:20:26 +0000"}],
+    Signature = #hmac_signature{method = Method,
+                                contentmd5 = ContentMD5,
+                                contenttype = ContentType,
+                                date = Date,
+                                headers = Headers,
+                                resource = URL},
+    Sig = sign_data(?privatekey, Signature),
+    Expected = "k3nL7gH3+PadhTEVn5Ip83xlYzk=",
+    ?assertEqual(Expected, Sig).
+
+unit_test_() ->
+    Setup   = fun() -> ok end,
+    Cleanup = fun(_) -> ok end,
+
+    Series1 = [
+               fun hash_test1/1,
+               fun hash_test2/1,
+               fun hash_test3/1
+              ],
+
+    Series2 = [
+               fun signature_test1/1,
+               fun signature_test2/1,
+               fun signature_test3/1,
+               fun signature_test4/1,
+               fun signature_test5/1,
+               fun signature_test6/1,
+               fun signature_test7/1,
+               fun signature_test8/1,
+               fun signature_test9/1
+              ],
+
+    Series3 = [
+               fun amazon_test1/1
+              ],
+
+    {setup, Setup, Cleanup, [
+                             {with, [], Series1},
+                             {with, [], Series2},
+                             {with, [], Series3}
+                            ]}.

File extern/mochiweb/examples/https/https_store.erl

+
+%% Trivial web storage app. It's available over both HTTP (port 8442)
+%% and HTTPS (port 8443). You use a PUT to store items, a GET to
+%% retrieve them and DELETE to delete them. The HTTP POST method is
+%% invalid for this application. Example (using HTTPS transport):
+%%
+%%   $ curl -k --verbose https://localhost:8443/flintstones
+%%   ...
+%%   404 Not Found
+%%   ...
+%%   $ echo -e "Fred\nWilma\nBarney" |
+%%           curl -k --verbose https://localhost:8443/flintstones \
+%%                -X PUT -H "Content-Type: text/plain" --data-binary @-
+%%   ...
+%%   201 Created
+%%   ...
+%%   $ curl -k --verbose https://localhost:8443/flintstones
+%%   ...
+%%   Fred
+%%   Wilma
+%%   Barney
+%%   ...
+%%   $ curl -k --verbose https://localhost:8443/flintstones -X DELETE
+%%   ...
+%%   200 OK
+%%   ...
+%%   $ curl -k --verbose https://localhost:8443/flintstones
+%%   ...
+%%   404 Not Found
+%%   ...
+%%
+%% All submitted data is stored in memory (in an ets table). Could be
+%% useful for ad-hoc testing.
+
+-module(https_store).
+
+-export([start/0,
+         stop/0,
+         dispatch/1,
+         loop/1
+         ]).
+
+-define(HTTP_OPTS, [
+            {loop, {?MODULE, dispatch}},
+            {port, 8442},
+            {name, http_8442}
+            ]).
+
+-define(HTTPS_OPTS, [
+            {loop, {?MODULE, dispatch}},
+            {port, 8443},
+            {name, https_8443},
+            {ssl, true},
+            {ssl_opts, [
+                {certfile, "server_cert.pem"},
+                {keyfile, "server_key.pem"}]}
+            ]).
+
+-record(sd, {http, https}).
+-record(resource, {type, data}).
+
+start() ->
+    {ok, Http} = mochiweb_http:start(?HTTP_OPTS),
+    {ok, Https} = mochiweb_http:start(?HTTPS_OPTS),
+    SD = #sd{http=Http, https=Https},
+    Pid = spawn_link(fun() ->
+                             ets:new(?MODULE, [named_table]),
+                             loop(SD)
+                     end),
+    register(http_store, Pid),
+    ok.
+
+stop() ->
+    http_store ! stop,
+    ok.
+
+dispatch(Req) ->
+    case Req:get(method) of
+        'GET' ->
+            get_resource(Req);
+        'PUT' ->
+            put_resource(Req);
+        'DELETE' ->
+            delete_resource(Req);
+        _ ->
+            Headers = [{"Allow", "GET,PUT,DELETE"}],
+            Req:respond({405, Headers, "405 Method Not Allowed\r\n"})
+    end.
+
+get_resource(Req) ->
+    Path = Req:get(path),
+    case ets:lookup(?MODULE, Path) of
+        [{Path, #resource{type=Type, data=Data}}] ->
+            Req:ok({Type, Data});
+        [] ->
+            Req:respond({404, [], "404 Not Found\r\n"})
+    end.
+
+put_resource(Req) ->
+    ContentType = case Req:get_header_value("Content-Type") of
+        undefined ->
+            "application/octet-stream";
+        S ->
+            S
+    end,
+    Resource = #resource{type=ContentType, data=Req:recv_body()},
+    http_store ! {self(), {put, Req:get(path), Resource}},
+    Pid = whereis(http_store),
+    receive
+        {Pid, created} ->
+            Req:respond({201, [], "201 Created\r\n"});
+        {Pid, updated} ->
+            Req:respond({200, [], "200 OK\r\n"})
+    end.
+
+delete_resource(Req) ->
+    http_store ! {self(), {delete, Req:get(path)}},
+    Pid = whereis(http_store),
+    receive
+        {Pid, ok} ->
+            Req:respond({200, [], "200 OK\r\n"})
+    end.
+
+loop(#sd{http=Http, https=Https} = SD) ->
+    receive
+        stop ->
+            ok = mochiweb_http:stop(Http),
+            ok = mochiweb_http:stop(Https),
+            exit(normal);
+        {From, {put, Key, Val}} ->
+            Exists = ets:member(?MODULE, Key),
+            ets:insert(?MODULE, {Key, Val}),
+            case Exists of
+                true ->
+                    From ! {self(), updated};
+                false ->
+                    From ! {self(), created}
+            end;