From 30346cef63895e86542940dce03aa77c77dec212 Mon Sep 17 00:00:00 2001 From: Peter Lemenkov Date: Jul 30 2010 11:13:11 +0000 Subject: Ver. 1.0.0 --- diff --git a/.gitignore b/.gitignore index 713b8a2..d643bed 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,2 @@ apache-couchdb-0.11.1.tar.gz +apache-couchdb-1.0.0.tar.gz diff --git a/couchdb-1.0.0-0001-Force-init-script-installation.patch b/couchdb-1.0.0-0001-Force-init-script-installation.patch new file mode 100644 index 0000000..4f046c1 --- /dev/null +++ b/couchdb-1.0.0-0001-Force-init-script-installation.patch @@ -0,0 +1,39 @@ +From eeeeb10f4a99786046262c1d28e70a69768f0710 Mon Sep 17 00:00:00 2001 +From: Peter Lemenkov +Date: Thu, 27 May 2010 15:17:20 +0400 +Subject: [PATCH 1/9] Force init script installation + +--- + configure.ac | 16 +++------------- + 1 files changed, 3 insertions(+), 13 deletions(-) + +diff --git a/configure.ac b/configure.ac +index 0c08952..08a1c1c 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -294,19 +294,9 @@ launchd_enabled=false + + if test "$use_init" = "yes"; then + AC_MSG_CHECKING(location of init directory) +- if test -d /etc/rc.d; then +- init_enabled=true +- AC_SUBST([initdir], ['${sysconfdir}/rc.d']) +- AC_MSG_RESULT(${initdir}) +- else +- if test -d /etc/init.d; then +- init_enabled=true +- AC_SUBST([initdir], ['${sysconfdir}/init.d']) +- AC_MSG_RESULT(${initdir}) +- else +- AC_MSG_RESULT(not found) +- fi +- fi ++ init_enabled=true ++ AC_SUBST([initdir], ['${sysconfdir}/rc.d']) ++ AC_MSG_RESULT(${initdir}) + fi + + if test "$use_launchd" = "yes"; then +-- +1.7.1.1 + diff --git a/couchdb-1.0.0-0002-Install-into-erllibdir-by-default.patch b/couchdb-1.0.0-0002-Install-into-erllibdir-by-default.patch new file mode 100644 index 0000000..0ab87a7 --- /dev/null +++ b/couchdb-1.0.0-0002-Install-into-erllibdir-by-default.patch @@ -0,0 +1,34 @@ +From 22a6d6b33d32141bd4e64fd602010c31fa11e4ed Mon Sep 17 00:00:00 2001 +From: Peter Lemenkov +Date: Thu, 27 May 2010 15:18:29 +0400 +Subject: [PATCH 2/9] Install into erllibdir by default + +--- + configure | 4 ++-- + 1 files changed, 2 insertions(+), 2 deletions(-) + +diff --git a/configure b/configure +index ce82d90..28caf74 100755 +--- a/configure ++++ b/configure +@@ -12193,7 +12193,7 @@ localdatadir=${datadir}/${package_identifier} + + localdocdir=${datadir}/doc/${package_identifier} + +-locallibdir=${libdir}/${package_identifier} ++locallibdir=${libdir} + + localstatelibdir=${localstatedir}/lib/${package_identifier} + +@@ -12209,7 +12209,7 @@ if test x${IS_WINDOWS} = xTRUE; then + localerlanglibdir=${libdir} + + else +- locallibbindir=${locallibdir}/bin ++ locallibbindir=${locallibdir}/erlang/lib/couch-${version}/priv + + localerlanglibdir=${locallibdir}/erlang/lib + +-- +1.7.1.1 + diff --git a/couchdb-1.0.0-0003-Remove-bundled-erlang-oauth-library.patch b/couchdb-1.0.0-0003-Remove-bundled-erlang-oauth-library.patch new file mode 100644 index 0000000..b7f6a35 --- /dev/null +++ b/couchdb-1.0.0-0003-Remove-bundled-erlang-oauth-library.patch @@ -0,0 +1,964 @@ +From def82e3226932c188c29b29d8cd792a33ba5599e Mon Sep 17 00:00:00 2001 +From: Peter Lemenkov +Date: Wed, 14 Jul 2010 17:57:54 +0400 +Subject: [PATCH 3/9] Remove bundled erlang-oauth library + +--- + configure | 3 - + configure.ac | 1 - + src/Makefile.am | 2 +- + src/Makefile.in | 2 +- + src/erlang-oauth/Makefile.am | 49 ---- + src/erlang-oauth/Makefile.in | 484 ---------------------------------- + src/erlang-oauth/oauth.app.in | 20 -- + src/erlang-oauth/oauth.erl | 107 -------- + src/erlang-oauth/oauth_hmac_sha1.erl | 11 - + src/erlang-oauth/oauth_http.erl | 22 -- + src/erlang-oauth/oauth_plaintext.erl | 10 - + src/erlang-oauth/oauth_unix.erl | 16 -- + src/erlang-oauth/oauth_uri.erl | 88 ------ + test/etap/test_util.erl.in | 2 +- + 14 files changed, 3 insertions(+), 814 deletions(-) + delete mode 100644 src/erlang-oauth/Makefile.am + delete mode 100644 src/erlang-oauth/Makefile.in + delete mode 100644 src/erlang-oauth/oauth.app.in + delete mode 100644 src/erlang-oauth/oauth.erl + delete mode 100644 src/erlang-oauth/oauth_hmac_sha1.erl + delete mode 100644 src/erlang-oauth/oauth_http.erl + delete mode 100644 src/erlang-oauth/oauth_plaintext.erl + delete mode 100644 src/erlang-oauth/oauth_unix.erl + delete mode 100644 src/erlang-oauth/oauth_uri.erl + +diff --git a/configure b/configure +index 28caf74..b3040cf 100755 +--- a/configure ++++ b/configure +@@ -12261,8 +12261,6 @@ ac_config_files="$ac_config_files src/couchdb/Makefile" + + ac_config_files="$ac_config_files src/couchdb/priv/Makefile" + +-ac_config_files="$ac_config_files src/erlang-oauth/Makefile" +- + ac_config_files="$ac_config_files src/etap/Makefile" + + ac_config_files="$ac_config_files src/ibrowse/Makefile" +@@ -13290,7 +13288,6 @@ do + "src/couchdb/couch.app.tpl") CONFIG_FILES="$CONFIG_FILES src/couchdb/couch.app.tpl" ;; + "src/couchdb/Makefile") CONFIG_FILES="$CONFIG_FILES src/couchdb/Makefile" ;; + "src/couchdb/priv/Makefile") CONFIG_FILES="$CONFIG_FILES src/couchdb/priv/Makefile" ;; +- "src/erlang-oauth/Makefile") CONFIG_FILES="$CONFIG_FILES src/erlang-oauth/Makefile" ;; + "src/etap/Makefile") CONFIG_FILES="$CONFIG_FILES src/etap/Makefile" ;; + "src/ibrowse/Makefile") CONFIG_FILES="$CONFIG_FILES src/ibrowse/Makefile" ;; + "src/mochiweb/Makefile") CONFIG_FILES="$CONFIG_FILES src/mochiweb/Makefile" ;; +diff --git a/configure.ac b/configure.ac +index 08a1c1c..84a38d3 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -387,7 +387,6 @@ AC_CONFIG_FILES([src/Makefile]) + AC_CONFIG_FILES([src/couchdb/couch.app.tpl]) + AC_CONFIG_FILES([src/couchdb/Makefile]) + AC_CONFIG_FILES([src/couchdb/priv/Makefile]) +-AC_CONFIG_FILES([src/erlang-oauth/Makefile]) + AC_CONFIG_FILES([src/etap/Makefile]) + AC_CONFIG_FILES([src/ibrowse/Makefile]) + AC_CONFIG_FILES([src/mochiweb/Makefile]) +diff --git a/src/Makefile.am b/src/Makefile.am +index b9529f9..e577138 100644 +--- a/src/Makefile.am ++++ b/src/Makefile.am +@@ -10,4 +10,4 @@ + ## License for the specific language governing permissions and limitations under + ## the License. + +-SUBDIRS = couchdb erlang-oauth etap ibrowse mochiweb ++SUBDIRS = couchdb etap ibrowse mochiweb +diff --git a/src/Makefile.in b/src/Makefile.in +index daff9c3..3fec89c 100644 +--- a/src/Makefile.in ++++ b/src/Makefile.in +@@ -248,7 +248,7 @@ version_minor = @version_minor@ + version_release = @version_release@ + version_revision = @version_revision@ + version_stage = @version_stage@ +-SUBDIRS = couchdb erlang-oauth etap ibrowse mochiweb ++SUBDIRS = couchdb etap ibrowse mochiweb + all: all-recursive + + .SUFFIXES: +diff --git a/src/erlang-oauth/Makefile.am b/src/erlang-oauth/Makefile.am +deleted file mode 100644 +index 1d12339..0000000 +--- a/src/erlang-oauth/Makefile.am ++++ /dev/null +@@ -1,49 +0,0 @@ +-## 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. +- +-oauthebindir = $(localerlanglibdir)/erlang-oauth/ebin +- +-# Removed oauth_rsa_sha1.erl until we require R12B5 or +-# we add a ./configure option to enable it. +- +-oauth_file_collection = \ +- oauth.app.in \ +- oauth.erl \ +- oauth_hmac_sha1.erl \ +- oauth_http.erl \ +- oauth_plaintext.erl \ +- oauth_unix.erl \ +- oauth_uri.erl +- +-oauthebin_make_generated_file_list = \ +- oauth.app \ +- oauth.beam \ +- oauth_hmac_sha1.beam \ +- oauth_http.beam \ +- oauth_plaintext.beam \ +- oauth_unix.beam \ +- oauth_uri.beam +- +-oauthebin_DATA = \ +- $(oauthebin_make_generated_file_list) +- +-EXTRA_DIST = \ +- $(oauth_file_collection) +- +-CLEANFILES = \ +- $(oauthebin_make_generated_file_list) +- +-%.app: %.app.in +- cp $< $@ +- +-%.beam: %.erl +- $(ERLC) $(ERLC_FLAGS) $< +diff --git a/src/erlang-oauth/Makefile.in b/src/erlang-oauth/Makefile.in +deleted file mode 100644 +index 282883b..0000000 +--- a/src/erlang-oauth/Makefile.in ++++ /dev/null +@@ -1,484 +0,0 @@ +-# Makefile.in generated by automake 1.11 from Makefile.am. +-# @configure_input@ +- +-# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, +-# 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation, +-# Inc. +-# This Makefile.in is free software; the Free Software Foundation +-# gives unlimited permission to copy and/or distribute it, +-# with or without modifications, as long as this notice is preserved. +- +-# This program is distributed in the hope that it will be useful, +-# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +-# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +-# PARTICULAR PURPOSE. +- +-@SET_MAKE@ +- +-VPATH = @srcdir@ +-pkgdatadir = $(datadir)/@PACKAGE@ +-pkgincludedir = $(includedir)/@PACKAGE@ +-pkglibdir = $(libdir)/@PACKAGE@ +-pkglibexecdir = $(libexecdir)/@PACKAGE@ +-am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +-install_sh_DATA = $(install_sh) -c -m 644 +-install_sh_PROGRAM = $(install_sh) -c +-install_sh_SCRIPT = $(install_sh) -c +-INSTALL_HEADER = $(INSTALL_DATA) +-transform = $(program_transform_name) +-NORMAL_INSTALL = : +-PRE_INSTALL = : +-POST_INSTALL = : +-NORMAL_UNINSTALL = : +-PRE_UNINSTALL = : +-POST_UNINSTALL = : +-build_triplet = @build@ +-host_triplet = @host@ +-subdir = src/erlang-oauth +-DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in +-ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +-am__aclocal_m4_deps = $(top_srcdir)/m4/ac_check_curl.m4 \ +- $(top_srcdir)/m4/ac_check_icu.m4 $(top_srcdir)/m4/libtool.m4 \ +- $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \ +- $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \ +- $(top_srcdir)/acinclude.m4 $(top_srcdir)/configure.ac +-am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ +- $(ACLOCAL_M4) +-mkinstalldirs = $(install_sh) -d +-CONFIG_HEADER = $(top_builddir)/config.h +-CONFIG_CLEAN_FILES = +-CONFIG_CLEAN_VPATH_FILES = +-SOURCES = +-DIST_SOURCES = +-am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; +-am__vpath_adj = case $$p in \ +- $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ +- *) f=$$p;; \ +- esac; +-am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; +-am__install_max = 40 +-am__nobase_strip_setup = \ +- srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` +-am__nobase_strip = \ +- for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" +-am__nobase_list = $(am__nobase_strip_setup); \ +- for p in $$list; do echo "$$p $$p"; done | \ +- sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ +- $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ +- if (++n[$$2] == $(am__install_max)) \ +- { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ +- END { for (dir in files) print dir, files[dir] }' +-am__base_list = \ +- sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ +- sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' +-am__installdirs = "$(DESTDIR)$(oauthebindir)" +-DATA = $(oauthebin_DATA) +-DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +-ACLOCAL = @ACLOCAL@ +-AMTAR = @AMTAR@ +-AR = @AR@ +-AUTOCONF = @AUTOCONF@ +-AUTOHEADER = @AUTOHEADER@ +-AUTOMAKE = @AUTOMAKE@ +-AWK = @AWK@ +-CC = @CC@ +-CCDEPMODE = @CCDEPMODE@ +-CFLAGS = @CFLAGS@ +-CPP = @CPP@ +-CPPFLAGS = @CPPFLAGS@ +-CURL_CFLAGS = @CURL_CFLAGS@ +-CURL_CONFIG = @CURL_CONFIG@ +-CURL_LDFLAGS = @CURL_LDFLAGS@ +-CURL_LIBS = @CURL_LIBS@ +-CYGPATH_W = @CYGPATH_W@ +-DEFS = @DEFS@ +-DEPDIR = @DEPDIR@ +-DSYMUTIL = @DSYMUTIL@ +-DUMPBIN = @DUMPBIN@ +-ECHO_C = @ECHO_C@ +-ECHO_N = @ECHO_N@ +-ECHO_T = @ECHO_T@ +-EGREP = @EGREP@ +-ERL = @ERL@ +-ERLC = @ERLC@ +-ERLC_FLAGS = @ERLC_FLAGS@ +-EXEEXT = @EXEEXT@ +-FGREP = @FGREP@ +-FLAGS = @FLAGS@ +-GREP = @GREP@ +-HELP2MAN_EXECUTABLE = @HELP2MAN_EXECUTABLE@ +-ICU_CFLAGS = @ICU_CFLAGS@ +-ICU_CONFIG = @ICU_CONFIG@ +-ICU_CXXFLAGS = @ICU_CXXFLAGS@ +-ICU_LIBS = @ICU_LIBS@ +-ICU_LOCAL_BIN = @ICU_LOCAL_BIN@ +-ICU_LOCAL_CFLAGS = @ICU_LOCAL_CFLAGS@ +-ICU_LOCAL_LDFLAGS = @ICU_LOCAL_LDFLAGS@ +-INNO_COMPILER_EXECUTABLE = @INNO_COMPILER_EXECUTABLE@ +-INSTALL = @INSTALL@ +-INSTALL_DATA = @INSTALL_DATA@ +-INSTALL_PROGRAM = @INSTALL_PROGRAM@ +-INSTALL_SCRIPT = @INSTALL_SCRIPT@ +-INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +-JSLIB = @JSLIB@ +-JS_LIB_BASE = @JS_LIB_BASE@ +-JS_LIB_BINARY = @JS_LIB_BINARY@ +-JS_LIB_DIR = @JS_LIB_DIR@ +-LD = @LD@ +-LDFLAGS = @LDFLAGS@ +-LIBOBJS = @LIBOBJS@ +-LIBS = @LIBS@ +-LIBTOOL = @LIBTOOL@ +-LIPO = @LIPO@ +-LN_S = @LN_S@ +-LTLIBOBJS = @LTLIBOBJS@ +-MAKEINFO = @MAKEINFO@ +-MKDIR_P = @MKDIR_P@ +-NM = @NM@ +-NMEDIT = @NMEDIT@ +-OBJDUMP = @OBJDUMP@ +-OBJEXT = @OBJEXT@ +-OTOOL = @OTOOL@ +-OTOOL64 = @OTOOL64@ +-PACKAGE = @PACKAGE@ +-PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +-PACKAGE_NAME = @PACKAGE_NAME@ +-PACKAGE_STRING = @PACKAGE_STRING@ +-PACKAGE_TARNAME = @PACKAGE_TARNAME@ +-PACKAGE_URL = @PACKAGE_URL@ +-PACKAGE_VERSION = @PACKAGE_VERSION@ +-PATH_SEPARATOR = @PATH_SEPARATOR@ +-RANLIB = @RANLIB@ +-SED = @SED@ +-SET_MAKE = @SET_MAKE@ +-SHELL = @SHELL@ +-STRIP = @STRIP@ +-VERSION = @VERSION@ +-abs_builddir = @abs_builddir@ +-abs_srcdir = @abs_srcdir@ +-abs_top_builddir = @abs_top_builddir@ +-abs_top_srcdir = @abs_top_srcdir@ +-ac_ct_CC = @ac_ct_CC@ +-ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +-am__include = @am__include@ +-am__leading_dot = @am__leading_dot@ +-am__quote = @am__quote@ +-am__tar = @am__tar@ +-am__untar = @am__untar@ +-bindir = @bindir@ +-bug_uri = @bug_uri@ +-build = @build@ +-build_alias = @build_alias@ +-build_cpu = @build_cpu@ +-build_os = @build_os@ +-build_vendor = @build_vendor@ +-builddir = @builddir@ +-datadir = @datadir@ +-datarootdir = @datarootdir@ +-docdir = @docdir@ +-dvidir = @dvidir@ +-exec_prefix = @exec_prefix@ +-host = @host@ +-host_alias = @host_alias@ +-host_cpu = @host_cpu@ +-host_os = @host_os@ +-host_vendor = @host_vendor@ +-htmldir = @htmldir@ +-includedir = @includedir@ +-infodir = @infodir@ +-initdir = @initdir@ +-install_sh = @install_sh@ +-launchddir = @launchddir@ +-libdir = @libdir@ +-libexecdir = @libexecdir@ +-localconfdir = @localconfdir@ +-localdatadir = @localdatadir@ +-localdocdir = @localdocdir@ +-localedir = @localedir@ +-localerlanglibdir = @localerlanglibdir@ +-locallibbindir = @locallibbindir@ +-locallibdir = @locallibdir@ +-localstatedir = @localstatedir@ +-localstatelibdir = @localstatelibdir@ +-localstatelogdir = @localstatelogdir@ +-localstaterundir = @localstaterundir@ +-lt_ECHO = @lt_ECHO@ +-mandir = @mandir@ +-mkdir_p = @mkdir_p@ +-msvc_redist_dir = @msvc_redist_dir@ +-msvc_redist_name = @msvc_redist_name@ +-oldincludedir = @oldincludedir@ +-openssl_bin_dir = @openssl_bin_dir@ +-package_author_address = @package_author_address@ +-package_author_name = @package_author_name@ +-package_identifier = @package_identifier@ +-package_name = @package_name@ +-package_tarname = @package_tarname@ +-pdfdir = @pdfdir@ +-prefix = @prefix@ +-program_transform_name = @program_transform_name@ +-psdir = @psdir@ +-sbindir = @sbindir@ +-sharedstatedir = @sharedstatedir@ +-srcdir = @srcdir@ +-sysconfdir = @sysconfdir@ +-target_alias = @target_alias@ +-top_build_prefix = @top_build_prefix@ +-top_builddir = @top_builddir@ +-top_srcdir = @top_srcdir@ +-version = @version@ +-version_major = @version_major@ +-version_minor = @version_minor@ +-version_release = @version_release@ +-version_revision = @version_revision@ +-version_stage = @version_stage@ +-oauthebindir = $(localerlanglibdir)/erlang-oauth/ebin +- +-# Removed oauth_rsa_sha1.erl until we require R12B5 or +-# we add a ./configure option to enable it. +-oauth_file_collection = \ +- oauth.app.in \ +- oauth.erl \ +- oauth_hmac_sha1.erl \ +- oauth_http.erl \ +- oauth_plaintext.erl \ +- oauth_unix.erl \ +- oauth_uri.erl +- +-oauthebin_make_generated_file_list = \ +- oauth.app \ +- oauth.beam \ +- oauth_hmac_sha1.beam \ +- oauth_http.beam \ +- oauth_plaintext.beam \ +- oauth_unix.beam \ +- oauth_uri.beam +- +-oauthebin_DATA = \ +- $(oauthebin_make_generated_file_list) +- +-EXTRA_DIST = \ +- $(oauth_file_collection) +- +-CLEANFILES = \ +- $(oauthebin_make_generated_file_list) +- +-all: all-am +- +-.SUFFIXES: +-$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) +- @for dep in $?; do \ +- case '$(am__configure_deps)' in \ +- *$$dep*) \ +- ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ +- && { if test -f $@; then exit 0; else break; fi; }; \ +- exit 1;; \ +- esac; \ +- done; \ +- echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/erlang-oauth/Makefile'; \ +- $(am__cd) $(top_srcdir) && \ +- $(AUTOMAKE) --foreign src/erlang-oauth/Makefile +-.PRECIOUS: Makefile +-Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status +- @case '$?' in \ +- *config.status*) \ +- cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ +- *) \ +- echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ +- cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ +- esac; +- +-$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) +- cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +- +-$(top_srcdir)/configure: $(am__configure_deps) +- cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +-$(ACLOCAL_M4): $(am__aclocal_m4_deps) +- cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +-$(am__aclocal_m4_deps): +- +-mostlyclean-libtool: +- -rm -f *.lo +- +-clean-libtool: +- -rm -rf .libs _libs +-install-oauthebinDATA: $(oauthebin_DATA) +- @$(NORMAL_INSTALL) +- test -z "$(oauthebindir)" || $(MKDIR_P) "$(DESTDIR)$(oauthebindir)" +- @list='$(oauthebin_DATA)'; test -n "$(oauthebindir)" || list=; \ +- for p in $$list; do \ +- if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ +- echo "$$d$$p"; \ +- done | $(am__base_list) | \ +- while read files; do \ +- echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(oauthebindir)'"; \ +- $(INSTALL_DATA) $$files "$(DESTDIR)$(oauthebindir)" || exit $$?; \ +- done +- +-uninstall-oauthebinDATA: +- @$(NORMAL_UNINSTALL) +- @list='$(oauthebin_DATA)'; test -n "$(oauthebindir)" || list=; \ +- files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ +- test -n "$$files" || exit 0; \ +- echo " ( cd '$(DESTDIR)$(oauthebindir)' && rm -f" $$files ")"; \ +- cd "$(DESTDIR)$(oauthebindir)" && rm -f $$files +-tags: TAGS +-TAGS: +- +-ctags: CTAGS +-CTAGS: +- +- +-distdir: $(DISTFILES) +- @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ +- topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ +- list='$(DISTFILES)'; \ +- dist_files=`for file in $$list; do echo $$file; done | \ +- sed -e "s|^$$srcdirstrip/||;t" \ +- -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ +- case $$dist_files in \ +- */*) $(MKDIR_P) `echo "$$dist_files" | \ +- sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ +- sort -u` ;; \ +- esac; \ +- for file in $$dist_files; do \ +- if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ +- if test -d $$d/$$file; then \ +- dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ +- if test -d "$(distdir)/$$file"; then \ +- find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ +- fi; \ +- if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ +- cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ +- find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ +- fi; \ +- cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ +- else \ +- test -f "$(distdir)/$$file" \ +- || cp -p $$d/$$file "$(distdir)/$$file" \ +- || exit 1; \ +- fi; \ +- done +-check-am: all-am +-check: check-am +-all-am: Makefile $(DATA) +-installdirs: +- for dir in "$(DESTDIR)$(oauthebindir)"; do \ +- test -z "$$dir" || $(MKDIR_P) "$$dir"; \ +- done +-install: install-am +-install-exec: install-exec-am +-install-data: install-data-am +-uninstall: uninstall-am +- +-install-am: all-am +- @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am +- +-installcheck: installcheck-am +-install-strip: +- $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ +- install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ +- `test -z '$(STRIP)' || \ +- echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +-mostlyclean-generic: +- +-clean-generic: +- -test -z "$(CLEANFILES)" || rm -f $(CLEANFILES) +- +-distclean-generic: +- -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) +- -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) +- +-maintainer-clean-generic: +- @echo "This command is intended for maintainers to use" +- @echo "it deletes files that may require special tools to rebuild." +-clean: clean-am +- +-clean-am: clean-generic clean-libtool mostlyclean-am +- +-distclean: distclean-am +- -rm -f Makefile +-distclean-am: clean-am distclean-generic +- +-dvi: dvi-am +- +-dvi-am: +- +-html: html-am +- +-html-am: +- +-info: info-am +- +-info-am: +- +-install-data-am: install-oauthebinDATA +- +-install-dvi: install-dvi-am +- +-install-dvi-am: +- +-install-exec-am: +- +-install-html: install-html-am +- +-install-html-am: +- +-install-info: install-info-am +- +-install-info-am: +- +-install-man: +- +-install-pdf: install-pdf-am +- +-install-pdf-am: +- +-install-ps: install-ps-am +- +-install-ps-am: +- +-installcheck-am: +- +-maintainer-clean: maintainer-clean-am +- -rm -f Makefile +-maintainer-clean-am: distclean-am maintainer-clean-generic +- +-mostlyclean: mostlyclean-am +- +-mostlyclean-am: mostlyclean-generic mostlyclean-libtool +- +-pdf: pdf-am +- +-pdf-am: +- +-ps: ps-am +- +-ps-am: +- +-uninstall-am: uninstall-oauthebinDATA +- +-.MAKE: install-am install-strip +- +-.PHONY: all all-am check check-am clean clean-generic clean-libtool \ +- distclean distclean-generic distclean-libtool distdir dvi \ +- dvi-am html html-am info info-am install install-am \ +- install-data install-data-am install-dvi install-dvi-am \ +- install-exec install-exec-am install-html install-html-am \ +- install-info install-info-am install-man install-oauthebinDATA \ +- install-pdf install-pdf-am install-ps install-ps-am \ +- install-strip installcheck installcheck-am installdirs \ +- maintainer-clean maintainer-clean-generic mostlyclean \ +- mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ +- uninstall uninstall-am uninstall-oauthebinDATA +- +- +-%.app: %.app.in +- cp $< $@ +- +-%.beam: %.erl +- $(ERLC) $(ERLC_FLAGS) $< +- +-# Tell versions [3.59,3.63) of GNU make to not export all variables. +-# Otherwise a system limit (for SysV at least) may be exceeded. +-.NOEXPORT: +diff --git a/src/erlang-oauth/oauth.app.in b/src/erlang-oauth/oauth.app.in +deleted file mode 100644 +index 6357b9b..0000000 +--- a/src/erlang-oauth/oauth.app.in ++++ /dev/null +@@ -1,20 +0,0 @@ +-{application, oauth, [ +- {description, "Erlang OAuth implementation"}, +- {vsn, "dev"}, +- {modules, [ +- oauth, +- oauth_hmac_sha1, +- oauth_http, +- oauth_plaintext, +- oauth_rsa_sha1, +- oauth_unix, +- oauth_uri +- ]}, +- {registered, []}, +- {applications, [ +- kernel, +- stdlib, +- crypto, +- inets +- ]} +-]}. +diff --git a/src/erlang-oauth/oauth.erl b/src/erlang-oauth/oauth.erl +deleted file mode 100644 +index 866655c..0000000 +--- a/src/erlang-oauth/oauth.erl ++++ /dev/null +@@ -1,107 +0,0 @@ +--module(oauth). +- +--export( +- [ get/5 +- , header/1 +- , post/5 +- , signature/5 +- , signature_base_string/3 +- , signed_params/6 +- , token/1 +- , token_secret/1 +- , uri/2 +- , verify/6 +- ]). +- +- +-get(URL, ExtraParams, Consumer, Token, TokenSecret) -> +- SignedParams = signed_params("GET", URL, ExtraParams, Consumer, Token, TokenSecret), +- oauth_http:get(uri(URL, SignedParams)). +- +-post(URL, ExtraParams, Consumer, Token, TokenSecret) -> +- SignedParams = signed_params("POST", URL, ExtraParams, Consumer, Token, TokenSecret), +- oauth_http:post(URL, oauth_uri:params_to_string(SignedParams)). +- +-uri(Base, []) -> +- Base; +-uri(Base, Params) -> +- lists:concat([Base, "?", oauth_uri:params_to_string(Params)]). +- +-header(Params) -> +- {"Authorization", "OAuth " ++ oauth_uri:params_to_header_string(Params)}. +- +-token(Params) -> +- proplists:get_value("oauth_token", Params). +- +-token_secret(Params) -> +- proplists:get_value("oauth_token_secret", Params). +- +-verify(Signature, HttpMethod, URL, Params, Consumer, TokenSecret) -> +- case signature_method(Consumer) of +- plaintext -> +- oauth_plaintext:verify(Signature, consumer_secret(Consumer), TokenSecret); +- hmac_sha1 -> +- BaseString = signature_base_string(HttpMethod, URL, Params), +- oauth_hmac_sha1:verify(Signature, BaseString, consumer_secret(Consumer), TokenSecret); +- rsa_sha1 -> +- BaseString = signature_base_string(HttpMethod, URL, Params), +- oauth_rsa_sha1:verify(Signature, BaseString, consumer_secret(Consumer)) +- end. +- +-signed_params(HttpMethod, URL, ExtraParams, Consumer, Token, TokenSecret) -> +- Params = token_param(Token, params(Consumer, ExtraParams)), +- [{"oauth_signature", signature(HttpMethod, URL, Params, Consumer, TokenSecret)}|Params]. +- +-signature(HttpMethod, URL, Params, Consumer, TokenSecret) -> +- case signature_method(Consumer) of +- plaintext -> +- oauth_plaintext:signature(consumer_secret(Consumer), TokenSecret); +- hmac_sha1 -> +- BaseString = signature_base_string(HttpMethod, URL, Params), +- oauth_hmac_sha1:signature(BaseString, consumer_secret(Consumer), TokenSecret); +- rsa_sha1 -> +- BaseString = signature_base_string(HttpMethod, URL, Params), +- oauth_rsa_sha1:signature(BaseString, consumer_secret(Consumer)) +- end. +- +-signature_base_string(HttpMethod, URL, Params) -> +- NormalizedURL = oauth_uri:normalize(URL), +- NormalizedParams = oauth_uri:params_to_string(lists:sort(Params)), +- oauth_uri:calate("&", [HttpMethod, NormalizedURL, NormalizedParams]). +- +-token_param("", Params) -> +- Params; +-token_param(Token, Params) -> +- [{"oauth_token", Token}|Params]. +- +-params(Consumer, Params) -> +- Nonce = base64:encode_to_string(crypto:rand_bytes(32)), % cf. ruby-oauth +- params(Consumer, oauth_unix:timestamp(), Nonce, Params). +- +-params(Consumer, Timestamp, Nonce, Params) -> +- [ {"oauth_version", "1.0"} +- , {"oauth_nonce", Nonce} +- , {"oauth_timestamp", integer_to_list(Timestamp)} +- , {"oauth_signature_method", signature_method_string(Consumer)} +- , {"oauth_consumer_key", consumer_key(Consumer)} +- | Params +- ]. +- +-signature_method_string(Consumer) -> +- case signature_method(Consumer) of +- plaintext -> +- "PLAINTEXT"; +- hmac_sha1 -> +- "HMAC-SHA1"; +- rsa_sha1 -> +- "RSA-SHA1" +- end. +- +-signature_method(_Consumer={_, _, Method}) -> +- Method. +- +-consumer_secret(_Consumer={_, Secret, _}) -> +- Secret. +- +-consumer_key(_Consumer={Key, _, _}) -> +- Key. +diff --git a/src/erlang-oauth/oauth_hmac_sha1.erl b/src/erlang-oauth/oauth_hmac_sha1.erl +deleted file mode 100644 +index 79d59f3..0000000 +--- a/src/erlang-oauth/oauth_hmac_sha1.erl ++++ /dev/null +@@ -1,11 +0,0 @@ +--module(oauth_hmac_sha1). +- +--export([signature/3, verify/4]). +- +- +-signature(BaseString, CS, TS) -> +- Key = oauth_uri:calate("&", [CS, TS]), +- base64:encode_to_string(crypto:sha_mac(Key, BaseString)). +- +-verify(Signature, BaseString, CS, TS) -> +- couch_util:verify(signature(BaseString, CS, TS), Signature). +diff --git a/src/erlang-oauth/oauth_http.erl b/src/erlang-oauth/oauth_http.erl +deleted file mode 100644 +index bf5a4ba..0000000 +--- a/src/erlang-oauth/oauth_http.erl ++++ /dev/null +@@ -1,22 +0,0 @@ +--module(oauth_http). +- +--export([get/1, post/2, response_params/1, response_body/1, response_code/1]). +- +- +-get(URL) -> +- request(get, {URL, []}). +- +-post(URL, Data) -> +- request(post, {URL, [], "application/x-www-form-urlencoded", Data}). +- +-request(Method, Request) -> +- http:request(Method, Request, [{autoredirect, false}], []). +- +-response_params(Response) -> +- oauth_uri:params_from_string(response_body(Response)). +- +-response_body({{_, _, _}, _, Body}) -> +- Body. +- +-response_code({{_, Code, _}, _, _}) -> +- Code. +diff --git a/src/erlang-oauth/oauth_plaintext.erl b/src/erlang-oauth/oauth_plaintext.erl +deleted file mode 100644 +index 41a1e9b..0000000 +--- a/src/erlang-oauth/oauth_plaintext.erl ++++ /dev/null +@@ -1,10 +0,0 @@ +--module(oauth_plaintext). +- +--export([signature/2, verify/3]). +- +- +-signature(CS, TS) -> +- oauth_uri:calate("&", [CS, TS]). +- +-verify(Signature, CS, TS) -> +- couch_util:verify(signature(CS, TS), Signature). +diff --git a/src/erlang-oauth/oauth_unix.erl b/src/erlang-oauth/oauth_unix.erl +deleted file mode 100644 +index 73ca314..0000000 +--- a/src/erlang-oauth/oauth_unix.erl ++++ /dev/null +@@ -1,16 +0,0 @@ +--module(oauth_unix). +- +--export([timestamp/0]). +- +- +-timestamp() -> +- timestamp(calendar:universal_time()). +- +-timestamp(DateTime) -> +- seconds(DateTime) - epoch(). +- +-epoch() -> +- seconds({{1970,1,1},{00,00,00}}). +- +-seconds(DateTime) -> +- calendar:datetime_to_gregorian_seconds(DateTime). +diff --git a/src/erlang-oauth/oauth_uri.erl b/src/erlang-oauth/oauth_uri.erl +deleted file mode 100644 +index fb27ae7..0000000 +--- a/src/erlang-oauth/oauth_uri.erl ++++ /dev/null +@@ -1,88 +0,0 @@ +--module(oauth_uri). +- +--export([normalize/1, calate/2, encode/1]). +--export([params_from_string/1, params_to_string/1, +- params_from_header_string/1, params_to_header_string/1]). +- +--import(lists, [concat/1]). +- +--define(is_uppercase_alpha(C), C >= $A, C =< $Z). +--define(is_lowercase_alpha(C), C >= $a, C =< $z). +--define(is_alpha(C), ?is_uppercase_alpha(C); ?is_lowercase_alpha(C)). +--define(is_digit(C), C >= $0, C =< $9). +--define(is_alphanumeric(C), ?is_alpha(C); ?is_digit(C)). +--define(is_unreserved(C), ?is_alphanumeric(C); C =:= $-; C =:= $_; C =:= $.; C =:= $~). +--define(is_hex(C), ?is_digit(C); C >= $A, C =< $F). +- +- +-normalize(URI) -> +- case http_uri:parse(URI) of +- {Scheme, UserInfo, Host, Port, Path, _Query} -> +- normalize(Scheme, UserInfo, string:to_lower(Host), Port, [Path]); +- Else -> +- Else +- end. +- +-normalize(http, UserInfo, Host, 80, Acc) -> +- normalize(http, UserInfo, [Host|Acc]); +-normalize(https, UserInfo, Host, 443, Acc) -> +- normalize(https, UserInfo, [Host|Acc]); +-normalize(Scheme, UserInfo, Host, Port, Acc) -> +- normalize(Scheme, UserInfo, [Host, ":", Port|Acc]). +- +-normalize(Scheme, [], Acc) -> +- concat([Scheme, "://"|Acc]); +-normalize(Scheme, UserInfo, Acc) -> +- concat([Scheme, "://", UserInfo, "@"|Acc]). +- +-params_to_header_string(Params) -> +- intercalate(", ", [concat([encode(K), "=\"", encode(V), "\""]) || {K, V} <- Params]). +- +-params_from_header_string(String) -> +- [param_from_header_string(Param) || Param <- re:split(String, ",\\s*", [{return, list}]), Param =/= ""]. +- +-param_from_header_string(Param) -> +- [Key, QuotedValue] = string:tokens(Param, "="), +- Value = string:substr(QuotedValue, 2, length(QuotedValue) - 2), +- {decode(Key), decode(Value)}. +- +-params_from_string(Params) -> +- [param_from_string(Param) || Param <- string:tokens(Params, "&")]. +- +-param_from_string(Param) -> +- list_to_tuple([decode(Value) || Value <- string:tokens(Param, "=")]). +- +-params_to_string(Params) -> +- intercalate("&", [calate("=", [K, V]) || {K, V} <- Params]). +- +-calate(Sep, Xs) -> +- intercalate(Sep, [encode(X) || X <- Xs]). +- +-intercalate(Sep, Xs) -> +- concat(intersperse(Sep, Xs)). +- +-intersperse(_, []) -> []; +-intersperse(_, [X]) -> [X]; +-intersperse(Sep, [X|Xs]) -> +- [X, Sep|intersperse(Sep, Xs)]. +- +-decode(Chars) -> +- decode(Chars, []). +- +-decode([], Decoded) -> +- lists:reverse(Decoded); +-decode([$%,A,B|Etc], Decoded) when ?is_hex(A), ?is_hex(B) -> +- decode(Etc, [erlang:list_to_integer([A,B], 16)|Decoded]); +-decode([C|Etc], Decoded) when ?is_unreserved(C) -> +- decode(Etc, [C|Decoded]). +- +-encode(Chars) -> +- encode(Chars, []). +- +-encode([], Encoded) -> +- lists:flatten(lists:reverse(Encoded)); +-encode([C|Etc], Encoded) when ?is_unreserved(C) -> +- encode(Etc, [C|Encoded]); +-encode([C|Etc], Encoded) -> +- Value = io_lib:format("%~2.1.0s", [erlang:integer_to_list(C, 16)]), +- encode(Etc, [Value|Encoded]). +diff --git a/test/etap/test_util.erl.in b/test/etap/test_util.erl.in +index 4c42edb..79b0417 100644 +--- a/test/etap/test_util.erl.in ++++ b/test/etap/test_util.erl.in +@@ -22,7 +22,7 @@ builddir() -> + "@abs_top_builddir@". + + init_code_path() -> +- Paths = ["etap", "couchdb", "erlang-oauth", "ibrowse", "mochiweb"], ++ Paths = ["etap", "couchdb", "ibrowse", "mochiweb"], + lists:foreach(fun(Name) -> + code:add_pathz(filename:join([builddir(), "src", Name])) + end, Paths). +-- +1.7.1.1 + diff --git a/couchdb-1.0.0-0004-Remove-bundled-erlang-etap-library.patch b/couchdb-1.0.0-0004-Remove-bundled-erlang-etap-library.patch new file mode 100644 index 0000000..0cd4a28 --- /dev/null +++ b/couchdb-1.0.0-0004-Remove-bundled-erlang-etap-library.patch @@ -0,0 +1,1912 @@ +From f028c14174d3bfb5e1a54a8ab5b28653a084364c Mon Sep 17 00:00:00 2001 +From: Peter Lemenkov +Date: Wed, 14 Jul 2010 18:00:03 +0400 +Subject: [PATCH 4/9] Remove bundled erlang-etap library + +--- + configure | 3 - + configure.ac | 1 - + src/Makefile.am | 2 +- + src/Makefile.in | 2 +- + src/etap/Makefile.am | 44 ---- + src/etap/Makefile.in | 476 ----------------------------------------- + src/etap/etap.erl | 416 ----------------------------------- + src/etap/etap_application.erl | 72 ------ + src/etap/etap_can.erl | 79 ------- + src/etap/etap_exception.erl | 66 ------ + src/etap/etap_process.erl | 42 ---- + src/etap/etap_report.erl | 343 ----------------------------- + src/etap/etap_request.erl | 89 -------- + src/etap/etap_string.erl | 47 ---- + src/etap/etap_web.erl | 65 ------ + test/etap/test_util.erl.in | 2 +- + 16 files changed, 3 insertions(+), 1746 deletions(-) + delete mode 100644 src/etap/Makefile.am + delete mode 100644 src/etap/Makefile.in + delete mode 100644 src/etap/etap.erl + delete mode 100644 src/etap/etap_application.erl + delete mode 100644 src/etap/etap_can.erl + delete mode 100644 src/etap/etap_exception.erl + delete mode 100644 src/etap/etap_process.erl + delete mode 100644 src/etap/etap_report.erl + delete mode 100644 src/etap/etap_request.erl + delete mode 100644 src/etap/etap_string.erl + delete mode 100644 src/etap/etap_web.erl + +diff --git a/configure b/configure +index b3040cf..26e0459 100755 +--- a/configure ++++ b/configure +@@ -12261,8 +12261,6 @@ ac_config_files="$ac_config_files src/couchdb/Makefile" + + ac_config_files="$ac_config_files src/couchdb/priv/Makefile" + +-ac_config_files="$ac_config_files src/etap/Makefile" +- + ac_config_files="$ac_config_files src/ibrowse/Makefile" + + ac_config_files="$ac_config_files src/mochiweb/Makefile" +@@ -13288,7 +13286,6 @@ do + "src/couchdb/couch.app.tpl") CONFIG_FILES="$CONFIG_FILES src/couchdb/couch.app.tpl" ;; + "src/couchdb/Makefile") CONFIG_FILES="$CONFIG_FILES src/couchdb/Makefile" ;; + "src/couchdb/priv/Makefile") CONFIG_FILES="$CONFIG_FILES src/couchdb/priv/Makefile" ;; +- "src/etap/Makefile") CONFIG_FILES="$CONFIG_FILES src/etap/Makefile" ;; + "src/ibrowse/Makefile") CONFIG_FILES="$CONFIG_FILES src/ibrowse/Makefile" ;; + "src/mochiweb/Makefile") CONFIG_FILES="$CONFIG_FILES src/mochiweb/Makefile" ;; + "test/Makefile") CONFIG_FILES="$CONFIG_FILES test/Makefile" ;; +diff --git a/configure.ac b/configure.ac +index 84a38d3..e9375be 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -387,7 +387,6 @@ AC_CONFIG_FILES([src/Makefile]) + AC_CONFIG_FILES([src/couchdb/couch.app.tpl]) + AC_CONFIG_FILES([src/couchdb/Makefile]) + AC_CONFIG_FILES([src/couchdb/priv/Makefile]) +-AC_CONFIG_FILES([src/etap/Makefile]) + AC_CONFIG_FILES([src/ibrowse/Makefile]) + AC_CONFIG_FILES([src/mochiweb/Makefile]) + AC_CONFIG_FILES([test/Makefile]) +diff --git a/src/Makefile.am b/src/Makefile.am +index e577138..19a5d20 100644 +--- a/src/Makefile.am ++++ b/src/Makefile.am +@@ -10,4 +10,4 @@ + ## License for the specific language governing permissions and limitations under + ## the License. + +-SUBDIRS = couchdb etap ibrowse mochiweb ++SUBDIRS = couchdb ibrowse mochiweb +diff --git a/src/Makefile.in b/src/Makefile.in +index 3fec89c..ae1b828 100644 +--- a/src/Makefile.in ++++ b/src/Makefile.in +@@ -248,7 +248,7 @@ version_minor = @version_minor@ + version_release = @version_release@ + version_revision = @version_revision@ + version_stage = @version_stage@ +-SUBDIRS = couchdb etap ibrowse mochiweb ++SUBDIRS = couchdb ibrowse mochiweb + all: all-recursive + + .SUFFIXES: +diff --git a/src/etap/Makefile.am b/src/etap/Makefile.am +deleted file mode 100644 +index 732347b..0000000 +--- a/src/etap/Makefile.am ++++ /dev/null +@@ -1,44 +0,0 @@ +-## 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. +- +-etapebindir = $(localerlanglibdir)/etap/ebin +- +-etap_file_collection = \ +- etap.erl \ +- etap_application.erl \ +- etap_can.erl \ +- etap_exception.erl \ +- etap_process.erl \ +- etap_report.erl \ +- etap_request.erl \ +- etap_string.erl \ +- etap_web.erl +- +-etapebin_make_generated_file_list = \ +- etap.beam \ +- etap_application.beam \ +- etap_can.beam \ +- etap_exception.beam \ +- etap_process.beam \ +- etap_report.beam \ +- etap_request.beam \ +- etap_string.beam \ +- etap_web.beam +- +-etapebin_DATA = $(etapebin_make_generated_file_list) +- +-EXTRA_DIST = $(etap_file_collection) +- +-CLEANFILES = $(etapebin_make_generated_file_list) +- +-%.beam: %.erl +- $(ERLC) $(ERLC_FLAGS) $< +diff --git a/src/etap/Makefile.in b/src/etap/Makefile.in +deleted file mode 100644 +index df91ff7..0000000 +--- a/src/etap/Makefile.in ++++ /dev/null +@@ -1,476 +0,0 @@ +-# Makefile.in generated by automake 1.11 from Makefile.am. +-# @configure_input@ +- +-# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, +-# 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation, +-# Inc. +-# This Makefile.in is free software; the Free Software Foundation +-# gives unlimited permission to copy and/or distribute it, +-# with or without modifications, as long as this notice is preserved. +- +-# This program is distributed in the hope that it will be useful, +-# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +-# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +-# PARTICULAR PURPOSE. +- +-@SET_MAKE@ +- +-VPATH = @srcdir@ +-pkgdatadir = $(datadir)/@PACKAGE@ +-pkgincludedir = $(includedir)/@PACKAGE@ +-pkglibdir = $(libdir)/@PACKAGE@ +-pkglibexecdir = $(libexecdir)/@PACKAGE@ +-am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +-install_sh_DATA = $(install_sh) -c -m 644 +-install_sh_PROGRAM = $(install_sh) -c +-install_sh_SCRIPT = $(install_sh) -c +-INSTALL_HEADER = $(INSTALL_DATA) +-transform = $(program_transform_name) +-NORMAL_INSTALL = : +-PRE_INSTALL = : +-POST_INSTALL = : +-NORMAL_UNINSTALL = : +-PRE_UNINSTALL = : +-POST_UNINSTALL = : +-build_triplet = @build@ +-host_triplet = @host@ +-subdir = src/etap +-DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in +-ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +-am__aclocal_m4_deps = $(top_srcdir)/m4/ac_check_curl.m4 \ +- $(top_srcdir)/m4/ac_check_icu.m4 $(top_srcdir)/m4/libtool.m4 \ +- $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \ +- $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \ +- $(top_srcdir)/acinclude.m4 $(top_srcdir)/configure.ac +-am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ +- $(ACLOCAL_M4) +-mkinstalldirs = $(install_sh) -d +-CONFIG_HEADER = $(top_builddir)/config.h +-CONFIG_CLEAN_FILES = +-CONFIG_CLEAN_VPATH_FILES = +-SOURCES = +-DIST_SOURCES = +-am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; +-am__vpath_adj = case $$p in \ +- $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ +- *) f=$$p;; \ +- esac; +-am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; +-am__install_max = 40 +-am__nobase_strip_setup = \ +- srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` +-am__nobase_strip = \ +- for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" +-am__nobase_list = $(am__nobase_strip_setup); \ +- for p in $$list; do echo "$$p $$p"; done | \ +- sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ +- $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ +- if (++n[$$2] == $(am__install_max)) \ +- { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ +- END { for (dir in files) print dir, files[dir] }' +-am__base_list = \ +- sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ +- sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' +-am__installdirs = "$(DESTDIR)$(etapebindir)" +-DATA = $(etapebin_DATA) +-DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +-ACLOCAL = @ACLOCAL@ +-AMTAR = @AMTAR@ +-AR = @AR@ +-AUTOCONF = @AUTOCONF@ +-AUTOHEADER = @AUTOHEADER@ +-AUTOMAKE = @AUTOMAKE@ +-AWK = @AWK@ +-CC = @CC@ +-CCDEPMODE = @CCDEPMODE@ +-CFLAGS = @CFLAGS@ +-CPP = @CPP@ +-CPPFLAGS = @CPPFLAGS@ +-CURL_CFLAGS = @CURL_CFLAGS@ +-CURL_CONFIG = @CURL_CONFIG@ +-CURL_LDFLAGS = @CURL_LDFLAGS@ +-CURL_LIBS = @CURL_LIBS@ +-CYGPATH_W = @CYGPATH_W@ +-DEFS = @DEFS@ +-DEPDIR = @DEPDIR@ +-DSYMUTIL = @DSYMUTIL@ +-DUMPBIN = @DUMPBIN@ +-ECHO_C = @ECHO_C@ +-ECHO_N = @ECHO_N@ +-ECHO_T = @ECHO_T@ +-EGREP = @EGREP@ +-ERL = @ERL@ +-ERLC = @ERLC@ +-ERLC_FLAGS = @ERLC_FLAGS@ +-EXEEXT = @EXEEXT@ +-FGREP = @FGREP@ +-FLAGS = @FLAGS@ +-GREP = @GREP@ +-HELP2MAN_EXECUTABLE = @HELP2MAN_EXECUTABLE@ +-ICU_CFLAGS = @ICU_CFLAGS@ +-ICU_CONFIG = @ICU_CONFIG@ +-ICU_CXXFLAGS = @ICU_CXXFLAGS@ +-ICU_LIBS = @ICU_LIBS@ +-ICU_LOCAL_BIN = @ICU_LOCAL_BIN@ +-ICU_LOCAL_CFLAGS = @ICU_LOCAL_CFLAGS@ +-ICU_LOCAL_LDFLAGS = @ICU_LOCAL_LDFLAGS@ +-INNO_COMPILER_EXECUTABLE = @INNO_COMPILER_EXECUTABLE@ +-INSTALL = @INSTALL@ +-INSTALL_DATA = @INSTALL_DATA@ +-INSTALL_PROGRAM = @INSTALL_PROGRAM@ +-INSTALL_SCRIPT = @INSTALL_SCRIPT@ +-INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +-JSLIB = @JSLIB@ +-JS_LIB_BASE = @JS_LIB_BASE@ +-JS_LIB_BINARY = @JS_LIB_BINARY@ +-JS_LIB_DIR = @JS_LIB_DIR@ +-LD = @LD@ +-LDFLAGS = @LDFLAGS@ +-LIBOBJS = @LIBOBJS@ +-LIBS = @LIBS@ +-LIBTOOL = @LIBTOOL@ +-LIPO = @LIPO@ +-LN_S = @LN_S@ +-LTLIBOBJS = @LTLIBOBJS@ +-MAKEINFO = @MAKEINFO@ +-MKDIR_P = @MKDIR_P@ +-NM = @NM@ +-NMEDIT = @NMEDIT@ +-OBJDUMP = @OBJDUMP@ +-OBJEXT = @OBJEXT@ +-OTOOL = @OTOOL@ +-OTOOL64 = @OTOOL64@ +-PACKAGE = @PACKAGE@ +-PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +-PACKAGE_NAME = @PACKAGE_NAME@ +-PACKAGE_STRING = @PACKAGE_STRING@ +-PACKAGE_TARNAME = @PACKAGE_TARNAME@ +-PACKAGE_URL = @PACKAGE_URL@ +-PACKAGE_VERSION = @PACKAGE_VERSION@ +-PATH_SEPARATOR = @PATH_SEPARATOR@ +-RANLIB = @RANLIB@ +-SED = @SED@ +-SET_MAKE = @SET_MAKE@ +-SHELL = @SHELL@ +-STRIP = @STRIP@ +-VERSION = @VERSION@ +-abs_builddir = @abs_builddir@ +-abs_srcdir = @abs_srcdir@ +-abs_top_builddir = @abs_top_builddir@ +-abs_top_srcdir = @abs_top_srcdir@ +-ac_ct_CC = @ac_ct_CC@ +-ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +-am__include = @am__include@ +-am__leading_dot = @am__leading_dot@ +-am__quote = @am__quote@ +-am__tar = @am__tar@ +-am__untar = @am__untar@ +-bindir = @bindir@ +-bug_uri = @bug_uri@ +-build = @build@ +-build_alias = @build_alias@ +-build_cpu = @build_cpu@ +-build_os = @build_os@ +-build_vendor = @build_vendor@ +-builddir = @builddir@ +-datadir = @datadir@ +-datarootdir = @datarootdir@ +-docdir = @docdir@ +-dvidir = @dvidir@ +-exec_prefix = @exec_prefix@ +-host = @host@ +-host_alias = @host_alias@ +-host_cpu = @host_cpu@ +-host_os = @host_os@ +-host_vendor = @host_vendor@ +-htmldir = @htmldir@ +-includedir = @includedir@ +-infodir = @infodir@ +-initdir = @initdir@ +-install_sh = @install_sh@ +-launchddir = @launchddir@ +-libdir = @libdir@ +-libexecdir = @libexecdir@ +-localconfdir = @localconfdir@ +-localdatadir = @localdatadir@ +-localdocdir = @localdocdir@ +-localedir = @localedir@ +-localerlanglibdir = @localerlanglibdir@ +-locallibbindir = @locallibbindir@ +-locallibdir = @locallibdir@ +-localstatedir = @localstatedir@ +-localstatelibdir = @localstatelibdir@ +-localstatelogdir = @localstatelogdir@ +-localstaterundir = @localstaterundir@ +-lt_ECHO = @lt_ECHO@ +-mandir = @mandir@ +-mkdir_p = @mkdir_p@ +-msvc_redist_dir = @msvc_redist_dir@ +-msvc_redist_name = @msvc_redist_name@ +-oldincludedir = @oldincludedir@ +-openssl_bin_dir = @openssl_bin_dir@ +-package_author_address = @package_author_address@ +-package_author_name = @package_author_name@ +-package_identifier = @package_identifier@ +-package_name = @package_name@ +-package_tarname = @package_tarname@ +-pdfdir = @pdfdir@ +-prefix = @prefix@ +-program_transform_name = @program_transform_name@ +-psdir = @psdir@ +-sbindir = @sbindir@ +-sharedstatedir = @sharedstatedir@ +-srcdir = @srcdir@ +-sysconfdir = @sysconfdir@ +-target_alias = @target_alias@ +-top_build_prefix = @top_build_prefix@ +-top_builddir = @top_builddir@ +-top_srcdir = @top_srcdir@ +-version = @version@ +-version_major = @version_major@ +-version_minor = @version_minor@ +-version_release = @version_release@ +-version_revision = @version_revision@ +-version_stage = @version_stage@ +-etapebindir = $(localerlanglibdir)/etap/ebin +-etap_file_collection = \ +- etap.erl \ +- etap_application.erl \ +- etap_can.erl \ +- etap_exception.erl \ +- etap_process.erl \ +- etap_report.erl \ +- etap_request.erl \ +- etap_string.erl \ +- etap_web.erl +- +-etapebin_make_generated_file_list = \ +- etap.beam \ +- etap_application.beam \ +- etap_can.beam \ +- etap_exception.beam \ +- etap_process.beam \ +- etap_report.beam \ +- etap_request.beam \ +- etap_string.beam \ +- etap_web.beam +- +-etapebin_DATA = $(etapebin_make_generated_file_list) +-EXTRA_DIST = $(etap_file_collection) +-CLEANFILES = $(etapebin_make_generated_file_list) +-all: all-am +- +-.SUFFIXES: +-$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) +- @for dep in $?; do \ +- case '$(am__configure_deps)' in \ +- *$$dep*) \ +- ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ +- && { if test -f $@; then exit 0; else break; fi; }; \ +- exit 1;; \ +- esac; \ +- done; \ +- echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/etap/Makefile'; \ +- $(am__cd) $(top_srcdir) && \ +- $(AUTOMAKE) --foreign src/etap/Makefile +-.PRECIOUS: Makefile +-Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status +- @case '$?' in \ +- *config.status*) \ +- cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ +- *) \ +- echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ +- cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ +- esac; +- +-$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) +- cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +- +-$(top_srcdir)/configure: $(am__configure_deps) +- cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +-$(ACLOCAL_M4): $(am__aclocal_m4_deps) +- cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +-$(am__aclocal_m4_deps): +- +-mostlyclean-libtool: +- -rm -f *.lo +- +-clean-libtool: +- -rm -rf .libs _libs +-install-etapebinDATA: $(etapebin_DATA) +- @$(NORMAL_INSTALL) +- test -z "$(etapebindir)" || $(MKDIR_P) "$(DESTDIR)$(etapebindir)" +- @list='$(etapebin_DATA)'; test -n "$(etapebindir)" || list=; \ +- for p in $$list; do \ +- if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ +- echo "$$d$$p"; \ +- done | $(am__base_list) | \ +- while read files; do \ +- echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(etapebindir)'"; \ +- $(INSTALL_DATA) $$files "$(DESTDIR)$(etapebindir)" || exit $$?; \ +- done +- +-uninstall-etapebinDATA: +- @$(NORMAL_UNINSTALL) +- @list='$(etapebin_DATA)'; test -n "$(etapebindir)" || list=; \ +- files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ +- test -n "$$files" || exit 0; \ +- echo " ( cd '$(DESTDIR)$(etapebindir)' && rm -f" $$files ")"; \ +- cd "$(DESTDIR)$(etapebindir)" && rm -f $$files +-tags: TAGS +-TAGS: +- +-ctags: CTAGS +-CTAGS: +- +- +-distdir: $(DISTFILES) +- @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ +- topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ +- list='$(DISTFILES)'; \ +- dist_files=`for file in $$list; do echo $$file; done | \ +- sed -e "s|^$$srcdirstrip/||;t" \ +- -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ +- case $$dist_files in \ +- */*) $(MKDIR_P) `echo "$$dist_files" | \ +- sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ +- sort -u` ;; \ +- esac; \ +- for file in $$dist_files; do \ +- if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ +- if test -d $$d/$$file; then \ +- dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ +- if test -d "$(distdir)/$$file"; then \ +- find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ +- fi; \ +- if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ +- cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ +- find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ +- fi; \ +- cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ +- else \ +- test -f "$(distdir)/$$file" \ +- || cp -p $$d/$$file "$(distdir)/$$file" \ +- || exit 1; \ +- fi; \ +- done +-check-am: all-am +-check: check-am +-all-am: Makefile $(DATA) +-installdirs: +- for dir in "$(DESTDIR)$(etapebindir)"; do \ +- test -z "$$dir" || $(MKDIR_P) "$$dir"; \ +- done +-install: install-am +-install-exec: install-exec-am +-install-data: install-data-am +-uninstall: uninstall-am +- +-install-am: all-am +- @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am +- +-installcheck: installcheck-am +-install-strip: +- $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ +- install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ +- `test -z '$(STRIP)' || \ +- echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +-mostlyclean-generic: +- +-clean-generic: +- -test -z "$(CLEANFILES)" || rm -f $(CLEANFILES) +- +-distclean-generic: +- -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) +- -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) +- +-maintainer-clean-generic: +- @echo "This command is intended for maintainers to use" +- @echo "it deletes files that may require special tools to rebuild." +-clean: clean-am +- +-clean-am: clean-generic clean-libtool mostlyclean-am +- +-distclean: distclean-am +- -rm -f Makefile +-distclean-am: clean-am distclean-generic +- +-dvi: dvi-am +- +-dvi-am: +- +-html: html-am +- +-html-am: +- +-info: info-am +- +-info-am: +- +-install-data-am: install-etapebinDATA +- +-install-dvi: install-dvi-am +- +-install-dvi-am: +- +-install-exec-am: +- +-install-html: install-html-am +- +-install-html-am: +- +-install-info: install-info-am +- +-install-info-am: +- +-install-man: +- +-install-pdf: install-pdf-am +- +-install-pdf-am: +- +-install-ps: install-ps-am +- +-install-ps-am: +- +-installcheck-am: +- +-maintainer-clean: maintainer-clean-am +- -rm -f Makefile +-maintainer-clean-am: distclean-am maintainer-clean-generic +- +-mostlyclean: mostlyclean-am +- +-mostlyclean-am: mostlyclean-generic mostlyclean-libtool +- +-pdf: pdf-am +- +-pdf-am: +- +-ps: ps-am +- +-ps-am: +- +-uninstall-am: uninstall-etapebinDATA +- +-.MAKE: install-am install-strip +- +-.PHONY: all all-am check check-am clean clean-generic clean-libtool \ +- distclean distclean-generic distclean-libtool distdir dvi \ +- dvi-am html html-am info info-am install install-am \ +- install-data install-data-am install-dvi install-dvi-am \ +- install-etapebinDATA install-exec install-exec-am install-html \ +- install-html-am install-info install-info-am install-man \ +- install-pdf install-pdf-am install-ps install-ps-am \ +- install-strip installcheck installcheck-am installdirs \ +- maintainer-clean maintainer-clean-generic mostlyclean \ +- mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ +- uninstall uninstall-am uninstall-etapebinDATA +- +- +-%.beam: %.erl +- $(ERLC) $(ERLC_FLAGS) $< +- +-# Tell versions [3.59,3.63) of GNU make to not export all variables. +-# Otherwise a system limit (for SysV at least) may be exceeded. +-.NOEXPORT: +diff --git a/src/etap/etap.erl b/src/etap/etap.erl +deleted file mode 100644 +index 5ad5dba..0000000 +--- a/src/etap/etap.erl ++++ /dev/null +@@ -1,416 +0,0 @@ +-%% Copyright (c) 2008-2009 Nick Gerakines +-%% +-%% 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. +-%% +-%% @author Nick Gerakines [http://socklabs.com/] +-%% @author Jeremy Wall +-%% @version 0.3.4 +-%% @copyright 2007-2008 Jeremy Wall, 2008-2009 Nick Gerakines +-%% @reference http://testanything.org/wiki/index.php/Main_Page +-%% @reference http://en.wikipedia.org/wiki/Test_Anything_Protocol +-%% @todo Finish implementing the skip directive. +-%% @todo Document the messages handled by this receive loop. +-%% @todo Explain in documentation why we use a process to handle test input. +-%% @doc etap is a TAP testing module for Erlang components and applications. +-%% This module allows developers to test their software using the TAP method. +-%% +-%%

+-%% TAP, the Test Anything Protocol, is a simple text-based interface between +-%% testing modules in a test harness. TAP started life as part of the test +-%% harness for Perl but now has implementations in C/C++, Python, PHP, Perl +-%% and probably others by the time you read this. +-%%

+-%% +-%% The testing process begins by defining a plan using etap:plan/1, running +-%% a number of etap tests and then calling eta:end_tests/0. Please refer to +-%% the Erlang modules in the t directory of this project for example tests. +--module(etap). +--export([ +- ensure_test_server/0, start_etap_server/0, test_server/1, +- diag/1, diag/2, plan/1, end_tests/0, not_ok/2, ok/2, is/3, isnt/3, +- any/3, none/3, fun_is/3, is_greater/3, skip/1, skip/2, +- ensure_coverage_starts/0, ensure_coverage_ends/0, coverage_report/0, +- datetime/1, skip/3, bail/0, bail/1 +-]). +--record(test_state, {planned = 0, count = 0, pass = 0, fail = 0, skip = 0, skip_reason = ""}). +--vsn("0.3.4"). +- +-%% @spec plan(N) -> Result +-%% N = unknown | skip | {skip, string()} | integer() +-%% Result = ok +-%% @doc Create a test plan and boot strap the test server. +-plan(unknown) -> +- ensure_coverage_starts(), +- ensure_test_server(), +- etap_server ! {self(), plan, unknown}, +- ok; +-plan(skip) -> +- io:format("1..0 # skip~n"); +-plan({skip, Reason}) -> +- io:format("1..0 # skip ~s~n", [Reason]); +-plan(N) when is_integer(N), N > 0 -> +- ensure_coverage_starts(), +- ensure_test_server(), +- etap_server ! {self(), plan, N}, +- ok. +- +-%% @spec end_tests() -> ok +-%% @doc End the current test plan and output test results. +-%% @todo This should probably be done in the test_server process. +-end_tests() -> +- ensure_coverage_ends(), +- etap_server ! {self(), state}, +- State = receive X -> X end, +- if +- State#test_state.planned == -1 -> +- io:format("1..~p~n", [State#test_state.count]); +- true -> +- ok +- end, +- case whereis(etap_server) of +- undefined -> ok; +- _ -> etap_server ! done, ok +- end. +- +-%% @private +-ensure_coverage_starts() -> +- case os:getenv("COVER") of +- false -> ok; +- _ -> +- BeamDir = case os:getenv("COVER_BIN") of false -> "ebin"; X -> X end, +- cover:compile_beam_directory(BeamDir) +- end. +- +-%% @private +-%% @doc Attempts to write out any collected coverage data to the cover/ +-%% directory. This function should not be called externally, but it could be. +-ensure_coverage_ends() -> +- case os:getenv("COVER") of +- false -> ok; +- _ -> +- filelib:ensure_dir("cover/"), +- Name = lists:flatten([ +- io_lib:format("~.16b", [X]) || X <- binary_to_list(erlang:md5( +- term_to_binary({make_ref(), now()}) +- )) +- ]), +- cover:export("cover/" ++ Name ++ ".coverdata") +- end. +- +-%% @spec coverage_report() -> ok +-%% @doc Use the cover module's covreage report builder to create code coverage +-%% reports from recently created coverdata files. +-coverage_report() -> +- [cover:import(File) || File <- filelib:wildcard("cover/*.coverdata")], +- lists:foreach( +- fun(Mod) -> +- cover:analyse_to_file(Mod, atom_to_list(Mod) ++ "_coverage.txt", []) +- end, +- cover:imported_modules() +- ), +- ok. +- +-bail() -> +- bail(""). +- +-bail(Reason) -> +- etap_server ! {self(), diag, "Bail out! " ++ Reason}, +- ensure_coverage_ends(), +- etap_server ! done, ok, +- ok. +- +- +-%% @spec diag(S) -> ok +-%% S = string() +-%% @doc Print a debug/status message related to the test suite. +-diag(S) -> etap_server ! {self(), diag, "# " ++ S}, ok. +- +-%% @spec diag(Format, Data) -> ok +-%% Format = atom() | string() | binary() +-%% Data = [term()] +-%% UnicodeList = [Unicode] +-%% Unicode = int() +-%% @doc Print a debug/status message related to the test suite. +-%% Function arguments are passed through io_lib:format/2. +-diag(Format, Data) -> diag(io_lib:format(Format, Data)). +- +-%% @spec ok(Expr, Desc) -> Result +-%% Expr = true | false +-%% Desc = string() +-%% Result = true | false +-%% @doc Assert that a statement is true. +-ok(Expr, Desc) -> mk_tap(Expr == true, Desc). +- +-%% @spec not_ok(Expr, Desc) -> Result +-%% Expr = true | false +-%% Desc = string() +-%% Result = true | false +-%% @doc Assert that a statement is false. +-not_ok(Expr, Desc) -> mk_tap(Expr == false, Desc). +- +-%% @spec is(Got, Expected, Desc) -> Result +-%% Got = any() +-%% Expected = any() +-%% Desc = string() +-%% Result = true | false +-%% @doc Assert that two values are the same. +-is(Got, Expected, Desc) -> +- case mk_tap(Got == Expected, Desc) of +- false -> +- etap_server ! {self(), diag, " ---"}, +- etap_server ! {self(), diag, io_lib:format(" description: ~p", [Desc])}, +- etap_server ! {self(), diag, io_lib:format(" found: ~p", [Got])}, +- etap_server ! {self(), diag, io_lib:format(" wanted: ~p", [Expected])}, +- etap_server ! {self(), diag, " ..."}, +- false; +- true -> true +- end. +- +-%% @spec isnt(Got, Expected, Desc) -> Result +-%% Got = any() +-%% Expected = any() +-%% Desc = string() +-%% Result = true | false +-%% @doc Assert that two values are not the same. +-isnt(Got, Expected, Desc) -> mk_tap(Got /= Expected, Desc). +- +-%% @spec is_greater(ValueA, ValueB, Desc) -> Result +-%% ValueA = number() +-%% ValueB = number() +-%% Desc = string() +-%% Result = true | false +-%% @doc Assert that an integer is greater than another. +-is_greater(ValueA, ValueB, Desc) when is_integer(ValueA), is_integer(ValueB) -> +- mk_tap(ValueA > ValueB, Desc). +- +-%% @spec any(Got, Items, Desc) -> Result +-%% Got = any() +-%% Items = [any()] +-%% Desc = string() +-%% Result = true | false +-%% @doc Assert that an item is in a list. +-any(Got, Items, Desc) -> +- is(lists:member(Got, Items), true, Desc). +- +-%% @spec none(Got, Items, Desc) -> Result +-%% Got = any() +-%% Items = [any()] +-%% Desc = string() +-%% Result = true | false +-%% @doc Assert that an item is not in a list. +-none(Got, Items, Desc) -> +- is(lists:member(Got, Items), false, Desc). +- +-%% @spec fun_is(Fun, Expected, Desc) -> Result +-%% Fun = function() +-%% Expected = any() +-%% Desc = string() +-%% Result = true | false +-%% @doc Use an anonymous function to assert a pattern match. +-fun_is(Fun, Expected, Desc) when is_function(Fun) -> +- is(Fun(Expected), true, Desc). +- +-%% @equiv skip(TestFun, "") +-skip(TestFun) when is_function(TestFun) -> +- skip(TestFun, ""). +- +-%% @spec skip(TestFun, Reason) -> ok +-%% TestFun = function() +-%% Reason = string() +-%% @doc Skip a test. +-skip(TestFun, Reason) when is_function(TestFun), is_list(Reason) -> +- begin_skip(Reason), +- catch TestFun(), +- end_skip(), +- ok. +- +-%% @spec skip(Q, TestFun, Reason) -> ok +-%% Q = true | false | function() +-%% TestFun = function() +-%% Reason = string() +-%% @doc Skips a test conditionally. The first argument to this function can +-%% either be the 'true' or 'false' atoms or a function that returns 'true' or +-%% 'false'. +-skip(QFun, TestFun, Reason) when is_function(QFun), is_function(TestFun), is_list(Reason) -> +- case QFun() of +- true -> begin_skip(Reason), TestFun(), end_skip(); +- _ -> TestFun() +- end, +- ok; +- +-skip(Q, TestFun, Reason) when is_function(TestFun), is_list(Reason), Q == true -> +- begin_skip(Reason), +- TestFun(), +- end_skip(), +- ok; +- +-skip(_, TestFun, Reason) when is_function(TestFun), is_list(Reason) -> +- TestFun(), +- ok. +- +-%% @private +-begin_skip(Reason) -> +- etap_server ! {self(), begin_skip, Reason}. +- +-%% @private +-end_skip() -> +- etap_server ! {self(), end_skip}. +- +-% --- +-% Internal / Private functions +- +-%% @private +-%% @doc Start the etap_server process if it is not running already. +-ensure_test_server() -> +- case whereis(etap_server) of +- undefined -> +- proc_lib:start(?MODULE, start_etap_server,[]); +- _ -> +- diag("The test server is already running.") +- end. +- +-%% @private +-%% @doc Start the etap_server loop and register itself as the etap_server +-%% process. +-start_etap_server() -> +- catch register(etap_server, self()), +- proc_lib:init_ack(ok), +- etap:test_server(#test_state{ +- planned = 0, +- count = 0, +- pass = 0, +- fail = 0, +- skip = 0, +- skip_reason = "" +- }). +- +- +-%% @private +-%% @doc The main etap_server receive/run loop. The etap_server receive loop +-%% responds to seven messages apperatining to failure or passing of tests. +-%% It is also used to initiate the testing process with the {_, plan, _} +-%% message that clears the current test state. +-test_server(State) -> +- NewState = receive +- {_From, plan, unknown} -> +- io:format("# Current time local ~s~n", [datetime(erlang:localtime())]), +- io:format("# Using etap version ~p~n", [ proplists:get_value(vsn, proplists:get_value(attributes, etap:module_info())) ]), +- State#test_state{ +- planned = -1, +- count = 0, +- pass = 0, +- fail = 0, +- skip = 0, +- skip_reason = "" +- }; +- {_From, plan, N} -> +- io:format("# Current time local ~s~n", [datetime(erlang:localtime())]), +- io:format("# Using etap version ~p~n", [ proplists:get_value(vsn, proplists:get_value(attributes, etap:module_info())) ]), +- io:format("1..~p~n", [N]), +- State#test_state{ +- planned = N, +- count = 0, +- pass = 0, +- fail = 0, +- skip = 0, +- skip_reason = "" +- }; +- {_From, begin_skip, Reason} -> +- State#test_state{ +- skip = 1, +- skip_reason = Reason +- }; +- {_From, end_skip} -> +- State#test_state{ +- skip = 0, +- skip_reason = "" +- }; +- {_From, pass, Desc} -> +- FullMessage = skip_diag( +- " - " ++ Desc, +- State#test_state.skip, +- State#test_state.skip_reason +- ), +- io:format("ok ~p ~s~n", [State#test_state.count + 1, FullMessage]), +- State#test_state{ +- count = State#test_state.count + 1, +- pass = State#test_state.pass + 1 +- }; +- +- {_From, fail, Desc} -> +- FullMessage = skip_diag( +- " - " ++ Desc, +- State#test_state.skip, +- State#test_state.skip_reason +- ), +- io:format("not ok ~p ~s~n", [State#test_state.count + 1, FullMessage]), +- State#test_state{ +- count = State#test_state.count + 1, +- fail = State#test_state.fail + 1 +- }; +- {From, state} -> +- From ! State, +- State; +- {_From, diag, Message} -> +- io:format("~s~n", [Message]), +- State; +- {From, count} -> +- From ! State#test_state.count, +- State; +- {From, is_skip} -> +- From ! State#test_state.skip, +- State; +- done -> +- exit(normal) +- end, +- test_server(NewState). +- +-%% @private +-%% @doc Process the result of a test and send it to the etap_server process. +-mk_tap(Result, Desc) -> +- IsSkip = lib:sendw(etap_server, is_skip), +- case [IsSkip, Result] of +- [_, true] -> +- etap_server ! {self(), pass, Desc}, +- true; +- [1, _] -> +- etap_server ! {self(), pass, Desc}, +- true; +- _ -> +- etap_server ! {self(), fail, Desc}, +- false +- end. +- +-%% @private +-%% @doc Format a date/time string. +-datetime(DateTime) -> +- {{Year, Month, Day}, {Hour, Min, Sec}} = DateTime, +- io_lib:format("~4.10.0B-~2.10.0B-~2.10.0B ~2.10.0B:~2.10.0B:~2.10.0B", [Year, Month, Day, Hour, Min, Sec]). +- +-%% @private +-%% @doc Craft an output message taking skip/todo into consideration. +-skip_diag(Message, 0, _) -> +- Message; +-skip_diag(_Message, 1, "") -> +- " # SKIP"; +-skip_diag(_Message, 1, Reason) -> +- " # SKIP : " ++ Reason. +diff --git a/src/etap/etap_application.erl b/src/etap/etap_application.erl +deleted file mode 100644 +index 98b5275..0000000 +--- a/src/etap/etap_application.erl ++++ /dev/null +@@ -1,72 +0,0 @@ +-%% Copyright (c) 2008-2009 Nick Gerakines +-%% +-%% 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. +-%% +-%% @author Nick Gerakines [http://socklabs.com/] +-%% @copyright 2008 Nick Gerakines +-%% @reference http://testanything.org/wiki/index.php/Main_Page +-%% @reference http://en.wikipedia.org/wiki/Test_Anything_Protocol +-%% @todo Explain in documentation why we use a process to handle test input. +-%% @todo Add test to verify the number of members in a pg2 group. +-%% @doc Provide test functionality to the application and related behaviors. +--module(etap_application). +--export([ +- start_ok/2, ensure_loaded/3, load_ok/2, +- pg2_group_exists/2, pg2_group_doesntexist/2 +-]). +- +-%% @spec load_ok(string(), string()) -> true | false +-%% @doc Assert that an application can be loaded successfully. +-load_ok(AppName, Desc) -> +- etap:ok(application:load(AppName) == ok, Desc). +- +-%% @spec start_ok(string(), string()) -> true | false +-%% @doc Assert that an application can be started successfully. +-start_ok(AppName, Desc) -> +- etap:ok(application:start(AppName) == ok, Desc). +- +-%% @spec ensure_loaded(string(), string(), string()) -> true | false +-%% @doc Assert that an application has been loaded successfully. +-ensure_loaded(AppName, AppVsn, Desc) -> +- etap:any( +- fun(Match) -> case Match of {AppName, _, AppVsn} -> true; _ -> false end end, +- application:loaded_applications(), +- Desc +- ). +- +-%% @spec pg2_group_exists(string(), string()) -> true | false +-%% @doc Assert that a pg2 group exists. +-pg2_group_exists(GroupName, Desc) -> +- etap:any( +- fun(Match) -> Match == GroupName end, +- pg2:which_groups(), +- Desc +- ). +- +-%% @spec pg2_group_doesntexist(string(), string()) -> true | false +-%% @doc Assert that a pg2 group does not exists. +-pg2_group_doesntexist(GroupName, Desc) -> +- etap:none( +- fun(Match) -> Match == GroupName end, +- pg2:which_groups(), +- Desc +- ). +diff --git a/src/etap/etap_can.erl b/src/etap/etap_can.erl +deleted file mode 100644 +index 552b717..0000000 +--- a/src/etap/etap_can.erl ++++ /dev/null +@@ -1,79 +0,0 @@ +-%% Copyright (c) 2008-2009 Nick Gerakines +-%% +-%% 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. +-%% +-%% @reference http://testanything.org/wiki/index.php/Main_Page +-%% @reference http://en.wikipedia.org/wiki/Test_Anything_Protocol +-%% @doc Provide test functionality modules +--module(etap_can). +- +--export([ +- loaded_ok/2, can_ok/2, can_ok/3, +- has_attrib/2, is_attrib/3, is_behaviour/2 +-]). +- +-%% @spec loaded_ok(atom(), string()) -> true | false +-%% @doc Assert that a module has been loaded successfully. +-loaded_ok(M, Desc) when is_atom(M) -> +- etap:fun_is(fun({module, _}) -> true; (_) -> false end, code:load_file(M), Desc). +- +-%% @spec can_ok(atom(), atom()) -> true | false +-%% @doc Assert that a module exports a given function. +-can_ok(M, F) when is_atom(M), is_atom(F) -> +- Matches = [X || {X, _} <- M:module_info(exports), X == F], +- etap:ok(Matches > 0, lists:concat([M, " can ", F])). +- +-%% @spec can_ok(atom(), atom(), integer()) -> true | false +-%% @doc Assert that a module exports a given function with a given arity. +-can_ok(M, F, A) when is_atom(M); is_atom(F), is_number(A) -> +- Matches = [X || X <- M:module_info(exports), X == {F, A}], +- etap:ok(Matches > 0, lists:concat([M, " can ", F, "/", A])). +- +-%% @spec has_attrib(M, A) -> true | false +-%% M = atom() +-%% A = atom() +-%% @doc Asserts that a module has a given attribute. +-has_attrib(M, A) when is_atom(M), is_atom(A) -> +- etap:isnt( +- proplists:get_value(A, M:module_info(attributes), 'asdlkjasdlkads'), +- 'asdlkjasdlkads', +- lists:concat([M, " has attribute ", A]) +- ). +- +-%% @spec has_attrib(M, A. V) -> true | false +-%% M = atom() +-%% A = atom() +-%% V = any() +-%% @doc Asserts that a module has a given attribute with a given value. +-is_attrib(M, A, V) when is_atom(M) andalso is_atom(A) -> +- etap:is( +- proplists:get_value(A, M:module_info(attributes)), +- [V], +- lists:concat([M, "'s ", A, " is ", V]) +- ). +- +-%% @spec is_behavior(M, B) -> true | false +-%% M = atom() +-%% B = atom() +-%% @doc Asserts that a given module has a specific behavior. +-is_behaviour(M, B) when is_atom(M) andalso is_atom(B) -> +- is_attrib(M, behaviour, B). +diff --git a/src/etap/etap_exception.erl b/src/etap/etap_exception.erl +deleted file mode 100644 +index ba66072..0000000 +--- a/src/etap/etap_exception.erl ++++ /dev/null +@@ -1,66 +0,0 @@ +-%% Copyright (c) 2008-2009 Nick Gerakines +-%% +-%% 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. +-%% +-%% @reference http://testanything.org/wiki/index.php/Main_Page +-%% @reference http://en.wikipedia.org/wiki/Test_Anything_Protocol +-%% @doc Adds exception based testing to the etap suite. +--module(etap_exception). +- +--export([dies_ok/2, lives_ok/2, throws_ok/3]). +- +-% --- +-% External / Public functions +- +-%% @doc Assert that an exception is raised when running a given function. +-dies_ok(F, Desc) -> +- case (catch F()) of +- {'EXIT', _} -> etap:ok(true, Desc); +- _ -> etap:ok(false, Desc) +- end. +- +-%% @doc Assert that an exception is not raised when running a given function. +-lives_ok(F, Desc) -> +- etap:is(try_this(F), success, Desc). +- +-%% @doc Assert that the exception thrown by a function matches the given exception. +-throws_ok(F, Exception, Desc) -> +- try F() of +- _ -> etap:ok(nok, Desc) +- catch +- _:E -> +- etap:is(E, Exception, Desc) +- end. +- +-% --- +-% Internal / Private functions +- +-%% @private +-%% @doc Run a function and catch any exceptions. +-try_this(F) when is_function(F, 0) -> +- try F() of +- _ -> success +- catch +- throw:E -> {throw, E}; +- error:E -> {error, E}; +- exit:E -> {exit, E} +- end. +diff --git a/src/etap/etap_process.erl b/src/etap/etap_process.erl +deleted file mode 100644 +index 69f5ba0..0000000 +--- a/src/etap/etap_process.erl ++++ /dev/null +@@ -1,42 +0,0 @@ +-%% Copyright (c) 2008-2009 Nick Gerakines +-%% +-%% 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. +-%% +-%% @doc Adds process/pid testing to the etap suite. +--module(etap_process). +- +--export([is_pid/2, is_alive/2, is_mfa/3]). +- +-% --- +-% External / Public functions +- +-%% @doc Assert that a given variable is a pid. +-is_pid(Pid, Desc) when is_pid(Pid) -> etap:ok(true, Desc); +-is_pid(_, Desc) -> etap:ok(false, Desc). +- +-%% @doc Assert that a given process/pid is alive. +-is_alive(Pid, Desc) -> +- etap:ok(erlang:is_process_alive(Pid), Desc). +- +-%% @doc Assert that the current function of a pid is a given {M, F, A} tuple. +-is_mfa(Pid, MFA, Desc) -> +- etap:is({current_function, MFA}, erlang:process_info(Pid, current_function), Desc). +diff --git a/src/etap/etap_report.erl b/src/etap/etap_report.erl +deleted file mode 100644 +index 6d692fb..0000000 +--- a/src/etap/etap_report.erl ++++ /dev/null +@@ -1,343 +0,0 @@ +-%% Copyright (c) 2008-2009 Nick Gerakines +-%% +-%% 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. +-%% +-%% @doc A module for creating nice looking code coverage reports. +--module(etap_report). +--export([create/0]). +- +-%% @spec create() -> ok +-%% @doc Create html code coverage reports for each module that code coverage +-%% data exists for. +-create() -> +- [cover:import(File) || File <- filelib:wildcard("cover/*.coverdata")], +- Modules = lists:foldl( +- fun(Module, Acc) -> +- [{Module, file_report(Module)} | Acc] +- end, +- [], +- cover:imported_modules() +- ), +- index(Modules). +- +-%% @private +-index(Modules) -> +- {ok, IndexFD} = file:open("cover/index.html", [write]), +- io:format(IndexFD, "", []), +- io:format(IndexFD, "", []), +- lists:foldl( +- fun({Module, {Good, Bad, Source}}, LastRow) -> +- case {Good + Bad, Source} of +- {0, _} -> LastRow; +- {_, none} -> LastRow; +- _ -> +- CovPer = round((Good / (Good + Bad)) * 100), +- UnCovPer = round((Bad / (Good + Bad)) * 100), +- RowClass = case LastRow of 1 -> "odd"; _ -> "even" end, +- io:format(IndexFD, "
", [RowClass]), +- io:format(IndexFD, "~s", [atom_to_list(Module) ++ "_report.html", atom_to_list(Module)]), +- io:format(IndexFD, " +- +- +- +- +-
~p%  +- +- +-
+-
+- ", [CovPer, CovPer, UnCovPer]), +- io:format(IndexFD, "
", []), +- case LastRow of +- 1 -> 0; +- 0 -> 1 +- end +- end +- end, +- 0, +- lists:sort(Modules) +- ), +- {TotalGood, TotalBad} = lists:foldl( +- fun({_, {Good, Bad, Source}}, {TGood, TBad}) -> +- case Source of none -> {TGood, TBad}; _ -> {TGood + Good, TBad + Bad} end +- end, +- {0, 0}, +- Modules +- ), +- io:format(IndexFD, "

Generated on ~s.

~n", [etap:datetime({date(), time()})]), +- case TotalGood + TotalBad of +- 0 -> ok; +- _ -> +- TotalCovPer = round((TotalGood / (TotalGood + TotalBad)) * 100), +- TotalUnCovPer = round((TotalBad / (TotalGood + TotalBad)) * 100), +- io:format(IndexFD, "
", []), +- io:format(IndexFD, "Total +- +- +- +- +-
~p%  +- +- +-
+-
+- ", [TotalCovPer, TotalCovPer, TotalUnCovPer]), +- io:format(IndexFD, "
", []) +- end, +- io:format(IndexFD, "", []), +- file:close(IndexFD), +- ok. +- +-%% @private +-file_report(Module) -> +- {ok, Data} = cover:analyse(Module, calls, line), +- Source = find_source(Module), +- {Good, Bad} = collect_coverage(Data, {0, 0}), +- case {Source, Good + Bad} of +- {none, _} -> ok; +- {_, 0} -> ok; +- _ -> +- {ok, SourceFD} = file:open(Source, [read]), +- {ok, WriteFD} = file:open("cover/" ++ atom_to_list(Module) ++ "_report.html", [write]), +- io:format(WriteFD, "~s", [header(Module, Good, Bad)]), +- output_lines(Data, WriteFD, SourceFD, 1), +- io:format(WriteFD, "~s", [footer()]), +- file:close(WriteFD), +- file:close(SourceFD), +- ok +- end, +- {Good, Bad, Source}. +- +-%% @private +-collect_coverage([], Acc) -> Acc; +-collect_coverage([{{_, _}, 0} | Data], {Good, Bad}) -> +- collect_coverage(Data, {Good, Bad + 1}); +-collect_coverage([_ | Data], {Good, Bad}) -> +- collect_coverage(Data, {Good + 1, Bad}). +- +-%% @private +-output_lines(Data, WriteFD, SourceFD, LineNumber) -> +- {Match, NextData} = datas_match(Data, LineNumber), +- case io:get_line(SourceFD, '') of +- eof -> ok; +- Line = "%% @todo" ++ _ -> +- io:format(WriteFD, "~s", [out_line(LineNumber, highlight, Line)]), +- output_lines(NextData, WriteFD, SourceFD, LineNumber + 1); +- Line = "% " ++ _ -> +- io:format(WriteFD, "~s", [out_line(LineNumber, none, Line)]), +- output_lines(NextData, WriteFD, SourceFD, LineNumber + 1); +- Line -> +- case Match of +- {true, CC} -> +- io:format(WriteFD, "~s", [out_line(LineNumber, CC, Line)]), +- output_lines(NextData, WriteFD, SourceFD, LineNumber + 1); +- false -> +- io:format(WriteFD, "~s", [out_line(LineNumber, none, Line)]), +- output_lines(NextData, WriteFD, SourceFD, LineNumber + 1) +- end +- end. +- +-%% @private +-out_line(Number, none, Line) -> +- PadNu = string:right(integer_to_list(Number), 5, $.), +- io_lib:format("~s ~s", [Number, PadNu, Line]); +-out_line(Number, highlight, Line) -> +- PadNu = string:right(integer_to_list(Number), 5, $.), +- io_lib:format("~s ~s", [Number, PadNu, Line]); +-out_line(Number, 0, Line) -> +- PadNu = string:right(integer_to_list(Number), 5, $.), +- io_lib:format("~s ~s", [Number, PadNu, Line]); +-out_line(Number, _, Line) -> +- PadNu = string:right(integer_to_list(Number), 5, $.), +- io_lib:format("~s ~s", [Number, PadNu, Line]). +- +-%% @private +-datas_match([], _) -> {false, []}; +-datas_match([{{_, Line}, CC} | Datas], LineNumber) when Line == LineNumber -> {{true, CC}, Datas}; +-datas_match(Data, _) -> {false, Data}. +- +-%% @private +-find_source(Module) when is_atom(Module) -> +- Root = filename:rootname(Module), +- Dir = filename:dirname(Root), +- XDir = case os:getenv("SRC") of false -> "src"; X -> X end, +- find_source([ +- filename:join([Dir, Root ++ ".erl"]), +- filename:join([Dir, "..", "src", Root ++ ".erl"]), +- filename:join([Dir, "src", Root ++ ".erl"]), +- filename:join([Dir, "elibs", Root ++ ".erl"]), +- filename:join([Dir, "..", "elibs", Root ++ ".erl"]), +- filename:join([Dir, XDir, Root ++ ".erl"]) +- ]); +-find_source([]) -> none; +-find_source([Test | Tests]) -> +- case filelib:is_file(Test) of +- true -> Test; +- false -> find_source(Tests) +- end. +- +-%% @private +-header(Module, Good, Bad) -> +- io:format("Good ~p~n", [Good]), +- io:format("Bad ~p~n", [Bad]), +- CovPer = round((Good / (Good + Bad)) * 100), +- UnCovPer = round((Bad / (Good + Bad)) * 100), +- io:format("CovPer ~p~n", [CovPer]), +- io_lib:format(" +- +- +- ~s - C0 code coverage information +- +- +- +- +-

C0 code coverage information

+-

Generated on ~s with etap 0.3.4. +-

+- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +- +-
NameTotal linesLines of codeTotal coverageCode coverage
+- ~s +- +- ?? +- +- ?? +- +- ?? +- +- +- +- +- +-
~p%  +- +- +-
+-
+-
", [Module, etap:datetime({date(), time()}), atom_to_list(Module) ++ "_report.html", Module, CovPer, CovPer, UnCovPer]).
+-
+-%% @private
+-footer() ->
+-    "

Generated using etap 0.3.4.

+- +- +- ". +diff --git a/src/etap/etap_request.erl b/src/etap/etap_request.erl +deleted file mode 100644 +index 9fd23ac..0000000 +--- a/src/etap/etap_request.erl ++++ /dev/null +@@ -1,89 +0,0 @@ +-%% Copyright (c) 2008-2009 Nick Gerakines +-%% +-%% 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. +-%% +-%% @doc Provides test functionality against a specific web request. Many of +-%% the exported methods can be used to build your own more complex tests. +--module(etap_request, [Method, Url, InHeaders, InBody, Status, OutHeaders, OutBody]). +- +--export([status_is/2]). +- +--export([ +- method/0, url/0, status/0, status_code/0, status_line/0, rheaders/0, +- has_rheader/1, rheader/1, rbody/0, header_is/3, body_is/2, +- body_has_string/2 +-]). +- +-% --- +-% Tests +- +-%% @doc Assert that response status code is the given status code. +-status_is(Code, Desc) -> +- etap:is(status_code(), Code, Desc). +- +-header_is(Name, Value, Desc) -> +- etap:is(rheader(Name), Value, Desc). +- +-body_is(Value, Desc) -> +- etap:is(rbody(), Value, Desc). +- +-body_has_string(String, Desc) when is_list(OutBody), is_list(String) -> +- etap_string:contains_ok(OutBody, String, Desc). +- +-% --- +-% Accessor functions +- +-%% @doc Access a request's method. +-method() -> Method. +- +-%% @doc Access a request's URL. +-url() -> Url. +- +-%% @doc Access a request's status. +-status() -> Status. +- +-%% @doc Access a request's status code. +-status_code() -> +- {_, Code, _} = Status, +- Code. +- +-%% @doc Access a request's status line. +-status_line() -> +- {_, _, Line} = Status, +- Line. +- +-%% @doc Access a request's headers. +-rheaders() -> OutHeaders. +- +-%% @doc Dertermine if a specific request header exists. +-has_rheader(Key) -> +- lists:keymember(Key, 1, OutHeaders). +- +-%% @doc Return a specific request header. +-rheader(Key) -> +- case lists:keysearch(Key, 1, OutHeaders) of +- false -> undefined; +- {value, {Key, Value}} -> Value +- end. +- +-%% @doc Access the request's body. +-rbody() -> OutBody. +diff --git a/src/etap/etap_string.erl b/src/etap/etap_string.erl +deleted file mode 100644 +index 67aa3d5..0000000 +--- a/src/etap/etap_string.erl ++++ /dev/null +@@ -1,47 +0,0 @@ +-%% Copyright (c) 2008-2009 Nick Gerakines +-%% +-%% 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. +-%% +-%% @author Nick Gerakines [http://socklabs.com/] +-%% @copyright 2008 Nick Gerakines +-%% @doc Provide testing functionality for strings. +--module(etap_string). +- +--export([contains_ok/3, is_before/4]). +- +-%% @spec contains_ok(string(), string(), string()) -> true | false +-%% @doc Assert that a string is contained in another string. +-contains_ok(Source, String, Desc) -> +- etap:isnt( +- string:str(Source, String), +- 0, +- Desc +- ). +- +-%% @spec is_before(string(), string(), string(), string()) -> true | false +-%% @doc Assert that a string comes before another string within a larger body. +-is_before(Source, StringA, StringB, Desc) -> +- etap:is_greater( +- string:str(Source, StringB), +- string:str(Source, StringA), +- Desc +- ). +diff --git a/src/etap/etap_web.erl b/src/etap/etap_web.erl +deleted file mode 100644 +index fb7aee1..0000000 +--- a/src/etap/etap_web.erl ++++ /dev/null +@@ -1,65 +0,0 @@ +-%% Copyright (c) 2008-2009 Nick Gerakines +-%% +-%% 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. +-%% +-%% @author Nick Gerakines [http://socklabs.com/] +-%% @copyright 2008 Nick Gerakines +-%% @todo Support cookies. +-%% @doc Provide testing functionality for web requests. +--module(etap_web). +- +--export([simple_200/2, simple_404/2, build_request/4]). +- +-%% @doc Fetch a url and verify that it returned a 200 status. +-simple_200(Url, Desc) -> +- Request = build_request(get, Url, [], []), +- Request:status_is(200, Desc). +- +-%% @doc Fetch a url and verify that it returned a 404 status. +-simple_404(Url, Desc) -> +- Request = build_request(get, Url, [], []), +- Request:status_is(404, Desc). +- +-%% @doc Create and return a request structure. +-build_request(Method, Url, Headers, Body) +- when Method==options;Method==get;Method==head;Method==delete;Method==trace -> +- try http:request(Method, {Url, Headers}, [{autoredirect, false}], []) of +- {ok, {OutStatus, OutHeaders, OutBody}} -> +- etap_request:new(Method, Url, Headers, Body, OutStatus, OutHeaders, OutBody); +- _ -> error +- catch +- _:_ -> error +- end; +- +-%% @doc Create and return a request structure. +-build_request(Method, Url, Headers, Body) when Method == post; Method == put -> +- ContentType = case lists:keysearch("Content-Type", 1, Headers) of +- {value, {"Content-Type", X}} -> X; +- _ -> [] +- end, +- try http:request(Method, {Url, Headers, ContentType, Body}, [{autoredirect, false}], []) of +- {ok, {OutStatus, OutHeaders, OutBody}} -> +- etap_request:new(Method, Url, Headers, Body, OutStatus, OutHeaders, OutBody); +- _ -> error +- catch +- _:_ -> error +- end. +diff --git a/test/etap/test_util.erl.in b/test/etap/test_util.erl.in +index 79b0417..c57d7a8 100644 +--- a/test/etap/test_util.erl.in ++++ b/test/etap/test_util.erl.in +@@ -22,7 +22,7 @@ builddir() -> + "@abs_top_builddir@". + + init_code_path() -> +- Paths = ["etap", "couchdb", "ibrowse", "mochiweb"], ++ Paths = ["couchdb", "ibrowse", "mochiweb"], + lists:foreach(fun(Name) -> + code:add_pathz(filename:join([builddir(), "src", Name])) + end, Paths). +-- +1.7.1.1 + diff --git a/couchdb-1.0.0-0005-Remove-bundled-mochiweb-library.patch b/couchdb-1.0.0-0005-Remove-bundled-mochiweb-library.patch new file mode 100644 index 0000000..1967f8a --- /dev/null +++ b/couchdb-1.0.0-0005-Remove-bundled-mochiweb-library.patch @@ -0,0 +1,7750 @@ +From 8314b0e28845e12118e08a86b1a54581f3fcefea Mon Sep 17 00:00:00 2001 +From: Peter Lemenkov +Date: Wed, 14 Jul 2010 18:01:45 +0400 +Subject: [PATCH 5/9] Remove bundled mochiweb library + +--- + configure | 3 - + configure.ac | 1 - + src/Makefile.am | 2 +- + src/Makefile.in | 2 +- + src/mochiweb/Makefile.am | 80 --- + src/mochiweb/Makefile.in | 515 ------------------ + src/mochiweb/mochifmt.erl | 426 --------------- + src/mochiweb/mochifmt_records.erl | 30 - + src/mochiweb/mochifmt_std.erl | 23 - + src/mochiweb/mochihex.erl | 75 --- + src/mochiweb/mochijson.erl | 528 ------------------ + src/mochiweb/mochijson2.erl | 660 ----------------------- + src/mochiweb/mochinum.erl | 289 ---------- + src/mochiweb/mochiweb.app.in | 32 -- + src/mochiweb/mochiweb.erl | 110 ---- + src/mochiweb/mochiweb_app.erl | 20 - + src/mochiweb/mochiweb_charref.erl | 295 ---------- + src/mochiweb/mochiweb_cookies.erl | 257 --------- + src/mochiweb/mochiweb_echo.erl | 31 -- + src/mochiweb/mochiweb_headers.erl | 251 --------- + src/mochiweb/mochiweb_html.erl | 893 ------------------------------- + src/mochiweb/mochiweb_http.erl | 152 ------ + src/mochiweb/mochiweb_multipart.erl | 530 ------------------ + src/mochiweb/mochiweb_request.erl | 867 ------------------------------ + src/mochiweb/mochiweb_response.erl | 56 -- + src/mochiweb/mochiweb_skel.erl | 73 --- + src/mochiweb/mochiweb_socket_server.erl | 248 --------- + src/mochiweb/mochiweb_sup.erl | 34 -- + src/mochiweb/mochiweb_util.erl | 859 ----------------------------- + src/mochiweb/reloader.erl | 123 ----- + test/etap/test_util.erl.in | 2 +- + 31 files changed, 3 insertions(+), 7464 deletions(-) + delete mode 100644 src/mochiweb/Makefile.am + delete mode 100644 src/mochiweb/Makefile.in + delete mode 100644 src/mochiweb/mochifmt.erl + delete mode 100644 src/mochiweb/mochifmt_records.erl + delete mode 100644 src/mochiweb/mochifmt_std.erl + delete mode 100644 src/mochiweb/mochihex.erl + delete mode 100644 src/mochiweb/mochijson.erl + delete mode 100644 src/mochiweb/mochijson2.erl + delete mode 100644 src/mochiweb/mochinum.erl + delete mode 100644 src/mochiweb/mochiweb.app.in + delete mode 100644 src/mochiweb/mochiweb.erl + delete mode 100644 src/mochiweb/mochiweb_app.erl + delete mode 100644 src/mochiweb/mochiweb_charref.erl + delete mode 100644 src/mochiweb/mochiweb_cookies.erl + delete mode 100644 src/mochiweb/mochiweb_echo.erl + delete mode 100644 src/mochiweb/mochiweb_headers.erl + delete mode 100644 src/mochiweb/mochiweb_html.erl + delete mode 100644 src/mochiweb/mochiweb_http.erl + delete mode 100644 src/mochiweb/mochiweb_multipart.erl + delete mode 100644 src/mochiweb/mochiweb_request.erl + delete mode 100644 src/mochiweb/mochiweb_response.erl + delete mode 100644 src/mochiweb/mochiweb_skel.erl + delete mode 100644 src/mochiweb/mochiweb_socket_server.erl + delete mode 100644 src/mochiweb/mochiweb_sup.erl + delete mode 100644 src/mochiweb/mochiweb_util.erl + delete mode 100644 src/mochiweb/reloader.erl + +diff --git a/configure b/configure +index 26e0459..6a9c731 100755 +--- a/configure ++++ b/configure +@@ -12263,8 +12263,6 @@ ac_config_files="$ac_config_files src/couchdb/priv/Makefile" + + ac_config_files="$ac_config_files src/ibrowse/Makefile" + +-ac_config_files="$ac_config_files src/mochiweb/Makefile" +- + ac_config_files="$ac_config_files test/Makefile" + + ac_config_files="$ac_config_files test/bench/Makefile" +@@ -13287,7 +13285,6 @@ do + "src/couchdb/Makefile") CONFIG_FILES="$CONFIG_FILES src/couchdb/Makefile" ;; + "src/couchdb/priv/Makefile") CONFIG_FILES="$CONFIG_FILES src/couchdb/priv/Makefile" ;; + "src/ibrowse/Makefile") CONFIG_FILES="$CONFIG_FILES src/ibrowse/Makefile" ;; +- "src/mochiweb/Makefile") CONFIG_FILES="$CONFIG_FILES src/mochiweb/Makefile" ;; + "test/Makefile") CONFIG_FILES="$CONFIG_FILES test/Makefile" ;; + "test/bench/Makefile") CONFIG_FILES="$CONFIG_FILES test/bench/Makefile" ;; + "test/etap/Makefile") CONFIG_FILES="$CONFIG_FILES test/etap/Makefile" ;; +diff --git a/configure.ac b/configure.ac +index e9375be..b5a19ef 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -388,7 +388,6 @@ AC_CONFIG_FILES([src/couchdb/couch.app.tpl]) + AC_CONFIG_FILES([src/couchdb/Makefile]) + AC_CONFIG_FILES([src/couchdb/priv/Makefile]) + AC_CONFIG_FILES([src/ibrowse/Makefile]) +-AC_CONFIG_FILES([src/mochiweb/Makefile]) + AC_CONFIG_FILES([test/Makefile]) + AC_CONFIG_FILES([test/bench/Makefile]) + AC_CONFIG_FILES([test/etap/Makefile]) +diff --git a/src/Makefile.am b/src/Makefile.am +index 19a5d20..5a6646f 100644 +--- a/src/Makefile.am ++++ b/src/Makefile.am +@@ -10,4 +10,4 @@ + ## License for the specific language governing permissions and limitations under + ## the License. + +-SUBDIRS = couchdb ibrowse mochiweb ++SUBDIRS = couchdb ibrowse +diff --git a/src/Makefile.in b/src/Makefile.in +index ae1b828..2d11fc8 100644 +--- a/src/Makefile.in ++++ b/src/Makefile.in +@@ -248,7 +248,7 @@ version_minor = @version_minor@ + version_release = @version_release@ + version_revision = @version_revision@ + version_stage = @version_stage@ +-SUBDIRS = couchdb ibrowse mochiweb ++SUBDIRS = couchdb ibrowse + all: all-recursive + + .SUFFIXES: +diff --git a/src/mochiweb/Makefile.am b/src/mochiweb/Makefile.am +deleted file mode 100644 +index c191abf..0000000 +--- a/src/mochiweb/Makefile.am ++++ /dev/null +@@ -1,80 +0,0 @@ +-## 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. +- +-mochiwebebindir = $(localerlanglibdir)/mochiweb-r113/ebin +- +-mochiweb_file_collection = \ +- mochifmt.erl \ +- mochifmt_records.erl \ +- mochifmt_std.erl \ +- mochihex.erl \ +- mochijson.erl \ +- mochijson2.erl \ +- mochinum.erl \ +- mochiweb.app.in \ +- mochiweb.erl \ +- mochiweb_app.erl \ +- mochiweb_charref.erl \ +- mochiweb_cookies.erl \ +- mochiweb_echo.erl \ +- mochiweb_headers.erl \ +- mochiweb_html.erl \ +- mochiweb_http.erl \ +- mochiweb_multipart.erl \ +- mochiweb_request.erl \ +- mochiweb_response.erl \ +- mochiweb_skel.erl \ +- mochiweb_socket_server.erl \ +- mochiweb_sup.erl \ +- mochiweb_util.erl \ +- reloader.erl +- +-mochiwebebin_make_generated_file_list = \ +- mochifmt.beam \ +- mochifmt_records.beam \ +- mochifmt_std.beam \ +- mochihex.beam \ +- mochijson.beam \ +- mochijson2.beam \ +- mochinum.beam \ +- mochiweb.app \ +- mochiweb.beam \ +- mochiweb_app.beam \ +- mochiweb_charref.beam \ +- mochiweb_cookies.beam \ +- mochiweb_echo.beam \ +- mochiweb_headers.beam \ +- mochiweb_html.beam \ +- mochiweb_http.beam \ +- mochiweb_multipart.beam \ +- mochiweb_request.beam \ +- mochiweb_response.beam \ +- mochiweb_skel.beam \ +- mochiweb_socket_server.beam \ +- mochiweb_sup.beam \ +- mochiweb_util.beam \ +- reloader.beam +- +-mochiwebebin_DATA = \ +- $(mochiwebebin_make_generated_file_list) +- +-EXTRA_DIST = \ +- $(mochiweb_file_collection) +- +-CLEANFILES = \ +- $(mochiwebebin_make_generated_file_list) +- +-%.app: %.app.in +- cp $< $@ +- +-%.beam: %.erl +- $(ERLC) $(ERLC_FLAGS) $< +diff --git a/src/mochiweb/Makefile.in b/src/mochiweb/Makefile.in +deleted file mode 100644 +index 92f0acc..0000000 +--- a/src/mochiweb/Makefile.in ++++ /dev/null +@@ -1,515 +0,0 @@ +-# Makefile.in generated by automake 1.11 from Makefile.am. +-# @configure_input@ +- +-# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, +-# 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation, +-# Inc. +-# This Makefile.in is free software; the Free Software Foundation +-# gives unlimited permission to copy and/or distribute it, +-# with or without modifications, as long as this notice is preserved. +- +-# This program is distributed in the hope that it will be useful, +-# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +-# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +-# PARTICULAR PURPOSE. +- +-@SET_MAKE@ +- +-VPATH = @srcdir@ +-pkgdatadir = $(datadir)/@PACKAGE@ +-pkgincludedir = $(includedir)/@PACKAGE@ +-pkglibdir = $(libdir)/@PACKAGE@ +-pkglibexecdir = $(libexecdir)/@PACKAGE@ +-am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +-install_sh_DATA = $(install_sh) -c -m 644 +-install_sh_PROGRAM = $(install_sh) -c +-install_sh_SCRIPT = $(install_sh) -c +-INSTALL_HEADER = $(INSTALL_DATA) +-transform = $(program_transform_name) +-NORMAL_INSTALL = : +-PRE_INSTALL = : +-POST_INSTALL = : +-NORMAL_UNINSTALL = : +-PRE_UNINSTALL = : +-POST_UNINSTALL = : +-build_triplet = @build@ +-host_triplet = @host@ +-subdir = src/mochiweb +-DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in +-ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +-am__aclocal_m4_deps = $(top_srcdir)/m4/ac_check_curl.m4 \ +- $(top_srcdir)/m4/ac_check_icu.m4 $(top_srcdir)/m4/libtool.m4 \ +- $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \ +- $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \ +- $(top_srcdir)/acinclude.m4 $(top_srcdir)/configure.ac +-am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ +- $(ACLOCAL_M4) +-mkinstalldirs = $(install_sh) -d +-CONFIG_HEADER = $(top_builddir)/config.h +-CONFIG_CLEAN_FILES = +-CONFIG_CLEAN_VPATH_FILES = +-SOURCES = +-DIST_SOURCES = +-am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; +-am__vpath_adj = case $$p in \ +- $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ +- *) f=$$p;; \ +- esac; +-am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; +-am__install_max = 40 +-am__nobase_strip_setup = \ +- srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` +-am__nobase_strip = \ +- for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" +-am__nobase_list = $(am__nobase_strip_setup); \ +- for p in $$list; do echo "$$p $$p"; done | \ +- sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ +- $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ +- if (++n[$$2] == $(am__install_max)) \ +- { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ +- END { for (dir in files) print dir, files[dir] }' +-am__base_list = \ +- sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ +- sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' +-am__installdirs = "$(DESTDIR)$(mochiwebebindir)" +-DATA = $(mochiwebebin_DATA) +-DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +-ACLOCAL = @ACLOCAL@ +-AMTAR = @AMTAR@ +-AR = @AR@ +-AUTOCONF = @AUTOCONF@ +-AUTOHEADER = @AUTOHEADER@ +-AUTOMAKE = @AUTOMAKE@ +-AWK = @AWK@ +-CC = @CC@ +-CCDEPMODE = @CCDEPMODE@ +-CFLAGS = @CFLAGS@ +-CPP = @CPP@ +-CPPFLAGS = @CPPFLAGS@ +-CURL_CFLAGS = @CURL_CFLAGS@ +-CURL_CONFIG = @CURL_CONFIG@ +-CURL_LDFLAGS = @CURL_LDFLAGS@ +-CURL_LIBS = @CURL_LIBS@ +-CYGPATH_W = @CYGPATH_W@ +-DEFS = @DEFS@ +-DEPDIR = @DEPDIR@ +-DSYMUTIL = @DSYMUTIL@ +-DUMPBIN = @DUMPBIN@ +-ECHO_C = @ECHO_C@ +-ECHO_N = @ECHO_N@ +-ECHO_T = @ECHO_T@ +-EGREP = @EGREP@ +-ERL = @ERL@ +-ERLC = @ERLC@ +-ERLC_FLAGS = @ERLC_FLAGS@ +-EXEEXT = @EXEEXT@ +-FGREP = @FGREP@ +-FLAGS = @FLAGS@ +-GREP = @GREP@ +-HELP2MAN_EXECUTABLE = @HELP2MAN_EXECUTABLE@ +-ICU_CFLAGS = @ICU_CFLAGS@ +-ICU_CONFIG = @ICU_CONFIG@ +-ICU_CXXFLAGS = @ICU_CXXFLAGS@ +-ICU_LIBS = @ICU_LIBS@ +-ICU_LOCAL_BIN = @ICU_LOCAL_BIN@ +-ICU_LOCAL_CFLAGS = @ICU_LOCAL_CFLAGS@ +-ICU_LOCAL_LDFLAGS = @ICU_LOCAL_LDFLAGS@ +-INNO_COMPILER_EXECUTABLE = @INNO_COMPILER_EXECUTABLE@ +-INSTALL = @INSTALL@ +-INSTALL_DATA = @INSTALL_DATA@ +-INSTALL_PROGRAM = @INSTALL_PROGRAM@ +-INSTALL_SCRIPT = @INSTALL_SCRIPT@ +-INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +-JSLIB = @JSLIB@ +-JS_LIB_BASE = @JS_LIB_BASE@ +-JS_LIB_BINARY = @JS_LIB_BINARY@ +-JS_LIB_DIR = @JS_LIB_DIR@ +-LD = @LD@ +-LDFLAGS = @LDFLAGS@ +-LIBOBJS = @LIBOBJS@ +-LIBS = @LIBS@ +-LIBTOOL = @LIBTOOL@ +-LIPO = @LIPO@ +-LN_S = @LN_S@ +-LTLIBOBJS = @LTLIBOBJS@ +-MAKEINFO = @MAKEINFO@ +-MKDIR_P = @MKDIR_P@ +-NM = @NM@ +-NMEDIT = @NMEDIT@ +-OBJDUMP = @OBJDUMP@ +-OBJEXT = @OBJEXT@ +-OTOOL = @OTOOL@ +-OTOOL64 = @OTOOL64@ +-PACKAGE = @PACKAGE@ +-PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +-PACKAGE_NAME = @PACKAGE_NAME@ +-PACKAGE_STRING = @PACKAGE_STRING@ +-PACKAGE_TARNAME = @PACKAGE_TARNAME@ +-PACKAGE_URL = @PACKAGE_URL@ +-PACKAGE_VERSION = @PACKAGE_VERSION@ +-PATH_SEPARATOR = @PATH_SEPARATOR@ +-RANLIB = @RANLIB@ +-SED = @SED@ +-SET_MAKE = @SET_MAKE@ +-SHELL = @SHELL@ +-STRIP = @STRIP@ +-VERSION = @VERSION@ +-abs_builddir = @abs_builddir@ +-abs_srcdir = @abs_srcdir@ +-abs_top_builddir = @abs_top_builddir@ +-abs_top_srcdir = @abs_top_srcdir@ +-ac_ct_CC = @ac_ct_CC@ +-ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +-am__include = @am__include@ +-am__leading_dot = @am__leading_dot@ +-am__quote = @am__quote@ +-am__tar = @am__tar@ +-am__untar = @am__untar@ +-bindir = @bindir@ +-bug_uri = @bug_uri@ +-build = @build@ +-build_alias = @build_alias@ +-build_cpu = @build_cpu@ +-build_os = @build_os@ +-build_vendor = @build_vendor@ +-builddir = @builddir@ +-datadir = @datadir@ +-datarootdir = @datarootdir@ +-docdir = @docdir@ +-dvidir = @dvidir@ +-exec_prefix = @exec_prefix@ +-host = @host@ +-host_alias = @host_alias@ +-host_cpu = @host_cpu@ +-host_os = @host_os@ +-host_vendor = @host_vendor@ +-htmldir = @htmldir@ +-includedir = @includedir@ +-infodir = @infodir@ +-initdir = @initdir@ +-install_sh = @install_sh@ +-launchddir = @launchddir@ +-libdir = @libdir@ +-libexecdir = @libexecdir@ +-localconfdir = @localconfdir@ +-localdatadir = @localdatadir@ +-localdocdir = @localdocdir@ +-localedir = @localedir@ +-localerlanglibdir = @localerlanglibdir@ +-locallibbindir = @locallibbindir@ +-locallibdir = @locallibdir@ +-localstatedir = @localstatedir@ +-localstatelibdir = @localstatelibdir@ +-localstatelogdir = @localstatelogdir@ +-localstaterundir = @localstaterundir@ +-lt_ECHO = @lt_ECHO@ +-mandir = @mandir@ +-mkdir_p = @mkdir_p@ +-msvc_redist_dir = @msvc_redist_dir@ +-msvc_redist_name = @msvc_redist_name@ +-oldincludedir = @oldincludedir@ +-openssl_bin_dir = @openssl_bin_dir@ +-package_author_address = @package_author_address@ +-package_author_name = @package_author_name@ +-package_identifier = @package_identifier@ +-package_name = @package_name@ +-package_tarname = @package_tarname@ +-pdfdir = @pdfdir@ +-prefix = @prefix@ +-program_transform_name = @program_transform_name@ +-psdir = @psdir@ +-sbindir = @sbindir@ +-sharedstatedir = @sharedstatedir@ +-srcdir = @srcdir@ +-sysconfdir = @sysconfdir@ +-target_alias = @target_alias@ +-top_build_prefix = @top_build_prefix@ +-top_builddir = @top_builddir@ +-top_srcdir = @top_srcdir@ +-version = @version@ +-version_major = @version_major@ +-version_minor = @version_minor@ +-version_release = @version_release@ +-version_revision = @version_revision@ +-version_stage = @version_stage@ +-mochiwebebindir = $(localerlanglibdir)/mochiweb-r113/ebin +-mochiweb_file_collection = \ +- mochifmt.erl \ +- mochifmt_records.erl \ +- mochifmt_std.erl \ +- mochihex.erl \ +- mochijson.erl \ +- mochijson2.erl \ +- mochinum.erl \ +- mochiweb.app.in \ +- mochiweb.erl \ +- mochiweb_app.erl \ +- mochiweb_charref.erl \ +- mochiweb_cookies.erl \ +- mochiweb_echo.erl \ +- mochiweb_headers.erl \ +- mochiweb_html.erl \ +- mochiweb_http.erl \ +- mochiweb_multipart.erl \ +- mochiweb_request.erl \ +- mochiweb_response.erl \ +- mochiweb_skel.erl \ +- mochiweb_socket_server.erl \ +- mochiweb_sup.erl \ +- mochiweb_util.erl \ +- reloader.erl +- +-mochiwebebin_make_generated_file_list = \ +- mochifmt.beam \ +- mochifmt_records.beam \ +- mochifmt_std.beam \ +- mochihex.beam \ +- mochijson.beam \ +- mochijson2.beam \ +- mochinum.beam \ +- mochiweb.app \ +- mochiweb.beam \ +- mochiweb_app.beam \ +- mochiweb_charref.beam \ +- mochiweb_cookies.beam \ +- mochiweb_echo.beam \ +- mochiweb_headers.beam \ +- mochiweb_html.beam \ +- mochiweb_http.beam \ +- mochiweb_multipart.beam \ +- mochiweb_request.beam \ +- mochiweb_response.beam \ +- mochiweb_skel.beam \ +- mochiweb_socket_server.beam \ +- mochiweb_sup.beam \ +- mochiweb_util.beam \ +- reloader.beam +- +-mochiwebebin_DATA = \ +- $(mochiwebebin_make_generated_file_list) +- +-EXTRA_DIST = \ +- $(mochiweb_file_collection) +- +-CLEANFILES = \ +- $(mochiwebebin_make_generated_file_list) +- +-all: all-am +- +-.SUFFIXES: +-$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) +- @for dep in $?; do \ +- case '$(am__configure_deps)' in \ +- *$$dep*) \ +- ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ +- && { if test -f $@; then exit 0; else break; fi; }; \ +- exit 1;; \ +- esac; \ +- done; \ +- echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/mochiweb/Makefile'; \ +- $(am__cd) $(top_srcdir) && \ +- $(AUTOMAKE) --foreign src/mochiweb/Makefile +-.PRECIOUS: Makefile +-Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status +- @case '$?' in \ +- *config.status*) \ +- cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ +- *) \ +- echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ +- cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ +- esac; +- +-$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) +- cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +- +-$(top_srcdir)/configure: $(am__configure_deps) +- cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +-$(ACLOCAL_M4): $(am__aclocal_m4_deps) +- cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +-$(am__aclocal_m4_deps): +- +-mostlyclean-libtool: +- -rm -f *.lo +- +-clean-libtool: +- -rm -rf .libs _libs +-install-mochiwebebinDATA: $(mochiwebebin_DATA) +- @$(NORMAL_INSTALL) +- test -z "$(mochiwebebindir)" || $(MKDIR_P) "$(DESTDIR)$(mochiwebebindir)" +- @list='$(mochiwebebin_DATA)'; test -n "$(mochiwebebindir)" || list=; \ +- for p in $$list; do \ +- if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ +- echo "$$d$$p"; \ +- done | $(am__base_list) | \ +- while read files; do \ +- echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(mochiwebebindir)'"; \ +- $(INSTALL_DATA) $$files "$(DESTDIR)$(mochiwebebindir)" || exit $$?; \ +- done +- +-uninstall-mochiwebebinDATA: +- @$(NORMAL_UNINSTALL) +- @list='$(mochiwebebin_DATA)'; test -n "$(mochiwebebindir)" || list=; \ +- files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ +- test -n "$$files" || exit 0; \ +- echo " ( cd '$(DESTDIR)$(mochiwebebindir)' && rm -f" $$files ")"; \ +- cd "$(DESTDIR)$(mochiwebebindir)" && rm -f $$files +-tags: TAGS +-TAGS: +- +-ctags: CTAGS +-CTAGS: +- +- +-distdir: $(DISTFILES) +- @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ +- topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ +- list='$(DISTFILES)'; \ +- dist_files=`for file in $$list; do echo $$file; done | \ +- sed -e "s|^$$srcdirstrip/||;t" \ +- -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ +- case $$dist_files in \ +- */*) $(MKDIR_P) `echo "$$dist_files" | \ +- sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ +- sort -u` ;; \ +- esac; \ +- for file in $$dist_files; do \ +- if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ +- if test -d $$d/$$file; then \ +- dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ +- if test -d "$(distdir)/$$file"; then \ +- find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ +- fi; \ +- if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ +- cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ +- find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ +- fi; \ +- cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ +- else \ +- test -f "$(distdir)/$$file" \ +- || cp -p $$d/$$file "$(distdir)/$$file" \ +- || exit 1; \ +- fi; \ +- done +-check-am: all-am +-check: check-am +-all-am: Makefile $(DATA) +-installdirs: +- for dir in "$(DESTDIR)$(mochiwebebindir)"; do \ +- test -z "$$dir" || $(MKDIR_P) "$$dir"; \ +- done +-install: install-am +-install-exec: install-exec-am +-install-data: install-data-am +-uninstall: uninstall-am +- +-install-am: all-am +- @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am +- +-installcheck: installcheck-am +-install-strip: +- $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ +- install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ +- `test -z '$(STRIP)' || \ +- echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +-mostlyclean-generic: +- +-clean-generic: +- -test -z "$(CLEANFILES)" || rm -f $(CLEANFILES) +- +-distclean-generic: +- -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) +- -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) +- +-maintainer-clean-generic: +- @echo "This command is intended for maintainers to use" +- @echo "it deletes files that may require special tools to rebuild." +-clean: clean-am +- +-clean-am: clean-generic clean-libtool mostlyclean-am +- +-distclean: distclean-am +- -rm -f Makefile +-distclean-am: clean-am distclean-generic +- +-dvi: dvi-am +- +-dvi-am: +- +-html: html-am +- +-html-am: +- +-info: info-am +- +-info-am: +- +-install-data-am: install-mochiwebebinDATA +- +-install-dvi: install-dvi-am +- +-install-dvi-am: +- +-install-exec-am: +- +-install-html: install-html-am +- +-install-html-am: +- +-install-info: install-info-am +- +-install-info-am: +- +-install-man: +- +-install-pdf: install-pdf-am +- +-install-pdf-am: +- +-install-ps: install-ps-am +- +-install-ps-am: +- +-installcheck-am: +- +-maintainer-clean: maintainer-clean-am +- -rm -f Makefile +-maintainer-clean-am: distclean-am maintainer-clean-generic +- +-mostlyclean: mostlyclean-am +- +-mostlyclean-am: mostlyclean-generic mostlyclean-libtool +- +-pdf: pdf-am +- +-pdf-am: +- +-ps: ps-am +- +-ps-am: +- +-uninstall-am: uninstall-mochiwebebinDATA +- +-.MAKE: install-am install-strip +- +-.PHONY: all all-am check check-am clean clean-generic clean-libtool \ +- distclean distclean-generic distclean-libtool distdir dvi \ +- dvi-am html html-am info info-am install install-am \ +- install-data install-data-am install-dvi install-dvi-am \ +- install-exec install-exec-am install-html install-html-am \ +- install-info install-info-am install-man \ +- install-mochiwebebinDATA install-pdf install-pdf-am install-ps \ +- install-ps-am install-strip installcheck installcheck-am \ +- installdirs maintainer-clean maintainer-clean-generic \ +- mostlyclean mostlyclean-generic mostlyclean-libtool pdf pdf-am \ +- ps ps-am uninstall uninstall-am uninstall-mochiwebebinDATA +- +- +-%.app: %.app.in +- cp $< $@ +- +-%.beam: %.erl +- $(ERLC) $(ERLC_FLAGS) $< +- +-# Tell versions [3.59,3.63) of GNU make to not export all variables. +-# Otherwise a system limit (for SysV at least) may be exceeded. +-.NOEXPORT: +diff --git a/src/mochiweb/mochifmt.erl b/src/mochiweb/mochifmt.erl +deleted file mode 100644 +index da0a133..0000000 +--- a/src/mochiweb/mochifmt.erl ++++ /dev/null +@@ -1,426 +0,0 @@ +-%% @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/src/mochiweb/mochifmt_records.erl b/src/mochiweb/mochifmt_records.erl +deleted file mode 100644 +index 94c7797..0000000 +--- a/src/mochiweb/mochifmt_records.erl ++++ /dev/null +@@ -1,30 +0,0 @@ +-%% @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/src/mochiweb/mochifmt_std.erl b/src/mochiweb/mochifmt_std.erl +deleted file mode 100644 +index 9442016..0000000 +--- a/src/mochiweb/mochifmt_std.erl ++++ /dev/null +@@ -1,23 +0,0 @@ +-%% @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/src/mochiweb/mochihex.erl b/src/mochiweb/mochihex.erl +deleted file mode 100644 +index 7fe6899..0000000 +--- a/src/mochiweb/mochihex.erl ++++ /dev/null +@@ -1,75 +0,0 @@ +-%% @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/src/mochiweb/mochijson.erl b/src/mochiweb/mochijson.erl +deleted file mode 100644 +index 74695a7..0000000 +--- a/src/mochiweb/mochijson.erl ++++ /dev/null +@@ -1,528 +0,0 @@ +-%% @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]) -> +- equiv(V1, V2) andalso equiv_list(L1, L2). +- +-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/src/mochiweb/mochijson2.erl b/src/mochiweb/mochijson2.erl +deleted file mode 100644 +index 111c37b..0000000 +--- a/src/mochiweb/mochijson2.erl ++++ /dev/null +@@ -1,660 +0,0 @@ +-%% @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) -> +- integer_to_list(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, $\\, _/binary>> -> +- {escape, O}; +- <<_:O/binary, C1, _/binary>> when C1 < 128 -> +- tokenize_string_fast(B, 1 + O); +- <<_:O/binary, C1, C2, _/binary>> when C1 >= 194, C1 =< 223, +- C2 >= 128, C2 =< 191 -> +- tokenize_string_fast(B, 2 + O); +- <<_:O/binary, C1, C2, C3, _/binary>> when C1 >= 224, C1 =< 239, +- C2 >= 128, C2 =< 191, +- C3 >= 128, C3 =< 191 -> +- tokenize_string_fast(B, 3 + O); +- <<_:O/binary, C1, C2, C3, C4, _/binary>> when C1 >= 240, C1 =< 244, +- C2 >= 128, C2 =< 191, +- C3 >= 128, C3 =< 191, +- C4 >= 128, C4 =< 191 -> +- tokenize_string_fast(B, 4 + O); +- _ -> +- throw(invalid_utf8) +- 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, Rest/binary>> -> +- C = erlang:list_to_integer([C3, C2, C1, C0], 16), +- if C > 16#D7FF, C < 16#DC00 -> +- %% coalesce UTF-16 surrogate pair +- <<"\\u", D3, D2, D1, D0, _/binary>> = Rest, +- D = erlang:list_to_integer([D3,D2,D1,D0], 16), +- [CodePoint] = xmerl_ucs:from_utf16be(<>), +- Acc1 = lists:reverse(xmerl_ucs:to_utf8(CodePoint), Acc), +- tokenize_string(B, ?ADV_COL(S, 12), Acc1); +- true -> +- Acc1 = lists:reverse(xmerl_ucs:to_utf8(C), Acc), +- tokenize_string(B, ?ADV_COL(S, 6), Acc1) +- end; +- <<_: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]) -> +- equiv(V1, V2) andalso equiv_list(L1, L2). +- +-test_all() -> +- [1199344435545.0, 1] = decode(<<"[1199344435545.0,1]">>), +- <<16#F0,16#9D,16#9C,16#95>> = decode([34,"\\ud835","\\udf15",34]), +- test_encoder_utf8(), +- test_input_validation(), +- 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">>). +- +-test_input_validation() -> +- Good = [ +- {16#00A3, <>}, % pound +- {16#20AC, <>}, % euro +- {16#10196, <>} % denarius +- ], +- lists:foreach(fun({CodePoint, UTF8}) -> +- Expect = list_to_binary(xmerl_ucs:to_utf8(CodePoint)), +- Expect = decode(UTF8) +- end, Good), +- +- Bad = [ +- % 2nd, 3rd, or 4th byte of a multi-byte sequence w/o leading byte +- <>, +- % missing continuations, last byte in each should be 80-BF +- <>, +- <>, +- <>, +- % we don't support code points > 10FFFF per RFC 3629 +- <> +- ], +- lists:foreach(fun(X) -> +- ok = try decode(X) catch invalid_utf8 -> ok end +- end, Bad). +diff --git a/src/mochiweb/mochinum.erl b/src/mochiweb/mochinum.erl +deleted file mode 100644 +index 6a86604..0000000 +--- a/src/mochiweb/mochinum.erl ++++ /dev/null +@@ -1,289 +0,0 @@ +-%% @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/src/mochiweb/mochiweb.app.in b/src/mochiweb/mochiweb.app.in +deleted file mode 100644 +index b0f9014..0000000 +--- a/src/mochiweb/mochiweb.app.in ++++ /dev/null +@@ -1,32 +0,0 @@ +-{application, mochiweb, +- [{description, "MochiMedia Web Server"}, +- {vsn, "113"}, +- {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/src/mochiweb/mochiweb.erl b/src/mochiweb/mochiweb.erl +deleted file mode 100644 +index 0f4d52a..0000000 +--- a/src/mochiweb/mochiweb.erl ++++ /dev/null +@@ -1,110 +0,0 @@ +-%% @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/src/mochiweb/mochiweb_app.erl b/src/mochiweb/mochiweb_app.erl +deleted file mode 100644 +index 2b437f6..0000000 +--- a/src/mochiweb/mochiweb_app.erl ++++ /dev/null +@@ -1,20 +0,0 @@ +-%% @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/src/mochiweb/mochiweb_charref.erl b/src/mochiweb/mochiweb_charref.erl +deleted file mode 100644 +index d037d2f..0000000 +--- a/src/mochiweb/mochiweb_charref.erl ++++ /dev/null +@@ -1,295 +0,0 @@ +-%% @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/src/mochiweb/mochiweb_cookies.erl b/src/mochiweb/mochiweb_cookies.erl +deleted file mode 100644 +index 61711ff..0000000 +--- a/src/mochiweb/mochiweb_cookies.erl ++++ /dev/null +@@ -1,257 +0,0 @@ +-%% @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/src/mochiweb/mochiweb_echo.erl b/src/mochiweb/mochiweb_echo.erl +deleted file mode 100644 +index f32d680..0000000 +--- a/src/mochiweb/mochiweb_echo.erl ++++ /dev/null +@@ -1,31 +0,0 @@ +-%% @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/src/mochiweb/mochiweb_headers.erl b/src/mochiweb/mochiweb_headers.erl +deleted file mode 100644 +index d90fd67..0000000 +--- a/src/mochiweb/mochiweb_headers.erl ++++ /dev/null +@@ -1,251 +0,0 @@ +-%% @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([from_binary/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), +- HB = <<"Content-Length: 47\r\nContent-Type: text/plain\r\n\r\n">>, +- H_HB = ?MODULE:from_binary(HB), +- H_HB = ?MODULE:from_binary(binary_to_list(HB)), +- "47" = ?MODULE:get_value("Content-Length", H_HB), +- "text/plain" = ?MODULE:get_value("Content-Type", H_HB), +- L_H_HB = ?MODULE:to_list(H_HB), +- 2 = length(L_H_HB), +- true = lists:member({'Content-Length', "47"}, L_H_HB), +- true = lists:member({'Content-Type', "text/plain"}, L_H_HB), +- HL = [ <<"Content-Length: 47\r\n">>, <<"Content-Type: text/plain\r\n">> ], +- HL2 = [ "Content-Length: 47\r\n", <<"Content-Type: text/plain\r\n">> ], +- HL3 = [ <<"Content-Length: 47\r\n">>, "Content-Type: text/plain\r\n" ], +- H_HL = ?MODULE:from_binary(HL), +- H_HL = ?MODULE:from_binary(HL2), +- H_HL = ?MODULE:from_binary(HL3), +- "47" = ?MODULE:get_value("Content-Length", H_HL), +- "text/plain" = ?MODULE:get_value("Content-Type", H_HL), +- L_H_HL = ?MODULE:to_list(H_HL), +- 2 = length(L_H_HL), +- true = lists:member({'Content-Length', "47"}, L_H_HL), +- true = lists:member({'Content-Type', "text/plain"}, L_H_HL), +- [] = ?MODULE:to_list(?MODULE:from_binary(<<>>)), +- [] = ?MODULE:to_list(?MODULE:from_binary(<<"">>)), +- [] = ?MODULE:to_list(?MODULE:from_binary(<<"\r\n">>)), +- [] = ?MODULE:to_list(?MODULE:from_binary(<<"\r\n\r\n">>)), +- [] = ?MODULE:to_list(?MODULE:from_binary("")), +- [] = ?MODULE:to_list(?MODULE:from_binary([<<>>])), +- [] = ?MODULE:to_list(?MODULE:from_binary([<<"">>])), +- [] = ?MODULE:to_list(?MODULE:from_binary([<<"\r\n">>])), +- [] = ?MODULE:to_list(?MODULE:from_binary([<<"\r\n\r\n">>])), +- 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_binary(RawHttpHeader()) -> headers() +-%% @type RawHttpHeader() -> string() | binary() | [ string() | binary() ] +-%% +-%% @doc Transforms a raw HTTP header into a mochiweb headers structure. +-%% +-%% The given raw HTTP header can be one of the following: +-%% +-%% 1) A string or a binary representing a full HTTP header ending with +-%% double CRLF. +-%% Examples: +-%% "Content-Length: 47\r\nContent-Type: text/plain\r\n\r\n" +-%% <<"Content-Length: 47\r\nContent-Type: text/plain\r\n\r\n">> +-%% +-%% 2) A list of binaries or strings where each element represents a raw +-%% HTTP header line ending with a single CRLF. +-%% Examples: +-%% [ <<"Content-Length: 47\r\n">>, <<"Content-Type: text/plain\r\n">> ] +-%% [ "Content-Length: 47\r\n", "Content-Type: text/plain\r\n" ] +-%% [ "Content-Length: 47\r\n", <<"Content-Type: text/plain\r\n">> ] +-%% +-from_binary(RawHttpHeader) when is_binary(RawHttpHeader) -> +- from_binary(RawHttpHeader, []); +- +-from_binary(RawHttpHeaderList) -> +- from_binary(list_to_binary([RawHttpHeaderList, "\r\n"])). +- +-from_binary(RawHttpHeader, Acc) -> +- case erlang:decode_packet(httph, RawHttpHeader, []) of +- { ok, {http_header, _, H, _, V}, Rest } -> +- from_binary(Rest, [{H, V} | Acc]); +- _ -> +- make(Acc) +- end. +- +-%% @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/src/mochiweb/mochiweb_html.erl b/src/mochiweb/mochiweb_html.erl +deleted file mode 100644 +index 77100d5..0000000 +--- a/src/mochiweb/mochiweb_html.erl ++++ /dev/null +@@ -1,893 +0,0 @@ +-%% @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/src/mochiweb/mochiweb_http.erl b/src/mochiweb/mochiweb_http.erl +deleted file mode 100644 +index f1821f4..0000000 +--- a/src/mochiweb/mochiweb_http.erl ++++ /dev/null +@@ -1,152 +0,0 @@ +-%% @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]). +--export([after_response/2, reentry/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. +- +-reentry(Body) -> +- fun (Req) -> +- ?MODULE:after_response(Body, Req) +- 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), +- ?MODULE:after_response(Body, Req); +- {ok, {http_header, _, Name, _, Value}} -> +- headers(Socket, Request, [{Name, Value} | Headers], Body, +- 1 + HeaderCount); +- _Other -> +- gen_tcp:close(Socket), +- exit(normal) +- end. +- +-after_response(Body, Req) -> +- Socket = Req:get(socket), +- case Req:should_close() of +- true -> +- gen_tcp:close(Socket), +- exit(normal); +- false -> +- Req:cleanup(), +- ?MODULE:loop(Socket, Body) +- end. +diff --git a/src/mochiweb/mochiweb_multipart.erl b/src/mochiweb/mochiweb_multipart.erl +deleted file mode 100644 +index 0368a9a..0000000 +--- a/src/mochiweb/mochiweb_multipart.erl ++++ /dev/null +@@ -1,530 +0,0 @@ +-%% @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 = byte_size(Boundary), +- Chunk = read_chunk(Req, Length), +- Length1 = Length - byte_size(Chunk), +- <<"--", Boundary:BS/binary, "\r\n", Rest/binary>> = Chunk, +- feed_mp(headers, flash_multipart_hack(#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 = <>, +- flash_multipart_hack(State#mp{length=Length - byte_size(Data), +- buffer=Buffer1}). +- +-flash_multipart_hack(State=#mp{length=0, buffer=Buffer, boundary=Prefix}) -> +- %% http://code.google.com/p/mochiweb/issues/detail?id=22 +- %% Flash doesn't terminate multipart with \r\n properly so we fix it up here +- PrefixSize = size(Prefix), +- case size(Buffer) - (2 + PrefixSize) of +- Seek when Seek >= 0 -> +- case Buffer of +- <<_:Seek/binary, Prefix:PrefixSize/binary, "--">> -> +- Buffer1 = <>, +- State#mp{buffer=Buffer1}; +- _ -> +- State +- end; +- _ -> +- State +- end; +-flash_multipart_hack(State) -> +- State. +- +-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}) -> +- Boundary = find_boundary(Prefix, Buffer), +- case Boundary 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_callback({body, Got}, [{body, Expect} | Rest]) -> +- GotSize = size(Got), +- <> = Expect, +- fun (Next) -> test_callback(Next, [{body, Expect1} | Rest]) 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) -> +- ok = gen_tcp:send(Socket, BinContent), +- exit(normal) +- end, +- ClientFun = fun (Socket) -> +- Req = fake_request(Socket, ContentType, +- byte_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) -> +- ok = gen_tcp:send(Socket, BinContent), +- exit(normal) +- end, +- ClientFun = fun (Socket) -> +- Req = fake_request(Socket, ContentType, +- byte_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) -> +- ok = gen_tcp:send(Socket, BinContent), +- exit(normal) +- end, +- ClientFun = fun (Socket) -> +- Req = fake_request(Socket, ContentType, +- byte_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) -> +- ok = gen_tcp:send(Socket, BinContent), +- exit(normal) +- end, +- ClientFun = fun (Socket) -> +- Req = fake_request(Socket, ContentType, +- byte_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_flash_parse() -> +- ContentType = "multipart/form-data; boundary=----------ei4GI3GI3Ij5Ef1ae0KM7Ij5ei4Ij5", +- "----------ei4GI3GI3Ij5Ef1ae0KM7Ij5ei4Ij5" = get_boundary(ContentType), +- BinContent = <<"------------ei4GI3GI3Ij5Ef1ae0KM7Ij5ei4Ij5\r\nContent-Disposition: form-data; name=\"Filename\"\r\n\r\nhello.txt\r\n------------ei4GI3GI3Ij5Ef1ae0KM7Ij5ei4Ij5\r\nContent-Disposition: form-data; name=\"success_action_status\"\r\n\r\n201\r\n------------ei4GI3GI3Ij5Ef1ae0KM7Ij5ei4Ij5\r\nContent-Disposition: form-data; name=\"file\"; filename=\"hello.txt\"\r\nContent-Type: application/octet-stream\r\n\r\nhello\n\r\n------------ei4GI3GI3Ij5Ef1ae0KM7Ij5ei4Ij5\r\nContent-Disposition: form-data; name=\"Upload\"\r\n\r\nSubmit Query\r\n------------ei4GI3GI3Ij5Ef1ae0KM7Ij5ei4Ij5--">>, +- Expect = [{headers, +- [{"content-disposition", +- {"form-data", [{"name", "Filename"}]}}]}, +- {body, <<"hello.txt">>}, +- body_end, +- {headers, +- [{"content-disposition", +- {"form-data", [{"name", "success_action_status"}]}}]}, +- {body, <<"201">>}, +- body_end, +- {headers, +- [{"content-disposition", +- {"form-data", [{"name", "file"}, {"filename", "hello.txt"}]}}, +- {"content-type", {"application/octet-stream", []}}]}, +- {body, <<"hello\n">>}, +- body_end, +- {headers, +- [{"content-disposition", +- {"form-data", [{"name", "Upload"}]}}]}, +- {body, <<"Submit Query">>}, +- body_end, +- eof], +- TestCallback = fun (Next) -> test_callback(Next, Expect) end, +- ServerFun = fun (Socket) -> +- ok = gen_tcp:send(Socket, BinContent), +- exit(normal) +- end, +- ClientFun = fun (Socket) -> +- Req = fake_request(Socket, ContentType, +- byte_size(BinContent)), +- Res = parse_multipart_request(Req, TestCallback), +- {0, <<>>, ok} = Res, +- ok +- end, +- ok = with_socket_server(ServerFun, ClientFun), +- ok. +- +-test_flash_parse2() -> +- ContentType = "multipart/form-data; boundary=----------ei4GI3GI3Ij5Ef1ae0KM7Ij5ei4Ij5", +- "----------ei4GI3GI3Ij5Ef1ae0KM7Ij5ei4Ij5" = get_boundary(ContentType), +- Chunk = iolist_to_binary(string:copies("%", 4096)), +- BinContent = <<"------------ei4GI3GI3Ij5Ef1ae0KM7Ij5ei4Ij5\r\nContent-Disposition: form-data; name=\"Filename\"\r\n\r\nhello.txt\r\n------------ei4GI3GI3Ij5Ef1ae0KM7Ij5ei4Ij5\r\nContent-Disposition: form-data; name=\"success_action_status\"\r\n\r\n201\r\n------------ei4GI3GI3Ij5Ef1ae0KM7Ij5ei4Ij5\r\nContent-Disposition: form-data; name=\"file\"; filename=\"hello.txt\"\r\nContent-Type: application/octet-stream\r\n\r\n", Chunk/binary, "\r\n------------ei4GI3GI3Ij5Ef1ae0KM7Ij5ei4Ij5\r\nContent-Disposition: form-data; name=\"Upload\"\r\n\r\nSubmit Query\r\n------------ei4GI3GI3Ij5Ef1ae0KM7Ij5ei4Ij5--">>, +- Expect = [{headers, +- [{"content-disposition", +- {"form-data", [{"name", "Filename"}]}}]}, +- {body, <<"hello.txt">>}, +- body_end, +- {headers, +- [{"content-disposition", +- {"form-data", [{"name", "success_action_status"}]}}]}, +- {body, <<"201">>}, +- body_end, +- {headers, +- [{"content-disposition", +- {"form-data", [{"name", "file"}, {"filename", "hello.txt"}]}}, +- {"content-type", {"application/octet-stream", []}}]}, +- {body, Chunk}, +- body_end, +- {headers, +- [{"content-disposition", +- {"form-data", [{"name", "Upload"}]}}]}, +- {body, <<"Submit Query">>}, +- body_end, +- eof], +- TestCallback = fun (Next) -> test_callback(Next, Expect) end, +- ServerFun = fun (Socket) -> +- ok = gen_tcp:send(Socket, BinContent), +- exit(normal) +- end, +- ClientFun = fun (Socket) -> +- Req = fake_request(Socket, ContentType, +- byte_size(BinContent)), +- Res = parse_multipart_request(Req, TestCallback), +- {0, <<>>, ok} = Res, +- ok +- end, +- ok = with_socket_server(ServerFun, ClientFun), +- ok. +- +-test() -> +- test_find_in_binary(), +- test_find_boundary(), +- test_parse(), +- test_parse2(), +- test_parse3(), +- test_parse_form(), +- test_flash_parse(), +- test_flash_parse2(), +- ok. +diff --git a/src/mochiweb/mochiweb_request.erl b/src/mochiweb/mochiweb_request.erl +deleted file mode 100644 +index 5d7af26..0000000 +--- a/src/mochiweb/mochiweb_request.erl ++++ /dev/null +@@ -1,867 +0,0 @@ +-%% @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([accepted_encodings/1]). +--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()}]. +- +-% 5 minute default idle timeout +--define(IDLE_TIMEOUT, 300000). +- +-% 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, 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, Fun, FunState) -> +- Fun({0, <<>>}, FunState); +-stream_unchunked_body(Length, Fun, FunState) when Length > 0 -> +- Bin = recv(0), +- BinSize = byte_size(Bin), +- if BinSize > Length -> +- <> = Bin, +- gen_tcp:unrecv(Socket, Extra), +- NewState = Fun({Length, OurBody}, FunState), +- stream_unchunked_body(0, Fun, NewState); +- true -> +- NewState = Fun({BinSize, Bin}, FunState), +- stream_unchunked_body(Length - BinSize, Fun, NewState) +- end. +- +- +-%% @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]), +- case filelib:is_dir(FullPath) of +- true -> +- maybe_redirect(RelPath, FullPath, ExtraHeaders); +- false -> +- maybe_serve_file(FullPath, ExtraHeaders) +- end +- end. +- +-%% Internal API +- +-%% This has the same effect as the DirectoryIndex directive in httpd +-directory_index(FullPath) -> +- filename:join([FullPath, "index.html"]). +- +-maybe_redirect([], FullPath, ExtraHeaders) -> +- maybe_serve_file(directory_index(FullPath), ExtraHeaders); +- +-maybe_redirect(RelPath, FullPath, ExtraHeaders) -> +- case string:right(RelPath, 1) of +- "/" -> +- maybe_serve_file(directory_index(FullPath), ExtraHeaders); +- _ -> +- Host = mochiweb_headers:get_value("host", Headers), +- Location = "http://" ++ Host ++ "/" ++ RelPath ++ "/", +- LocationBin = list_to_binary(Location), +- MoreHeaders = [{"Location", Location}, +- {"Content-Type", "text/html"} | ExtraHeaders], +- Top = <<"" +- "" +- "301 Moved Permanently" +- "" +- "

Moved Permanently

" +- "

The document has moved >, +- Bottom = <<">here.

\n">>, +- Body = <>, +- respond({301, MoreHeaders, Body}) +- end. +- +-maybe_serve_file(File, ExtraHeaders) -> +- 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. +- +-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. +- +-%% @spec accepted_encodings([encoding()]) -> [encoding()] | error() +-%% @type encoding() -> string() +-%% @type error() -> bad_accept_encoding_value +-%% +-%% @doc Returns a list of encodings accepted by a request. Encodings that are +-%% not supported by the server will not be included in the return list. +-%% This list is computed from the "Accept-Encoding" header and +-%% its elements are ordered, descendingly, according to their Q values. +-%% +-%% Section 14.3 of the RFC 2616 (HTTP 1.1) describes the "Accept-Encoding" +-%% header and the process of determining which server supported encodings +-%% can be used for encoding the body for the request's response. +-%% +-%% Examples +-%% +-%% 1) For a missing "Accept-Encoding" header: +-%% accepted_encodings(["gzip", "identity"]) -> ["identity"] +-%% +-%% 2) For an "Accept-Encoding" header with value "gzip, deflate": +-%% accepted_encodings(["gzip", "identity"]) -> ["gzip", "identity"] +-%% +-%% 3) For an "Accept-Encoding" header with value "gzip;q=0.5, deflate": +-%% accepted_encodings(["gzip", "deflate", "identity"]) -> +-%% ["deflate", "gzip", "identity"] +-%% +-accepted_encodings(SupportedEncodings) -> +- AcceptEncodingHeader = case get_header_value("Accept-Encoding") of +- undefined -> +- ""; +- Value -> +- Value +- end, +- case mochiweb_util:parse_qvalues(AcceptEncodingHeader) of +- invalid_qvalue_string -> +- bad_accept_encoding_value; +- QList -> +- mochiweb_util:pick_accepted_encodings( +- QList, SupportedEncodings, "identity" +- ) +- 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 = byte_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/src/mochiweb/mochiweb_response.erl b/src/mochiweb/mochiweb_response.erl +deleted file mode 100644 +index 6285c4c..0000000 +--- a/src/mochiweb/mochiweb_response.erl ++++ /dev/null +@@ -1,56 +0,0 @@ +-%% @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/src/mochiweb/mochiweb_skel.erl b/src/mochiweb/mochiweb_skel.erl +deleted file mode 100644 +index 36b48be..0000000 +--- a/src/mochiweb/mochiweb_skel.erl ++++ /dev/null +@@ -1,73 +0,0 @@ +--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/src/mochiweb/mochiweb_socket_server.erl b/src/mochiweb/mochiweb_socket_server.erl +deleted file mode 100644 +index 7aafe29..0000000 +--- a/src/mochiweb/mochiweb_socket_server.erl ++++ /dev/null +@@ -1,248 +0,0 @@ +-%% @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=128}). +- +-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/src/mochiweb/mochiweb_sup.erl b/src/mochiweb/mochiweb_sup.erl +deleted file mode 100644 +index 5cb525b..0000000 +--- a/src/mochiweb/mochiweb_sup.erl ++++ /dev/null +@@ -1,34 +0,0 @@ +-%% @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/src/mochiweb/mochiweb_util.erl b/src/mochiweb/mochiweb_util.erl +deleted file mode 100644 +index d8fc89d..0000000 +--- a/src/mochiweb/mochiweb_util.erl ++++ /dev/null +@@ -1,859 +0,0 @@ +-%% @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([parse_qvalues/1, pick_accepted_encodings/3]). +--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 tuple_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]). +- +-%% @spec parse_qvalues(string()) -> [qvalue()] | error() +-%% @type qvalue() -> {element(), q()} +-%% @type element() -> string() +-%% @type q() -> 0.0 .. 1.0 +-%% @type error() -> invalid_qvalue_string +-%% +-%% @doc Parses a list (given as a string) of elements with Q values associated +-%% to them. Elements are separated by commas and each element is separated +-%% from its Q value by a semicolon. Q values are optional but when missing +-%% the value of an element is considered as 1.0. A Q value is always in the +-%% range [0.0, 1.0]. A Q value list is used for example as the value of the +-%% HTTP "Accept-Encoding" header. +-%% +-%% Q values are described in section 2.9 of the RFC 2616 (HTTP 1.1). +-%% +-%% Example: +-%% +-%% parse_qvalues("gzip; q=0.5, deflate, identity;q=0.0") -> +-%% [{"gzip", 0.5}, {"deflate", 1.0}, {"identity", 0.0}] +-%% +-parse_qvalues(QValuesStr) -> +- try +- {ok, Re} = re:compile("^\\s*q\\s*=\\s*((?:0|1)(?:\\.\\d{1,3})?)\\s*$"), +- lists:map( +- fun(Pair) -> +- case string:tokens(Pair, ";") of +- [Enc] -> +- {string:strip(Enc), 1.0}; +- [Enc, QStr] -> +- case re:run(QStr, Re, [{capture, [1], list}]) of +- {match, [Q]} -> +- QVal = case Q of +- "0" -> +- 0.0; +- "1" -> +- 1.0; +- Else -> +- list_to_float(Else) +- end, +- case QVal < 0.0 orelse QVal > 1.0 of +- false -> +- {string:strip(Enc), QVal} +- end +- end +- end +- end, +- string:tokens(string:to_lower(QValuesStr), ",") +- ) +- catch +- _Type:_Error -> +- invalid_qvalue_string +- end. +- +-%% @spec pick_accepted_encodings(qvalues(), [encoding()], encoding()) -> +-%% [encoding()] +-%% @type qvalues() -> [ {encoding(), q()} ] +-%% @type encoding() -> string() +-%% @type q() -> 0.0 .. 1.0 +-%% +-%% @doc Determines which encodings specified in the given Q values list are +-%% valid according to a list of supported encodings and a default encoding. +-%% +-%% The returned list of encodings is sorted, descendingly, according to the +-%% Q values of the given list. The last element of this list is the given +-%% default encoding unless this encoding is explicitily or implicitily +-%% marked with a Q value of 0.0 in the given Q values list. +-%% Note: encodings with the same Q value are kept in the same order as +-%% found in the input Q values list. +-%% +-%% This encoding picking process is described in section 14.3 of the +-%% RFC 2616 (HTTP 1.1). +-%% +-%% Example: +-%% +-%% pick_accepted_encodings( +-%% [{"gzip", 0.5}, {"deflate", 1.0}], +-%% ["gzip", "identity"], +-%% "identity" +-%% ) -> +-%% ["gzip", "identity"] +-%% +-pick_accepted_encodings(AcceptedEncs, SupportedEncs, DefaultEnc) -> +- SortedQList = lists:reverse( +- lists:sort(fun({_, Q1}, {_, Q2}) -> Q1 < Q2 end, AcceptedEncs) +- ), +- {Accepted, Refused} = lists:foldr( +- fun({E, Q}, {A, R}) -> +- case Q > 0.0 of +- true -> +- {[E | A], R}; +- false -> +- {A, [E | R]} +- end +- end, +- {[], []}, +- SortedQList +- ), +- Refused1 = lists:foldr( +- fun(Enc, Acc) -> +- case Enc of +- "*" -> +- lists:subtract(SupportedEncs, Accepted) ++ Acc; +- _ -> +- [Enc | Acc] +- end +- end, +- [], +- Refused +- ), +- Accepted1 = lists:foldr( +- fun(Enc, Acc) -> +- case Enc of +- "*" -> +- lists:subtract(SupportedEncs, Accepted ++ Refused1) ++ Acc; +- _ -> +- [Enc | Acc] +- end +- end, +- [], +- Accepted +- ), +- Accepted2 = case lists:member(DefaultEnc, Accepted1) of +- true -> +- Accepted1; +- false -> +- Accepted1 ++ [DefaultEnc] +- end, +- [E || E <- Accepted2, lists:member(E, SupportedEncs), +- not lists:member(E, Refused1)]. +- +-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(), +- test_parse_qvalues(), +- test_pick_accepted_encodings(), +- 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. +- +-test_parse_qvalues() -> +- [] = parse_qvalues(""), +- [{"identity", 0.0}] = parse_qvalues("identity;q=0"), +- [{"identity", 0.0}] = parse_qvalues("identity ;q=0"), +- [{"identity", 0.0}] = parse_qvalues(" identity; q =0 "), +- [{"identity", 0.0}] = parse_qvalues("identity ; q = 0"), +- [{"identity", 0.0}] = parse_qvalues("identity ; q= 0.0"), +- [{"gzip", 1.0}, {"deflate", 1.0}, {"identity", 0.0}] = parse_qvalues( +- "gzip,deflate,identity;q=0.0" +- ), +- [{"deflate", 1.0}, {"gzip", 1.0}, {"identity", 0.0}] = parse_qvalues( +- "deflate,gzip,identity;q=0.0" +- ), +- [{"gzip", 1.0}, {"deflate", 1.0}, {"gzip", 1.0}, {"identity", 0.0}] = +- parse_qvalues("gzip,deflate,gzip,identity;q=0"), +- [{"gzip", 1.0}, {"deflate", 1.0}, {"identity", 0.0}] = parse_qvalues( +- "gzip, deflate , identity; q=0.0" +- ), +- [{"gzip", 1.0}, {"deflate", 1.0}, {"identity", 0.0}] = parse_qvalues( +- "gzip; q=1, deflate;q=1.0, identity;q=0.0" +- ), +- [{"gzip", 0.5}, {"deflate", 1.0}, {"identity", 0.0}] = parse_qvalues( +- "gzip; q=0.5, deflate;q=1.0, identity;q=0" +- ), +- [{"gzip", 0.5}, {"deflate", 1.0}, {"identity", 0.0}] = parse_qvalues( +- "gzip; q=0.5, deflate , identity;q=0.0" +- ), +- [{"gzip", 0.5}, {"deflate", 0.8}, {"identity", 0.0}] = parse_qvalues( +- "gzip; q=0.5, deflate;q=0.8, identity;q=0.0" +- ), +- [{"gzip", 0.5}, {"deflate", 1.0}, {"identity", 1.0}] = parse_qvalues( +- "gzip; q=0.5,deflate,identity" +- ), +- [{"gzip", 0.5}, {"deflate", 1.0}, {"identity", 1.0}, {"identity", 1.0}] = +- parse_qvalues("gzip; q=0.5,deflate,identity, identity "), +- invalid_qvalue_string = parse_qvalues("gzip; q=1.1, deflate"), +- invalid_qvalue_string = parse_qvalues("gzip; q=0.5, deflate;q=2"), +- invalid_qvalue_string = parse_qvalues("gzip, deflate;q=AB"), +- invalid_qvalue_string = parse_qvalues("gzip; q=2.1, deflate"), +- ok. +- +-test_pick_accepted_encodings() -> +- ["identity"] = pick_accepted_encodings( +- [], +- ["gzip", "identity"], +- "identity" +- ), +- ["gzip", "identity"] = pick_accepted_encodings( +- [{"gzip", 1.0}], +- ["gzip", "identity"], +- "identity" +- ), +- ["identity"] = pick_accepted_encodings( +- [{"gzip", 0.0}], +- ["gzip", "identity"], +- "identity" +- ), +- ["gzip", "identity"] = pick_accepted_encodings( +- [{"gzip", 1.0}, {"deflate", 1.0}], +- ["gzip", "identity"], +- "identity" +- ), +- ["gzip", "identity"] = pick_accepted_encodings( +- [{"gzip", 0.5}, {"deflate", 1.0}], +- ["gzip", "identity"], +- "identity" +- ), +- ["identity"] = pick_accepted_encodings( +- [{"gzip", 0.0}, {"deflate", 0.0}], +- ["gzip", "identity"], +- "identity" +- ), +- ["gzip"] = pick_accepted_encodings( +- [{"gzip", 1.0}, {"deflate", 1.0}, {"identity", 0.0}], +- ["gzip", "identity"], +- "identity" +- ), +- ["gzip", "deflate", "identity"] = pick_accepted_encodings( +- [{"gzip", 1.0}, {"deflate", 1.0}], +- ["gzip", "deflate", "identity"], +- "identity" +- ), +- ["gzip", "deflate"] = pick_accepted_encodings( +- [{"gzip", 1.0}, {"deflate", 1.0}, {"identity", 0.0}], +- ["gzip", "deflate", "identity"], +- "identity" +- ), +- ["deflate", "gzip", "identity"] = pick_accepted_encodings( +- [{"gzip", 0.2}, {"deflate", 1.0}], +- ["gzip", "deflate", "identity"], +- "identity" +- ), +- ["deflate", "deflate", "gzip", "identity"] = pick_accepted_encodings( +- [{"gzip", 0.2}, {"deflate", 1.0}, {"deflate", 1.0}], +- ["gzip", "deflate", "identity"], +- "identity" +- ), +- ["deflate", "gzip", "gzip", "identity"] = pick_accepted_encodings( +- [{"gzip", 0.2}, {"deflate", 1.0}, {"gzip", 1.0}], +- ["gzip", "deflate", "identity"], +- "identity" +- ), +- ["gzip", "deflate", "gzip", "identity"] = pick_accepted_encodings( +- [{"gzip", 0.2}, {"deflate", 0.9}, {"gzip", 1.0}], +- ["gzip", "deflate", "identity"], +- "identity" +- ), +- [] = pick_accepted_encodings( +- [{"*", 0.0}], +- ["gzip", "deflate", "identity"], +- "identity" +- ), +- ["gzip", "deflate", "identity"] = pick_accepted_encodings( +- [{"*", 1.0}], +- ["gzip", "deflate", "identity"], +- "identity" +- ), +- ["gzip", "deflate", "identity"] = pick_accepted_encodings( +- [{"*", 0.6}], +- ["gzip", "deflate", "identity"], +- "identity" +- ), +- ["gzip"] = pick_accepted_encodings( +- [{"gzip", 1.0}, {"*", 0.0}], +- ["gzip", "deflate", "identity"], +- "identity" +- ), +- ["gzip", "deflate"] = pick_accepted_encodings( +- [{"gzip", 1.0}, {"deflate", 0.6}, {"*", 0.0}], +- ["gzip", "deflate", "identity"], +- "identity" +- ), +- ["deflate", "gzip"] = pick_accepted_encodings( +- [{"gzip", 0.5}, {"deflate", 1.0}, {"*", 0.0}], +- ["gzip", "deflate", "identity"], +- "identity" +- ), +- ["gzip", "identity"] = pick_accepted_encodings( +- [{"deflate", 0.0}, {"*", 1.0}], +- ["gzip", "deflate", "identity"], +- "identity" +- ), +- ["gzip", "identity"] = pick_accepted_encodings( +- [{"*", 1.0}, {"deflate", 0.0}], +- ["gzip", "deflate", "identity"], +- "identity" +- ), +- ok. +diff --git a/src/mochiweb/reloader.erl b/src/mochiweb/reloader.erl +deleted file mode 100644 +index 6835f8f..0000000 +--- a/src/mochiweb/reloader.erl ++++ /dev/null +@@ -1,123 +0,0 @@ +-%% @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, #file_info{mtime = Mtime}} when Mtime >= From, 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/test/etap/test_util.erl.in b/test/etap/test_util.erl.in +index c57d7a8..948958c 100644 +--- a/test/etap/test_util.erl.in ++++ b/test/etap/test_util.erl.in +@@ -22,7 +22,7 @@ builddir() -> + "@abs_top_builddir@". + + init_code_path() -> +- Paths = ["couchdb", "ibrowse", "mochiweb"], ++ Paths = ["couchdb", "ibrowse"], + lists:foreach(fun(Name) -> + code:add_pathz(filename:join([builddir(), "src", Name])) + end, Paths). +-- +1.7.1.1 + diff --git a/couchdb-1.0.0-0006-Remove-bundled-ibrowse-library.patch b/couchdb-1.0.0-0006-Remove-bundled-ibrowse-library.patch new file mode 100644 index 0000000..d59a690 --- /dev/null +++ b/couchdb-1.0.0-0006-Remove-bundled-ibrowse-library.patch @@ -0,0 +1,4088 @@ +From 2b7c6d5c4a3ee803ec39a6f6a1cffbbca4b4b95a Mon Sep 17 00:00:00 2001 +From: Peter Lemenkov +Date: Wed, 14 Jul 2010 18:03:33 +0400 +Subject: [PATCH 6/9] Remove bundled ibrowse library + +--- + configure | 3 - + configure.ac | 1 - + src/Makefile.am | 2 +- + src/Makefile.in | 2 +- + src/ibrowse/Makefile.am | 49 -- + src/ibrowse/Makefile.in | 484 ------------ + src/ibrowse/ibrowse.app.in | 13 - + src/ibrowse/ibrowse.erl | 760 ------------------ + src/ibrowse/ibrowse.hrl | 12 - + src/ibrowse/ibrowse_app.erl | 64 -- + src/ibrowse/ibrowse_http_client.erl | 1476 ----------------------------------- + src/ibrowse/ibrowse_lb.erl | 216 ----- + src/ibrowse/ibrowse_lib.erl | 399 ---------- + src/ibrowse/ibrowse_sup.erl | 65 -- + src/ibrowse/ibrowse_test.erl | 377 --------- + test/etap/test_util.erl.in | 2 +- + 16 files changed, 3 insertions(+), 3922 deletions(-) + delete mode 100644 src/ibrowse/Makefile.am + delete mode 100644 src/ibrowse/Makefile.in + delete mode 100644 src/ibrowse/ibrowse.app.in + delete mode 100644 src/ibrowse/ibrowse.erl + delete mode 100644 src/ibrowse/ibrowse.hrl + delete mode 100644 src/ibrowse/ibrowse_app.erl + delete mode 100644 src/ibrowse/ibrowse_http_client.erl + delete mode 100644 src/ibrowse/ibrowse_lb.erl + delete mode 100644 src/ibrowse/ibrowse_lib.erl + delete mode 100644 src/ibrowse/ibrowse_sup.erl + delete mode 100644 src/ibrowse/ibrowse_test.erl + +diff --git a/configure b/configure +index 6a9c731..c369391 100755 +--- a/configure ++++ b/configure +@@ -12261,8 +12261,6 @@ ac_config_files="$ac_config_files src/couchdb/Makefile" + + ac_config_files="$ac_config_files src/couchdb/priv/Makefile" + +-ac_config_files="$ac_config_files src/ibrowse/Makefile" +- + ac_config_files="$ac_config_files test/Makefile" + + ac_config_files="$ac_config_files test/bench/Makefile" +@@ -13284,7 +13282,6 @@ do + "src/couchdb/couch.app.tpl") CONFIG_FILES="$CONFIG_FILES src/couchdb/couch.app.tpl" ;; + "src/couchdb/Makefile") CONFIG_FILES="$CONFIG_FILES src/couchdb/Makefile" ;; + "src/couchdb/priv/Makefile") CONFIG_FILES="$CONFIG_FILES src/couchdb/priv/Makefile" ;; +- "src/ibrowse/Makefile") CONFIG_FILES="$CONFIG_FILES src/ibrowse/Makefile" ;; + "test/Makefile") CONFIG_FILES="$CONFIG_FILES test/Makefile" ;; + "test/bench/Makefile") CONFIG_FILES="$CONFIG_FILES test/bench/Makefile" ;; + "test/etap/Makefile") CONFIG_FILES="$CONFIG_FILES test/etap/Makefile" ;; +diff --git a/configure.ac b/configure.ac +index b5a19ef..f867060 100644 +--- a/configure.ac ++++ b/configure.ac +@@ -387,7 +387,6 @@ AC_CONFIG_FILES([src/Makefile]) + AC_CONFIG_FILES([src/couchdb/couch.app.tpl]) + AC_CONFIG_FILES([src/couchdb/Makefile]) + AC_CONFIG_FILES([src/couchdb/priv/Makefile]) +-AC_CONFIG_FILES([src/ibrowse/Makefile]) + AC_CONFIG_FILES([test/Makefile]) + AC_CONFIG_FILES([test/bench/Makefile]) + AC_CONFIG_FILES([test/etap/Makefile]) +diff --git a/src/Makefile.am b/src/Makefile.am +index 5a6646f..753b177 100644 +--- a/src/Makefile.am ++++ b/src/Makefile.am +@@ -10,4 +10,4 @@ + ## License for the specific language governing permissions and limitations under + ## the License. + +-SUBDIRS = couchdb ibrowse ++SUBDIRS = couchdb +diff --git a/src/Makefile.in b/src/Makefile.in +index 2d11fc8..93c5f43 100644 +--- a/src/Makefile.in ++++ b/src/Makefile.in +@@ -248,7 +248,7 @@ version_minor = @version_minor@ + version_release = @version_release@ + version_revision = @version_revision@ + version_stage = @version_stage@ +-SUBDIRS = couchdb ibrowse ++SUBDIRS = couchdb + all: all-recursive + + .SUFFIXES: +diff --git a/src/ibrowse/Makefile.am b/src/ibrowse/Makefile.am +deleted file mode 100644 +index 510f36a..0000000 +--- a/src/ibrowse/Makefile.am ++++ /dev/null +@@ -1,49 +0,0 @@ +-## 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. +- +-ibrowseebindir = $(localerlanglibdir)/ibrowse-1.5.2/ebin +- +-ibrowse_file_collection = \ +- ibrowse.app.in \ +- ibrowse.erl \ +- ibrowse_app.erl \ +- ibrowse_http_client.erl \ +- ibrowse_lb.erl \ +- ibrowse_lib.erl \ +- ibrowse_sup.erl \ +- ibrowse_test.erl +- +-ibrowseebin_make_generated_file_list = \ +- ibrowse.app \ +- ibrowse.beam \ +- ibrowse_app.beam \ +- ibrowse_http_client.beam \ +- ibrowse_lb.beam \ +- ibrowse_lib.beam \ +- ibrowse_sup.beam \ +- ibrowse_test.beam +- +-ibrowseebin_DATA = \ +- $(ibrowseebin_make_generated_file_list) +- +-EXTRA_DIST = \ +- $(ibrowse_file_collection) \ +- ibrowse.hrl +- +-CLEANFILES = \ +- $(ibrowseebin_make_generated_file_list) +- +-%.app: %.app.in +- cp $< $@ +- +-%.beam: %.erl +- $(ERLC) $(ERLC_FLAGS) $< +diff --git a/src/ibrowse/Makefile.in b/src/ibrowse/Makefile.in +deleted file mode 100644 +index 47581bc..0000000 +--- a/src/ibrowse/Makefile.in ++++ /dev/null +@@ -1,484 +0,0 @@ +-# Makefile.in generated by automake 1.11 from Makefile.am. +-# @configure_input@ +- +-# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, +-# 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation, +-# Inc. +-# This Makefile.in is free software; the Free Software Foundation +-# gives unlimited permission to copy and/or distribute it, +-# with or without modifications, as long as this notice is preserved. +- +-# This program is distributed in the hope that it will be useful, +-# but WITHOUT ANY WARRANTY, to the extent permitted by law; without +-# even the implied warranty of MERCHANTABILITY or FITNESS FOR A +-# PARTICULAR PURPOSE. +- +-@SET_MAKE@ +- +-VPATH = @srcdir@ +-pkgdatadir = $(datadir)/@PACKAGE@ +-pkgincludedir = $(includedir)/@PACKAGE@ +-pkglibdir = $(libdir)/@PACKAGE@ +-pkglibexecdir = $(libexecdir)/@PACKAGE@ +-am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd +-install_sh_DATA = $(install_sh) -c -m 644 +-install_sh_PROGRAM = $(install_sh) -c +-install_sh_SCRIPT = $(install_sh) -c +-INSTALL_HEADER = $(INSTALL_DATA) +-transform = $(program_transform_name) +-NORMAL_INSTALL = : +-PRE_INSTALL = : +-POST_INSTALL = : +-NORMAL_UNINSTALL = : +-PRE_UNINSTALL = : +-POST_UNINSTALL = : +-build_triplet = @build@ +-host_triplet = @host@ +-subdir = src/ibrowse +-DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in +-ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 +-am__aclocal_m4_deps = $(top_srcdir)/m4/ac_check_curl.m4 \ +- $(top_srcdir)/m4/ac_check_icu.m4 $(top_srcdir)/m4/libtool.m4 \ +- $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \ +- $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \ +- $(top_srcdir)/acinclude.m4 $(top_srcdir)/configure.ac +-am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ +- $(ACLOCAL_M4) +-mkinstalldirs = $(install_sh) -d +-CONFIG_HEADER = $(top_builddir)/config.h +-CONFIG_CLEAN_FILES = +-CONFIG_CLEAN_VPATH_FILES = +-SOURCES = +-DIST_SOURCES = +-am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; +-am__vpath_adj = case $$p in \ +- $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ +- *) f=$$p;; \ +- esac; +-am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; +-am__install_max = 40 +-am__nobase_strip_setup = \ +- srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` +-am__nobase_strip = \ +- for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" +-am__nobase_list = $(am__nobase_strip_setup); \ +- for p in $$list; do echo "$$p $$p"; done | \ +- sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ +- $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ +- if (++n[$$2] == $(am__install_max)) \ +- { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ +- END { for (dir in files) print dir, files[dir] }' +-am__base_list = \ +- sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ +- sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' +-am__installdirs = "$(DESTDIR)$(ibrowseebindir)" +-DATA = $(ibrowseebin_DATA) +-DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) +-ACLOCAL = @ACLOCAL@ +-AMTAR = @AMTAR@ +-AR = @AR@ +-AUTOCONF = @AUTOCONF@ +-AUTOHEADER = @AUTOHEADER@ +-AUTOMAKE = @AUTOMAKE@ +-AWK = @AWK@ +-CC = @CC@ +-CCDEPMODE = @CCDEPMODE@ +-CFLAGS = @CFLAGS@ +-CPP = @CPP@ +-CPPFLAGS = @CPPFLAGS@ +-CURL_CFLAGS = @CURL_CFLAGS@ +-CURL_CONFIG = @CURL_CONFIG@ +-CURL_LDFLAGS = @CURL_LDFLAGS@ +-CURL_LIBS = @CURL_LIBS@ +-CYGPATH_W = @CYGPATH_W@ +-DEFS = @DEFS@ +-DEPDIR = @DEPDIR@ +-DSYMUTIL = @DSYMUTIL@ +-DUMPBIN = @DUMPBIN@ +-ECHO_C = @ECHO_C@ +-ECHO_N = @ECHO_N@ +-ECHO_T = @ECHO_T@ +-EGREP = @EGREP@ +-ERL = @ERL@ +-ERLC = @ERLC@ +-ERLC_FLAGS = @ERLC_FLAGS@ +-EXEEXT = @EXEEXT@ +-FGREP = @FGREP@ +-FLAGS = @FLAGS@ +-GREP = @GREP@ +-HELP2MAN_EXECUTABLE = @HELP2MAN_EXECUTABLE@ +-ICU_CFLAGS = @ICU_CFLAGS@ +-ICU_CONFIG = @ICU_CONFIG@ +-ICU_CXXFLAGS = @ICU_CXXFLAGS@ +-ICU_LIBS = @ICU_LIBS@ +-ICU_LOCAL_BIN = @ICU_LOCAL_BIN@ +-ICU_LOCAL_CFLAGS = @ICU_LOCAL_CFLAGS@ +-ICU_LOCAL_LDFLAGS = @ICU_LOCAL_LDFLAGS@ +-INNO_COMPILER_EXECUTABLE = @INNO_COMPILER_EXECUTABLE@ +-INSTALL = @INSTALL@ +-INSTALL_DATA = @INSTALL_DATA@ +-INSTALL_PROGRAM = @INSTALL_PROGRAM@ +-INSTALL_SCRIPT = @INSTALL_SCRIPT@ +-INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ +-JSLIB = @JSLIB@ +-JS_LIB_BASE = @JS_LIB_BASE@ +-JS_LIB_BINARY = @JS_LIB_BINARY@ +-JS_LIB_DIR = @JS_LIB_DIR@ +-LD = @LD@ +-LDFLAGS = @LDFLAGS@ +-LIBOBJS = @LIBOBJS@ +-LIBS = @LIBS@ +-LIBTOOL = @LIBTOOL@ +-LIPO = @LIPO@ +-LN_S = @LN_S@ +-LTLIBOBJS = @LTLIBOBJS@ +-MAKEINFO = @MAKEINFO@ +-MKDIR_P = @MKDIR_P@ +-NM = @NM@ +-NMEDIT = @NMEDIT@ +-OBJDUMP = @OBJDUMP@ +-OBJEXT = @OBJEXT@ +-OTOOL = @OTOOL@ +-OTOOL64 = @OTOOL64@ +-PACKAGE = @PACKAGE@ +-PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ +-PACKAGE_NAME = @PACKAGE_NAME@ +-PACKAGE_STRING = @PACKAGE_STRING@ +-PACKAGE_TARNAME = @PACKAGE_TARNAME@ +-PACKAGE_URL = @PACKAGE_URL@ +-PACKAGE_VERSION = @PACKAGE_VERSION@ +-PATH_SEPARATOR = @PATH_SEPARATOR@ +-RANLIB = @RANLIB@ +-SED = @SED@ +-SET_MAKE = @SET_MAKE@ +-SHELL = @SHELL@ +-STRIP = @STRIP@ +-VERSION = @VERSION@ +-abs_builddir = @abs_builddir@ +-abs_srcdir = @abs_srcdir@ +-abs_top_builddir = @abs_top_builddir@ +-abs_top_srcdir = @abs_top_srcdir@ +-ac_ct_CC = @ac_ct_CC@ +-ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ +-am__include = @am__include@ +-am__leading_dot = @am__leading_dot@ +-am__quote = @am__quote@ +-am__tar = @am__tar@ +-am__untar = @am__untar@ +-bindir = @bindir@ +-bug_uri = @bug_uri@ +-build = @build@ +-build_alias = @build_alias@ +-build_cpu = @build_cpu@ +-build_os = @build_os@ +-build_vendor = @build_vendor@ +-builddir = @builddir@ +-datadir = @datadir@ +-datarootdir = @datarootdir@ +-docdir = @docdir@ +-dvidir = @dvidir@ +-exec_prefix = @exec_prefix@ +-host = @host@ +-host_alias = @host_alias@ +-host_cpu = @host_cpu@ +-host_os = @host_os@ +-host_vendor = @host_vendor@ +-htmldir = @htmldir@ +-includedir = @includedir@ +-infodir = @infodir@ +-initdir = @initdir@ +-install_sh = @install_sh@ +-launchddir = @launchddir@ +-libdir = @libdir@ +-libexecdir = @libexecdir@ +-localconfdir = @localconfdir@ +-localdatadir = @localdatadir@ +-localdocdir = @localdocdir@ +-localedir = @localedir@ +-localerlanglibdir = @localerlanglibdir@ +-locallibbindir = @locallibbindir@ +-locallibdir = @locallibdir@ +-localstatedir = @localstatedir@ +-localstatelibdir = @localstatelibdir@ +-localstatelogdir = @localstatelogdir@ +-localstaterundir = @localstaterundir@ +-lt_ECHO = @lt_ECHO@ +-mandir = @mandir@ +-mkdir_p = @mkdir_p@ +-msvc_redist_dir = @msvc_redist_dir@ +-msvc_redist_name = @msvc_redist_name@ +-oldincludedir = @oldincludedir@ +-openssl_bin_dir = @openssl_bin_dir@ +-package_author_address = @package_author_address@ +-package_author_name = @package_author_name@ +-package_identifier = @package_identifier@ +-package_name = @package_name@ +-package_tarname = @package_tarname@ +-pdfdir = @pdfdir@ +-prefix = @prefix@ +-program_transform_name = @program_transform_name@ +-psdir = @psdir@ +-sbindir = @sbindir@ +-sharedstatedir = @sharedstatedir@ +-srcdir = @srcdir@ +-sysconfdir = @sysconfdir@ +-target_alias = @target_alias@ +-top_build_prefix = @top_build_prefix@ +-top_builddir = @top_builddir@ +-top_srcdir = @top_srcdir@ +-version = @version@ +-version_major = @version_major@ +-version_minor = @version_minor@ +-version_release = @version_release@ +-version_revision = @version_revision@ +-version_stage = @version_stage@ +-ibrowseebindir = $(localerlanglibdir)/ibrowse-1.5.2/ebin +-ibrowse_file_collection = \ +- ibrowse.app.in \ +- ibrowse.erl \ +- ibrowse_app.erl \ +- ibrowse_http_client.erl \ +- ibrowse_lb.erl \ +- ibrowse_lib.erl \ +- ibrowse_sup.erl \ +- ibrowse_test.erl +- +-ibrowseebin_make_generated_file_list = \ +- ibrowse.app \ +- ibrowse.beam \ +- ibrowse_app.beam \ +- ibrowse_http_client.beam \ +- ibrowse_lb.beam \ +- ibrowse_lib.beam \ +- ibrowse_sup.beam \ +- ibrowse_test.beam +- +-ibrowseebin_DATA = \ +- $(ibrowseebin_make_generated_file_list) +- +-EXTRA_DIST = \ +- $(ibrowse_file_collection) \ +- ibrowse.hrl +- +-CLEANFILES = \ +- $(ibrowseebin_make_generated_file_list) +- +-all: all-am +- +-.SUFFIXES: +-$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) +- @for dep in $?; do \ +- case '$(am__configure_deps)' in \ +- *$$dep*) \ +- ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ +- && { if test -f $@; then exit 0; else break; fi; }; \ +- exit 1;; \ +- esac; \ +- done; \ +- echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/ibrowse/Makefile'; \ +- $(am__cd) $(top_srcdir) && \ +- $(AUTOMAKE) --foreign src/ibrowse/Makefile +-.PRECIOUS: Makefile +-Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status +- @case '$?' in \ +- *config.status*) \ +- cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ +- *) \ +- echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ +- cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ +- esac; +- +-$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) +- cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +- +-$(top_srcdir)/configure: $(am__configure_deps) +- cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +-$(ACLOCAL_M4): $(am__aclocal_m4_deps) +- cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh +-$(am__aclocal_m4_deps): +- +-mostlyclean-libtool: +- -rm -f *.lo +- +-clean-libtool: +- -rm -rf .libs _libs +-install-ibrowseebinDATA: $(ibrowseebin_DATA) +- @$(NORMAL_INSTALL) +- test -z "$(ibrowseebindir)" || $(MKDIR_P) "$(DESTDIR)$(ibrowseebindir)" +- @list='$(ibrowseebin_DATA)'; test -n "$(ibrowseebindir)" || list=; \ +- for p in $$list; do \ +- if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ +- echo "$$d$$p"; \ +- done | $(am__base_list) | \ +- while read files; do \ +- echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(ibrowseebindir)'"; \ +- $(INSTALL_DATA) $$files "$(DESTDIR)$(ibrowseebindir)" || exit $$?; \ +- done +- +-uninstall-ibrowseebinDATA: +- @$(NORMAL_UNINSTALL) +- @list='$(ibrowseebin_DATA)'; test -n "$(ibrowseebindir)" || list=; \ +- files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ +- test -n "$$files" || exit 0; \ +- echo " ( cd '$(DESTDIR)$(ibrowseebindir)' && rm -f" $$files ")"; \ +- cd "$(DESTDIR)$(ibrowseebindir)" && rm -f $$files +-tags: TAGS +-TAGS: +- +-ctags: CTAGS +-CTAGS: +- +- +-distdir: $(DISTFILES) +- @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ +- topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ +- list='$(DISTFILES)'; \ +- dist_files=`for file in $$list; do echo $$file; done | \ +- sed -e "s|^$$srcdirstrip/||;t" \ +- -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ +- case $$dist_files in \ +- */*) $(MKDIR_P) `echo "$$dist_files" | \ +- sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ +- sort -u` ;; \ +- esac; \ +- for file in $$dist_files; do \ +- if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ +- if test -d $$d/$$file; then \ +- dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ +- if test -d "$(distdir)/$$file"; then \ +- find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ +- fi; \ +- if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ +- cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ +- find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ +- fi; \ +- cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ +- else \ +- test -f "$(distdir)/$$file" \ +- || cp -p $$d/$$file "$(distdir)/$$file" \ +- || exit 1; \ +- fi; \ +- done +-check-am: all-am +-check: check-am +-all-am: Makefile $(DATA) +-installdirs: +- for dir in "$(DESTDIR)$(ibrowseebindir)"; do \ +- test -z "$$dir" || $(MKDIR_P) "$$dir"; \ +- done +-install: install-am +-install-exec: install-exec-am +-install-data: install-data-am +-uninstall: uninstall-am +- +-install-am: all-am +- @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am +- +-installcheck: installcheck-am +-install-strip: +- $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ +- install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ +- `test -z '$(STRIP)' || \ +- echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install +-mostlyclean-generic: +- +-clean-generic: +- -test -z "$(CLEANFILES)" || rm -f $(CLEANFILES) +- +-distclean-generic: +- -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) +- -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) +- +-maintainer-clean-generic: +- @echo "This command is intended for maintainers to use" +- @echo "it deletes files that may require special tools to rebuild." +-clean: clean-am +- +-clean-am: clean-generic clean-libtool mostlyclean-am +- +-distclean: distclean-am +- -rm -f Makefile +-distclean-am: clean-am distclean-generic +- +-dvi: dvi-am +- +-dvi-am: +- +-html: html-am +- +-html-am: +- +-info: info-am +- +-info-am: +- +-install-data-am: install-ibrowseebinDATA +- +-install-dvi: install-dvi-am +- +-install-dvi-am: +- +-install-exec-am: +- +-install-html: install-html-am +- +-install-html-am: +- +-install-info: install-info-am +- +-install-info-am: +- +-install-man: +- +-install-pdf: install-pdf-am +- +-install-pdf-am: +- +-install-ps: install-ps-am +- +-install-ps-am: +- +-installcheck-am: +- +-maintainer-clean: maintainer-clean-am +- -rm -f Makefile +-maintainer-clean-am: distclean-am maintainer-clean-generic +- +-mostlyclean: mostlyclean-am +- +-mostlyclean-am: mostlyclean-generic mostlyclean-libtool +- +-pdf: pdf-am +- +-pdf-am: +- +-ps: ps-am +- +-ps-am: +- +-uninstall-am: uninstall-ibrowseebinDATA +- +-.MAKE: install-am install-strip +- +-.PHONY: all all-am check check-am clean clean-generic clean-libtool \ +- distclean distclean-generic distclean-libtool distdir dvi \ +- dvi-am html html-am info info-am install install-am \ +- install-data install-data-am install-dvi install-dvi-am \ +- install-exec install-exec-am install-html install-html-am \ +- install-ibrowseebinDATA install-info install-info-am \ +- install-man install-pdf install-pdf-am install-ps \ +- install-ps-am install-strip installcheck installcheck-am \ +- installdirs maintainer-clean maintainer-clean-generic \ +- mostlyclean mostlyclean-generic mostlyclean-libtool pdf pdf-am \ +- ps ps-am uninstall uninstall-am uninstall-ibrowseebinDATA +- +- +-%.app: %.app.in +- cp $< $@ +- +-%.beam: %.erl +- $(ERLC) $(ERLC_FLAGS) $< +- +-# Tell versions [3.59,3.63) of GNU make to not export all variables. +-# Otherwise a system limit (for SysV at least) may be exceeded. +-.NOEXPORT: +diff --git a/src/ibrowse/ibrowse.app.in b/src/ibrowse/ibrowse.app.in +deleted file mode 100644 +index 4f43dd9..0000000 +--- a/src/ibrowse/ibrowse.app.in ++++ /dev/null +@@ -1,13 +0,0 @@ +-{application, ibrowse, +- [{description, "HTTP client application"}, +- {vsn, "1.5.1"}, +- {modules, [ ibrowse, +- ibrowse_http_client, +- ibrowse_app, +- ibrowse_sup, +- ibrowse_lib, +- ibrowse_lb ]}, +- {registered, []}, +- {applications, [kernel,stdlib,sasl]}, +- {env, []}, +- {mod, {ibrowse_app, []}}]}. +diff --git a/src/ibrowse/ibrowse.erl b/src/ibrowse/ibrowse.erl +deleted file mode 100644 +index 1913ef5..0000000 +--- a/src/ibrowse/ibrowse.erl ++++ /dev/null +@@ -1,760 +0,0 @@ +-%%%------------------------------------------------------------------- +-%%% File : ibrowse.erl +-%%% Author : Chandrashekhar Mullaparthi +-%%% Description : Load balancer process for HTTP client connections. +-%%% +-%%% Created : 11 Oct 2003 by Chandrashekhar Mullaparthi +-%%%------------------------------------------------------------------- +-%% @author Chandrashekhar Mullaparthi +-%% @copyright 2005-2009 Chandrashekhar Mullaparthi +-%% @version 1.5.2 +-%% @doc The ibrowse application implements an HTTP 1.1 client. This +-%% module implements the API of the HTTP client. There is one named +-%% process called 'ibrowse' which assists in load balancing and maintaining configuration. There is one load balancing process per unique webserver. There is +-%% one process to handle one TCP connection to a webserver +-%% (implemented in the module ibrowse_http_client). Multiple connections to a +-%% webserver are setup based on the settings for each webserver. The +-%% ibrowse process also determines which connection to pipeline a +-%% certain request on. The functions to call are send_req/3, +-%% send_req/4, send_req/5, send_req/6. +-%% +-%%

Here are a few sample invocations.

+-%% +-%% +-%% ibrowse:send_req("http://intranet/messenger/", [], get). +-%%

+-%% +-%% ibrowse:send_req("http://www.google.com/", [], get, [], +-%% [{proxy_user, "XXXXX"}, +-%% {proxy_password, "XXXXX"}, +-%% {proxy_host, "proxy"}, +-%% {proxy_port, 8080}], 1000). +-%%

+-%% +-%%ibrowse:send_req("http://www.erlang.org/download/otp_src_R10B-3.tar.gz", [], get, [], +-%% [{proxy_user, "XXXXX"}, +-%% {proxy_password, "XXXXX"}, +-%% {proxy_host, "proxy"}, +-%% {proxy_port, 8080}, +-%% {save_response_to_file, true}], 1000). +-%%

+-%% +-%% ibrowse:send_req("http://www.erlang.org", [], head). +-%% +-%%

+-%% ibrowse:send_req("http://www.sun.com", [], options). +-%% +-%%

+-%% ibrowse:send_req("http://www.bbc.co.uk", [], trace). +-%% +-%%

+-%% ibrowse:send_req("http://www.google.com", [], get, [], +-%% [{stream_to, self()}]). +-%%
+-%% +-%%

A driver exists which implements URL encoding in C, but the +-%% speed achieved using only erlang has been good enough, so the +-%% driver isn't actually used.

+- +--module(ibrowse). +--vsn('$Id: ibrowse.erl,v 1.8 2009/07/01 22:43:19 chandrusf Exp $ '). +- +--behaviour(gen_server). +-%%-------------------------------------------------------------------- +-%% Include files +-%%-------------------------------------------------------------------- +- +-%%-------------------------------------------------------------------- +-%% External exports +--export([start_link/0, start/0, stop/0]). +- +-%% gen_server callbacks +--export([init/1, handle_call/3, handle_cast/2, handle_info/2, +- terminate/2, code_change/3]). +- +-%% API interface +--export([ +- rescan_config/0, +- rescan_config/1, +- get_config_value/1, +- get_config_value/2, +- spawn_worker_process/2, +- spawn_link_worker_process/2, +- stop_worker_process/1, +- send_req/3, +- send_req/4, +- send_req/5, +- send_req/6, +- send_req_direct/4, +- send_req_direct/5, +- send_req_direct/6, +- send_req_direct/7, +- stream_next/1, +- set_max_sessions/3, +- set_max_pipeline_size/3, +- set_dest/3, +- trace_on/0, +- trace_off/0, +- trace_on/2, +- trace_off/2, +- all_trace_off/0, +- show_dest_status/0, +- show_dest_status/2 +- ]). +- +--ifdef(debug). +--compile(export_all). +--endif. +- +--import(ibrowse_lib, [ +- parse_url/1, +- get_value/3, +- do_trace/2 +- ]). +- +--record(state, {trace = false}). +- +--include("ibrowse.hrl"). +--include_lib("stdlib/include/ms_transform.hrl"). +- +--define(DEF_MAX_SESSIONS,10). +--define(DEF_MAX_PIPELINE_SIZE,10). +- +-%%==================================================================== +-%% External functions +-%%==================================================================== +-%%-------------------------------------------------------------------- +-%% Function: start_link/0 +-%% Description: Starts the server +-%%-------------------------------------------------------------------- +-%% @doc Starts the ibrowse process linked to the calling process. Usually invoked by the supervisor ibrowse_sup +-%% @spec start_link() -> {ok, pid()} +-start_link() -> +- gen_server:start_link({local, ?MODULE}, ?MODULE, [], []). +- +-%% @doc Starts the ibrowse process without linking. Useful when testing using the shell +-start() -> +- gen_server:start({local, ?MODULE}, ?MODULE, [], [{debug, []}]). +- +-%% @doc Stop the ibrowse process. Useful when testing using the shell. +-stop() -> +- catch gen_server:call(ibrowse, stop). +- +-%% @doc This is the basic function to send a HTTP request. +-%% The Status return value indicates the HTTP status code returned by the webserver +-%% @spec send_req(Url::string(), Headers::headerList(), Method::method()) -> response() +-%% headerList() = [{header(), value()}] +-%% header() = atom() | string() +-%% value() = term() +-%% method() = get | post | head | options | put | delete | trace | mkcol | propfind | proppatch | lock | unlock | move | copy +-%% Status = string() +-%% ResponseHeaders = [respHeader()] +-%% respHeader() = {headerName(), headerValue()} +-%% headerName() = string() +-%% headerValue() = string() +-%% response() = {ok, Status, ResponseHeaders, ResponseBody} | {ibrowse_req_id, req_id() } | {error, Reason} +-%% req_id() = term() +-%% ResponseBody = string() | {file, Filename} +-%% Reason = term() +-send_req(Url, Headers, Method) -> +- send_req(Url, Headers, Method, [], []). +- +-%% @doc Same as send_req/3. +-%% If a list is specified for the body it has to be a flat list. The body can also be a fun/0 or a fun/1.
+-%% If fun/0, the connection handling process will repeatdely call the fun until it returns an error or eof.
Fun() = {ok, Data} | eof

+-%% If fun/1, the connection handling process will repeatedly call the fun with the supplied state until it returns an error or eof.
Fun(State) = {ok, Data} | {ok, Data, NewState} | eof
+-%% @spec send_req(Url, Headers, Method::method(), Body::body()) -> response() +-%% body() = [] | string() | binary() | fun_arity_0() | {fun_arity_1(), initial_state()} +-%% initial_state() = term() +-send_req(Url, Headers, Method, Body) -> +- send_req(Url, Headers, Method, Body, []). +- +-%% @doc Same as send_req/4. +-%% For a description of SSL Options, look in the ssl manpage. If the +-%% HTTP Version to use is not specified, the default is 1.1. +-%%
+-%%

The host_header option is useful in the case where ibrowse is +-%% connecting to a component such as stunnel which then sets up a +-%% secure connection to a webserver. In this case, the URL supplied to +-%% ibrowse must have the stunnel host/port details, but that won't +-%% make sense to the destination webserver. This option can then be +-%% used to specify what should go in the Host header in +-%% the request.

+-%%
    +-%%
  • The stream_to option can be used to have the HTTP +-%% response streamed to a process as messages as data arrives on the +-%% socket. If the calling process wishes to control the rate at which +-%% data is received from the server, the option {stream_to, +-%% {process(), once}} can be specified. The calling process +-%% will have to invoke ibrowse:stream_next(Request_id) to +-%% receive the next packet.
  • +-%% +-%%
  • When both the options save_response_to_file and stream_to +-%% are specified, the former takes precedence.
  • +-%% +-%%
  • For the save_response_to_file option, the response body is saved to +-%% file only if the status code is in the 200-299 range. If not, the response body is returned +-%% as a string.
  • +-%%
  • Whenever an error occurs in the processing of a request, ibrowse will return as much +-%% information as it has, such as HTTP Status Code and HTTP Headers. When this happens, the response +-%% is of the form {error, {Reason, {stat_code, StatusCode}, HTTP_headers}}
  • +-%% +-%%
  • The inactivity_timeout option is useful when +-%% dealing with large response bodies and/or slow links. In these +-%% cases, it might be hard to estimate how long a request will take to +-%% complete. In such cases, the client might want to timeout if no +-%% data has been received on the link for a certain time interval.
  • +-%% +-%%
  • +-%% The connect_timeout option is to specify how long the +-%% client process should wait for connection establishment. This is +-%% useful in scenarios where connections to servers are usually setup +-%% very fast, but responses might take much longer compared to +-%% connection setup. In such cases, it is better for the calling +-%% process to timeout faster if there is a problem (DNS lookup +-%% delays/failures, network routing issues, etc). The total timeout +-%% value specified for the request will enforced. To illustrate using +-%% an example: +-%% +-%% ibrowse:send_req("http://www.example.com/cgi-bin/request", [], get, [], [{connect_timeout, 100}], 1000). +-%% +-%% In the above invocation, if the connection isn't established within +-%% 100 milliseconds, the request will fail with +-%% {error, conn_failed}.
    +-%% If connection setup succeeds, the total time allowed for the +-%% request to complete will be 1000 milliseconds minus the time taken +-%% for connection setup. +-%%
  • +-%%
+-%% +-%% @spec send_req(Url::string(), Headers::headerList(), Method::method(), Body::body(), Options::optionList()) -> response() +-%% optionList() = [option()] +-%% option() = {max_sessions, integer()} | +-%% {response_format,response_format()}| +-%% {stream_chunk_size, integer()} | +-%% {max_pipeline_size, integer()} | +-%% {trace, boolean()} | +-%% {is_ssl, boolean()} | +-%% {ssl_options, [SSLOpt]} | +-%% {pool_name, atom()} | +-%% {proxy_host, string()} | +-%% {proxy_port, integer()} | +-%% {proxy_user, string()} | +-%% {proxy_password, string()} | +-%% {use_absolute_uri, boolean()} | +-%% {basic_auth, {username(), password()}} | +-%% {cookie, string()} | +-%% {content_length, integer()} | +-%% {content_type, string()} | +-%% {save_response_to_file, srtf()} | +-%% {stream_to, stream_to()} | +-%% {http_vsn, {MajorVsn, MinorVsn}} | +-%% {host_header, string()} | +-%% {inactivity_timeout, integer()} | +-%% {connect_timeout, integer()} | +-%% {transfer_encoding, {chunked, ChunkSize}} +-%% +-%% stream_to() = process() | {process(), once} +-%% process() = pid() | atom() +-%% username() = string() +-%% password() = string() +-%% SSLOpt = term() +-%% ChunkSize = integer() +-%% srtf() = boolean() | filename() +-%% filename() = string() +-%% response_format() = list | binary +-send_req(Url, Headers, Method, Body, Options) -> +- send_req(Url, Headers, Method, Body, Options, 30000). +- +-%% @doc Same as send_req/5. +-%% All timeout values are in milliseconds. +-%% @spec send_req(Url, Headers::headerList(), Method::method(), Body::body(), Options::optionList(), Timeout) -> response() +-%% Timeout = integer() | infinity +-send_req(Url, Headers, Method, Body, Options, Timeout) -> +- case catch parse_url(Url) of +- #url{host = Host, +- port = Port, +- protocol = Protocol} = Parsed_url -> +- Lb_pid = case ets:lookup(ibrowse_lb, {Host, Port}) of +- [] -> +- get_lb_pid(Parsed_url); +- [#lb_pid{pid = Lb_pid_1}] -> +- Lb_pid_1 +- end, +- Max_sessions = get_max_sessions(Host, Port, Options), +- Max_pipeline_size = get_max_pipeline_size(Host, Port, Options), +- Options_1 = merge_options(Host, Port, Options), +- {SSLOptions, IsSSL} = +- case (Protocol == https) orelse +- get_value(is_ssl, Options_1, false) of +- false -> {[], false}; +- true -> {get_value(ssl_options, Options_1, []), true} +- end, +- case ibrowse_lb:spawn_connection(Lb_pid, Parsed_url, +- Max_sessions, +- Max_pipeline_size, +- {SSLOptions, IsSSL}) of +- {ok, Conn_Pid} -> +- do_send_req(Conn_Pid, Parsed_url, Headers, +- Method, Body, Options_1, Timeout); +- Err -> +- Err +- end; +- Err -> +- {error, {url_parsing_failed, Err}} +- end. +- +-merge_options(Host, Port, Options) -> +- Config_options = get_config_value({options, Host, Port}, []), +- lists:foldl( +- fun({Key, Val}, Acc) -> +- case lists:keysearch(Key, 1, Options) of +- false -> +- [{Key, Val} | Acc]; +- _ -> +- Acc +- end +- end, Options, Config_options). +- +-get_lb_pid(Url) -> +- gen_server:call(?MODULE, {get_lb_pid, Url}). +- +-get_max_sessions(Host, Port, Options) -> +- get_value(max_sessions, Options, +- get_config_value({max_sessions, Host, Port}, ?DEF_MAX_SESSIONS)). +- +-get_max_pipeline_size(Host, Port, Options) -> +- get_value(max_pipeline_size, Options, +- get_config_value({max_pipeline_size, Host, Port}, ?DEF_MAX_PIPELINE_SIZE)). +- +-%% @doc Deprecated. Use set_max_sessions/3 and set_max_pipeline_size/3 +-%% for achieving the same effect. +-set_dest(Host, Port, [{max_sessions, Max} | T]) -> +- set_max_sessions(Host, Port, Max), +- set_dest(Host, Port, T); +-set_dest(Host, Port, [{max_pipeline_size, Max} | T]) -> +- set_max_pipeline_size(Host, Port, Max), +- set_dest(Host, Port, T); +-set_dest(Host, Port, [{trace, Bool} | T]) when Bool == true; Bool == false -> +- ibrowse ! {trace, true, Host, Port}, +- set_dest(Host, Port, T); +-set_dest(_Host, _Port, [H | _]) -> +- exit({invalid_option, H}); +-set_dest(_, _, []) -> +- ok. +- +-%% @doc Set the maximum number of connections allowed to a specific Host:Port. +-%% @spec set_max_sessions(Host::string(), Port::integer(), Max::integer()) -> ok +-set_max_sessions(Host, Port, Max) when is_integer(Max), Max > 0 -> +- gen_server:call(?MODULE, {set_config_value, {max_sessions, Host, Port}, Max}). +- +-%% @doc Set the maximum pipeline size for each connection to a specific Host:Port. +-%% @spec set_max_pipeline_size(Host::string(), Port::integer(), Max::integer()) -> ok +-set_max_pipeline_size(Host, Port, Max) when is_integer(Max), Max > 0 -> +- gen_server:call(?MODULE, {set_config_value, {max_pipeline_size, Host, Port}, Max}). +- +-do_send_req(Conn_Pid, Parsed_url, Headers, Method, Body, Options, Timeout) -> +- case catch ibrowse_http_client:send_req(Conn_Pid, Parsed_url, +- Headers, Method, ensure_bin(Body), +- Options, Timeout) of +- {'EXIT', {timeout, _}} -> +- {error, req_timedout}; +- {'EXIT', Reason} -> +- {error, {'EXIT', Reason}}; +- {ok, St_code, Headers, Body} = Ret when is_binary(Body) -> +- case get_value(response_format, Options, list) of +- list -> +- {ok, St_code, Headers, binary_to_list(Body)}; +- binary -> +- Ret +- end; +- Ret -> +- Ret +- end. +- +-ensure_bin(L) when is_list(L) -> list_to_binary(L); +-ensure_bin(B) when is_binary(B) -> B; +-ensure_bin(Fun) when is_function(Fun) -> Fun; +-ensure_bin({Fun}) when is_function(Fun) -> Fun; +-ensure_bin({Fun, _} = Body) when is_function(Fun) -> Body. +- +-%% @doc Creates a HTTP client process to the specified Host:Port which +-%% is not part of the load balancing pool. This is useful in cases +-%% where some requests to a webserver might take a long time whereas +-%% some might take a very short time. To avoid getting these quick +-%% requests stuck in the pipeline behind time consuming requests, use +-%% this function to get a handle to a connection process.
+-%% Note: Calling this function only creates a worker process. No connection +-%% is setup. The connection attempt is made only when the first +-%% request is sent via any of the send_req_direct/4,5,6,7 functions.
+-%% Note: It is the responsibility of the calling process to control +-%% pipeline size on such connections. +-%% +-%% @spec spawn_worker_process(Host::string(), Port::integer()) -> {ok, pid()} +-spawn_worker_process(Host, Port) -> +- ibrowse_http_client:start({Host, Port}). +- +-%% @doc Same as spawn_worker_process/2 except the the calling process +-%% is linked to the worker process which is spawned. +-spawn_link_worker_process(Host, Port) -> +- ibrowse_http_client:start_link({Host, Port}). +- +-%% @doc Terminate a worker process spawned using +-%% spawn_worker_process/2 or spawn_link_worker_process/2. Requests in +-%% progress will get the error response
{error, closing_on_request}
+-%% @spec stop_worker_process(Conn_pid::pid()) -> ok +-stop_worker_process(Conn_pid) -> +- ibrowse_http_client:stop(Conn_pid). +- +-%% @doc Same as send_req/3 except that the first argument is the PID +-%% returned by spawn_worker_process/2 or spawn_link_worker_process/2 +-send_req_direct(Conn_pid, Url, Headers, Method) -> +- send_req_direct(Conn_pid, Url, Headers, Method, [], []). +- +-%% @doc Same as send_req/4 except that the first argument is the PID +-%% returned by spawn_worker_process/2 or spawn_link_worker_process/2 +-send_req_direct(Conn_pid, Url, Headers, Method, Body) -> +- send_req_direct(Conn_pid, Url, Headers, Method, Body, []). +- +-%% @doc Same as send_req/5 except that the first argument is the PID +-%% returned by spawn_worker_process/2 or spawn_link_worker_process/2 +-send_req_direct(Conn_pid, Url, Headers, Method, Body, Options) -> +- send_req_direct(Conn_pid, Url, Headers, Method, Body, Options, 30000). +- +-%% @doc Same as send_req/6 except that the first argument is the PID +-%% returned by spawn_worker_process/2 or spawn_link_worker_process/2 +-send_req_direct(Conn_pid, Url, Headers, Method, Body, Options, Timeout) -> +- case catch parse_url(Url) of +- #url{host = Host, +- port = Port} = Parsed_url -> +- Options_1 = merge_options(Host, Port, Options), +- case do_send_req(Conn_pid, Parsed_url, Headers, Method, Body, Options_1, Timeout) of +- {error, {'EXIT', {noproc, _}}} -> +- {error, worker_is_dead}; +- Ret -> +- Ret +- end; +- Err -> +- {error, {url_parsing_failed, Err}} +- end. +- +-%% @doc Tell ibrowse to stream the next chunk of data to the +-%% caller. Should be used in conjunction with the +-%% stream_to option +-%% @spec stream_next(Req_id :: req_id()) -> ok | {error, unknown_req_id} +-stream_next(Req_id) -> +- case ets:lookup(ibrowse_stream, {req_id_pid, Req_id}) of +- [] -> +- {error, unknown_req_id}; +- [{_, Pid}] -> +- catch Pid ! {stream_next, Req_id}, +- ok +- end. +- +-%% @doc Turn tracing on for the ibrowse process +-trace_on() -> +- ibrowse ! {trace, true}. +-%% @doc Turn tracing off for the ibrowse process +-trace_off() -> +- ibrowse ! {trace, false}. +- +-%% @doc Turn tracing on for all connections to the specified HTTP +-%% server. Host is whatever is specified as the domain name in the URL +-%% @spec trace_on(Host, Port) -> ok +-%% Host = string() +-%% Port = integer() +-trace_on(Host, Port) -> +- ibrowse ! {trace, true, Host, Port}, +- ok. +- +-%% @doc Turn tracing OFF for all connections to the specified HTTP +-%% server. +-%% @spec trace_off(Host, Port) -> ok +-trace_off(Host, Port) -> +- ibrowse ! {trace, false, Host, Port}, +- ok. +- +-%% @doc Turn Off ALL tracing +-%% @spec all_trace_off() -> ok +-all_trace_off() -> +- ibrowse ! all_trace_off, +- ok. +- +-show_dest_status() -> +- Dests = lists:filter(fun({lb_pid, {Host, Port}, _}) when is_list(Host), +- is_integer(Port) -> +- true; +- (_) -> +- false +- end, ets:tab2list(ibrowse_lb)), +- All_ets = ets:all(), +- io:format("~-40.40s | ~-5.5s | ~-10.10s | ~s~n", +- ["Server:port", "ETS", "Num conns", "LB Pid"]), +- io:format("~80.80.=s~n", [""]), +- lists:foreach(fun({lb_pid, {Host, Port}, Lb_pid}) -> +- case lists:dropwhile( +- fun(Tid) -> +- ets:info(Tid, owner) /= Lb_pid +- end, All_ets) of +- [] -> +- io:format("~40.40s | ~-5.5s | ~-5.5s | ~s~n", +- [Host ++ ":" ++ integer_to_list(Port), +- "", +- "", +- io_lib:format("~p", [Lb_pid])] +- ); +- [Tid | _] -> +- catch ( +- begin +- Size = ets:info(Tid, size), +- io:format("~40.40s | ~-5.5s | ~-5.5s | ~s~n", +- [Host ++ ":" ++ integer_to_list(Port), +- integer_to_list(Tid), +- integer_to_list(Size), +- io_lib:format("~p", [Lb_pid])] +- ) +- end +- ) +- end +- end, Dests). +- +-%% @doc Shows some internal information about load balancing to a +-%% specified Host:Port. Info about workers spawned using +-%% spawn_worker_process/2 or spawn_link_worker_process/2 is not +-%% included. +-show_dest_status(Host, Port) -> +- case ets:lookup(ibrowse_lb, {Host, Port}) of +- [] -> +- no_active_processes; +- [#lb_pid{pid = Lb_pid}] -> +- io:format("Load Balancer Pid : ~p~n", [Lb_pid]), +- io:format("LB process msg q size : ~p~n", [(catch process_info(Lb_pid, message_queue_len))]), +- case lists:dropwhile( +- fun(Tid) -> +- ets:info(Tid, owner) /= Lb_pid +- end, ets:all()) of +- [] -> +- io:format("Couldn't locate ETS table for ~p~n", [Lb_pid]); +- [Tid | _] -> +- First = ets:first(Tid), +- Last = ets:last(Tid), +- Size = ets:info(Tid, size), +- io:format("LB ETS table id : ~p~n", [Tid]), +- io:format("Num Connections : ~p~n", [Size]), +- case Size of +- 0 -> +- ok; +- _ -> +- {First_p_sz, _} = First, +- {Last_p_sz, _} = Last, +- io:format("Smallest pipeline : ~1000.p~n", [First_p_sz]), +- io:format("Largest pipeline : ~1000.p~n", [Last_p_sz]) +- end +- end +- end. +- +-%% @doc Clear current configuration for ibrowse and load from the file +-%% ibrowse.conf in the IBROWSE_EBIN/../priv directory. Current +-%% configuration is cleared only if the ibrowse.conf file is readable +-%% using file:consult/1 +-rescan_config() -> +- gen_server:call(?MODULE, rescan_config). +- +-%% Clear current configuration for ibrowse and load from the specified +-%% file. Current configuration is cleared only if the specified +-%% file is readable using file:consult/1 +-rescan_config(File) when is_list(File) -> +- gen_server:call(?MODULE, {rescan_config, File}). +- +-%%==================================================================== +-%% Server functions +-%%==================================================================== +- +-%%-------------------------------------------------------------------- +-%% Function: init/1 +-%% Description: Initiates the server +-%% Returns: {ok, State} | +-%% {ok, State, Timeout} | +-%% ignore | +-%% {stop, Reason} +-%%-------------------------------------------------------------------- +-init(_) -> +- process_flag(trap_exit, true), +- State = #state{}, +- put(my_trace_flag, State#state.trace), +- put(ibrowse_trace_token, "ibrowse"), +- ets:new(ibrowse_lb, [named_table, public, {keypos, 2}]), +- ets:new(ibrowse_conf, [named_table, protected, {keypos, 2}]), +- ets:new(ibrowse_stream, [named_table, public]), +- import_config(), +- {ok, #state{}}. +- +-import_config() -> +- case code:priv_dir(ibrowse) of +- {error, _} = Err -> +- Err; +- PrivDir -> +- Filename = filename:join(PrivDir, "ibrowse.conf"), +- import_config(Filename) +- end. +- +-import_config(Filename) -> +- case file:consult(Filename) of +- {ok, Terms} -> +- ets:delete_all_objects(ibrowse_conf), +- Fun = fun({dest, Host, Port, MaxSess, MaxPipe, Options}) +- when is_list(Host), is_integer(Port), +- is_integer(MaxSess), MaxSess > 0, +- is_integer(MaxPipe), MaxPipe > 0, is_list(Options) -> +- I = [{{max_sessions, Host, Port}, MaxSess}, +- {{max_pipeline_size, Host, Port}, MaxPipe}, +- {{options, Host, Port}, Options}], +- lists:foreach( +- fun({X, Y}) -> +- ets:insert(ibrowse_conf, +- #ibrowse_conf{key = X, +- value = Y}) +- end, I); +- ({K, V}) -> +- ets:insert(ibrowse_conf, +- #ibrowse_conf{key = K, +- value = V}); +- (X) -> +- io:format("Skipping unrecognised term: ~p~n", [X]) +- end, +- lists:foreach(Fun, Terms); +- Err -> +- Err +- end. +- +-%% @doc Internal export +-get_config_value(Key) -> +- [#ibrowse_conf{value = V}] = ets:lookup(ibrowse_conf, Key), +- V. +- +-%% @doc Internal export +-get_config_value(Key, DefVal) -> +- case ets:lookup(ibrowse_conf, Key) of +- [] -> +- DefVal; +- [#ibrowse_conf{value = V}] -> +- V +- end. +- +-set_config_value(Key, Val) -> +- ets:insert(ibrowse_conf, #ibrowse_conf{key = Key, value = Val}). +-%%-------------------------------------------------------------------- +-%% Function: handle_call/3 +-%% Description: Handling call messages +-%% Returns: {reply, Reply, State} | +-%% {reply, Reply, State, Timeout} | +-%% {noreply, State} | +-%% {noreply, State, Timeout} | +-%% {stop, Reason, Reply, State} | (terminate/2 is called) +-%% {stop, Reason, State} (terminate/2 is called) +-%%-------------------------------------------------------------------- +-handle_call({get_lb_pid, #url{host = Host, port = Port} = Url}, _From, State) -> +- Pid = do_get_connection(Url, ets:lookup(ibrowse_lb, {Host, Port})), +- {reply, Pid, State}; +- +-handle_call(stop, _From, State) -> +- do_trace("IBROWSE shutting down~n", []), +- {stop, normal, ok, State}; +- +-handle_call({set_config_value, Key, Val}, _From, State) -> +- set_config_value(Key, Val), +- {reply, ok, State}; +- +-handle_call(rescan_config, _From, State) -> +- Ret = (catch import_config()), +- {reply, Ret, State}; +- +-handle_call({rescan_config, File}, _From, State) -> +- Ret = (catch import_config(File)), +- {reply, Ret, State}; +- +-handle_call(Request, _From, State) -> +- Reply = {unknown_request, Request}, +- {reply, Reply, State}. +- +-%%-------------------------------------------------------------------- +-%% Function: handle_cast/2 +-%% Description: Handling cast messages +-%% Returns: {noreply, State} | +-%% {noreply, State, Timeout} | +-%% {stop, Reason, State} (terminate/2 is called) +-%%-------------------------------------------------------------------- +- +-handle_cast(_Msg, State) -> +- {noreply, State}. +- +-%%-------------------------------------------------------------------- +-%% Function: handle_info/2 +-%% Description: Handling all non call/cast messages +-%% Returns: {noreply, State} | +-%% {noreply, State, Timeout} | +-%% {stop, Reason, State} (terminate/2 is called) +-%%-------------------------------------------------------------------- +-handle_info(all_trace_off, State) -> +- Mspec = [{{ibrowse_conf,{trace,'$1','$2'},true},[],[{{'$1','$2'}}]}], +- Trace_on_dests = ets:select(ibrowse_conf, Mspec), +- Fun = fun(#lb_pid{host_port = {H, P}, pid = Pid}, _) -> +- case lists:member({H, P}, Trace_on_dests) of +- false -> +- ok; +- true -> +- catch Pid ! {trace, false} +- end; +- (_, Acc) -> +- Acc +- end, +- ets:foldl(Fun, undefined, ibrowse_lb), +- ets:select_delete(ibrowse_conf, [{{ibrowse_conf,{trace,'$1','$2'},true},[],['true']}]), +- {noreply, State}; +- +-handle_info({trace, Bool}, State) -> +- put(my_trace_flag, Bool), +- {noreply, State}; +- +-handle_info({trace, Bool, Host, Port}, State) -> +- Fun = fun(#lb_pid{host_port = {H, P}, pid = Pid}, _) +- when H == Host, +- P == Port -> +- catch Pid ! {trace, Bool}; +- (_, Acc) -> +- Acc +- end, +- ets:foldl(Fun, undefined, ibrowse_lb), +- ets:insert(ibrowse_conf, #ibrowse_conf{key = {trace, Host, Port}, +- value = Bool}), +- {noreply, State}; +- +-handle_info(_Info, State) -> +- {noreply, State}. +- +-%%-------------------------------------------------------------------- +-%% Function: terminate/2 +-%% Description: Shutdown the server +-%% Returns: any (ignored by gen_server) +-%%-------------------------------------------------------------------- +-terminate(_Reason, _State) -> +- ok. +- +-%%-------------------------------------------------------------------- +-%% Func: code_change/3 +-%% Purpose: Convert process state when code is changed +-%% Returns: {ok, NewState} +-%%-------------------------------------------------------------------- +-code_change(_OldVsn, State, _Extra) -> +- {ok, State}. +- +-%%-------------------------------------------------------------------- +-%%% Internal functions +-%%-------------------------------------------------------------------- +-do_get_connection(#url{host = Host, port = Port}, []) -> +- {ok, Pid} = ibrowse_lb:start_link([Host, Port]), +- ets:insert(ibrowse_lb, #lb_pid{host_port = {Host, Port}, pid = Pid}), +- Pid; +-do_get_connection(_Url, [#lb_pid{pid = Pid}]) -> +- Pid. +diff --git a/src/ibrowse/ibrowse.hrl b/src/ibrowse/ibrowse.hrl +deleted file mode 100644 +index ebf3bb3..0000000 +--- a/src/ibrowse/ibrowse.hrl ++++ /dev/null +@@ -1,12 +0,0 @@ +--ifndef(IBROWSE_HRL). +--define(IBROWSE_HRL, "ibrowse.hrl"). +- +--record(url, {abspath, host, port, username, password, path, protocol}). +- +--record(lb_pid, {host_port, pid}). +- +--record(client_conn, {key, cur_pipeline_size = 0, reqs_served = 0}). +- +--record(ibrowse_conf, {key, value}). +- +--endif. +diff --git a/src/ibrowse/ibrowse_app.erl b/src/ibrowse/ibrowse_app.erl +deleted file mode 100644 +index 8c83e8f..0000000 +--- a/src/ibrowse/ibrowse_app.erl ++++ /dev/null +@@ -1,64 +0,0 @@ +-%%%------------------------------------------------------------------- +-%%% File : ibrowse_app.erl +-%%% Author : Chandrashekhar Mullaparthi +-%%% Description : +-%%% +-%%% Created : 15 Oct 2003 by Chandrashekhar Mullaparthi +-%%%------------------------------------------------------------------- +--module(ibrowse_app). +--vsn('$Id: ibrowse_app.erl,v 1.1 2005/05/05 22:28:28 chandrusf Exp $ '). +- +--behaviour(application). +-%%-------------------------------------------------------------------- +-%% Include files +-%%-------------------------------------------------------------------- +- +-%%-------------------------------------------------------------------- +-%% External exports +-%%-------------------------------------------------------------------- +--export([ +- start/2, +- stop/1 +- ]). +- +-%%-------------------------------------------------------------------- +-%% Internal exports +-%%-------------------------------------------------------------------- +--export([ +- ]). +- +-%%-------------------------------------------------------------------- +-%% Macros +-%%-------------------------------------------------------------------- +- +-%%-------------------------------------------------------------------- +-%% Records +-%%-------------------------------------------------------------------- +- +-%%==================================================================== +-%% External functions +-%%==================================================================== +-%%-------------------------------------------------------------------- +-%% Func: start/2 +-%% Returns: {ok, Pid} | +-%% {ok, Pid, State} | +-%% {error, Reason} +-%%-------------------------------------------------------------------- +-start(_Type, _StartArgs) -> +- case ibrowse_sup:start_link() of +- {ok, Pid} -> +- {ok, Pid}; +- Error -> +- Error +- end. +- +-%%-------------------------------------------------------------------- +-%% Func: stop/1 +-%% Returns: any +-%%-------------------------------------------------------------------- +-stop(_State) -> +- ok. +- +-%%==================================================================== +-%% Internal functions +-%%==================================================================== +diff --git a/src/ibrowse/ibrowse_http_client.erl b/src/ibrowse/ibrowse_http_client.erl +deleted file mode 100644 +index a767b84..0000000 +--- a/src/ibrowse/ibrowse_http_client.erl ++++ /dev/null +@@ -1,1476 +0,0 @@ +-%%%------------------------------------------------------------------- +-%%% File : ibrowse_http_client.erl +-%%% Author : Chandrashekhar Mullaparthi +-%%% Description : The name says it all +-%%% +-%%% Created : 11 Oct 2003 by Chandrashekhar Mullaparthi +-%%%------------------------------------------------------------------- +--module(ibrowse_http_client). +--vsn('$Id: ibrowse_http_client.erl,v 1.19 2009/07/01 22:43:19 chandrusf Exp $ '). +- +--behaviour(gen_server). +-%%-------------------------------------------------------------------- +-%% Include files +-%%-------------------------------------------------------------------- +- +-%%-------------------------------------------------------------------- +-%% External exports +--export([ +- start_link/1, +- start/1, +- stop/1, +- send_req/7 +- ]). +- +--ifdef(debug). +--compile(export_all). +--endif. +- +-%% gen_server callbacks +--export([ +- init/1, +- handle_call/3, +- handle_cast/2, +- handle_info/2, +- terminate/2, +- code_change/3 +- ]). +- +--include("ibrowse.hrl"). +- +--record(state, {host, port, +- use_proxy = false, proxy_auth_digest, +- ssl_options = [], is_ssl = false, socket, +- reqs=queue:new(), cur_req, status=idle, http_status_code, +- reply_buffer = <<>>, rep_buf_size=0, streamed_size = 0, +- recvd_headers=[], +- is_closing, send_timer, content_length, +- deleted_crlf = false, transfer_encoding, +- chunk_size, chunk_size_buffer = <<>>, recvd_chunk_size, +- lb_ets_tid, cur_pipeline_size = 0, prev_req_id +- }). +- +--record(request, {url, method, options, from, +- stream_to, caller_controls_socket = false, +- req_id, +- stream_chunk_size, +- save_response_to_file = false, +- tmp_file_name, tmp_file_fd, +- response_format}). +- +--import(ibrowse_lib, [ +- get_value/2, +- get_value/3, +- do_trace/2 +- ]). +- +--define(DEFAULT_STREAM_CHUNK_SIZE, 1024*1024). +- +-%%==================================================================== +-%% External functions +-%%==================================================================== +-%%-------------------------------------------------------------------- +-%% Function: start_link/0 +-%% Description: Starts the server +-%%-------------------------------------------------------------------- +-start(Args) -> +- gen_server:start(?MODULE, Args, []). +- +-start_link(Args) -> +- gen_server:start_link(?MODULE, Args, []). +- +-stop(Conn_pid) -> +- gen_server:call(Conn_pid, stop). +- +-send_req(Conn_Pid, Url, Headers, Method, Body, Options, Timeout) -> +- gen_server:call( +- Conn_Pid, +- {send_req, {Url, Headers, Method, Body, Options, Timeout}}, Timeout). +- +-%%==================================================================== +-%% Server functions +-%%==================================================================== +- +-%%-------------------------------------------------------------------- +-%% Function: init/1 +-%% Description: Initiates the server +-%% Returns: {ok, State} | +-%% {ok, State, Timeout} | +-%% ignore | +-%% {stop, Reason} +-%%-------------------------------------------------------------------- +-init({Lb_Tid, #url{host = Host, port = Port}, {SSLOptions, Is_ssl}}) -> +- State = #state{host = Host, +- port = Port, +- ssl_options = SSLOptions, +- is_ssl = Is_ssl, +- lb_ets_tid = Lb_Tid}, +- put(ibrowse_trace_token, [Host, $:, integer_to_list(Port)]), +- put(my_trace_flag, ibrowse_lib:get_trace_status(Host, Port)), +- {ok, State}; +-init({Host, Port}) -> +- State = #state{host = Host, +- port = Port}, +- put(ibrowse_trace_token, [Host, $:, integer_to_list(Port)]), +- put(my_trace_flag, ibrowse_lib:get_trace_status(Host, Port)), +- {ok, State}; +-init(#url{host=Host, port=Port, protocol=Protocol}) -> +- State = #state{ +- host = Host, +- port = Port, +- is_ssl = (Protocol == https), +- ssl_options = [{ssl_imp, new}] +- }, +- put(ibrowse_trace_token, [Host, $:, integer_to_list(Port)]), +- put(my_trace_flag, ibrowse_lib:get_trace_status(Host, Port)), +- {ok, State}. +- +-%%-------------------------------------------------------------------- +-%% Function: handle_call/3 +-%% Description: Handling call messages +-%% Returns: {reply, Reply, State} | +-%% {reply, Reply, State, Timeout} | +-%% {noreply, State} | +-%% {noreply, State, Timeout} | +-%% {stop, Reason, Reply, State} | (terminate/2 is called) +-%% {stop, Reason, State} (terminate/2 is called) +-%%-------------------------------------------------------------------- +-%% Received a request when the remote server has already sent us a +-%% Connection: Close header +-handle_call({send_req, _}, _From, #state{is_closing = true} = State) -> +- {reply, {error, connection_closing}, State}; +- +-handle_call({send_req, {Url, Headers, Method, Body, Options, Timeout}}, +- From, State) -> +- send_req_1(From, Url, Headers, Method, Body, Options, Timeout, State); +- +-handle_call(stop, _From, State) -> +- do_close(State), +- do_error_reply(State, closing_on_request), +- {stop, normal, ok, State#state{socket=undefined}}; +- +-handle_call(Request, _From, State) -> +- Reply = {unknown_request, Request}, +- {reply, Reply, State}. +- +-%%-------------------------------------------------------------------- +-%% Function: handle_cast/2 +-%% Description: Handling cast messages +-%% Returns: {noreply, State} | +-%% {noreply, State, Timeout} | +-%% {stop, Reason, State} (terminate/2 is called) +-%%-------------------------------------------------------------------- +-handle_cast(_Msg, State) -> +- {noreply, State}. +- +-%%-------------------------------------------------------------------- +-%% Function: handle_info/2 +-%% Description: Handling all non call/cast messages +-%% Returns: {noreply, State} | +-%% {noreply, State, Timeout} | +-%% {stop, Reason, State} (terminate/2 is called) +-%%-------------------------------------------------------------------- +-handle_info({tcp, _Sock, Data}, #state{status = Status} = State) -> +- do_trace("Data recvd in state: ~p. Size: ~p. ~p~n~n", [Status, size(Data), Data]), +- handle_sock_data(Data, State); +-handle_info({ssl, _Sock, Data}, State) -> +- handle_sock_data(Data, State); +- +-handle_info({stream_next, Req_id}, #state{socket = Socket, +- is_ssl = Is_ssl, +- cur_req = #request{req_id = Req_id}} = State) -> +- do_setopts(Socket, [{active, once}], Is_ssl), +- {noreply, State}; +- +-handle_info({stream_next, _Req_id}, State) -> +- {noreply, State}; +- +-handle_info({tcp_closed, _Sock}, State) -> +- do_trace("TCP connection closed by peer!~n", []), +- handle_sock_closed(State), +- {stop, normal, State}; +-handle_info({ssl_closed, _Sock}, State) -> +- do_trace("SSL connection closed by peer!~n", []), +- handle_sock_closed(State), +- {stop, normal, State}; +- +-handle_info({tcp_error, _Sock}, State) -> +- io:format("Error on connection to ~1000.p:~1000.p~n", [State#state.host, State#state.port]), +- handle_sock_closed(State), +- {stop, normal, State}; +-handle_info({ssl_error, _Sock}, State) -> +- io:format("Error on SSL connection to ~1000.p:~1000.p~n", [State#state.host, State#state.port]), +- handle_sock_closed(State), +- {stop, normal, State}; +- +-handle_info({req_timedout, From}, State) -> +- case lists:keysearch(From, #request.from, queue:to_list(State#state.reqs)) of +- false -> +- {noreply, State}; +- {value, _} -> +- shutting_down(State), +- do_error_reply(State, req_timedout), +- {stop, normal, State} +- end; +- +-handle_info(timeout, State) -> +- shutting_down(State), +- do_error_reply(State, req_timedout), +- {stop, normal, State}; +- +-handle_info({trace, Bool}, State) -> +- put(my_trace_flag, Bool), +- {noreply, State}; +- +-handle_info(Info, State) -> +- io:format("Unknown message recvd for ~1000.p:~1000.p -> ~p~n", +- [State#state.host, State#state.port, Info]), +- io:format("Recvd unknown message ~p when in state: ~p~n", [Info, State]), +- {noreply, State}. +- +-%%-------------------------------------------------------------------- +-%% Function: terminate/2 +-%% Description: Shutdown the server +-%% Returns: any (ignored by gen_server) +-%%-------------------------------------------------------------------- +-terminate(_Reason, State) -> +- do_close(State). +- +-%%-------------------------------------------------------------------- +-%% Func: code_change/3 +-%% Purpose: Convert process state when code is changed +-%% Returns: {ok, NewState} +-%%-------------------------------------------------------------------- +-code_change(_OldVsn, State, _Extra) -> +- {ok, State}. +- +-%%-------------------------------------------------------------------- +-%%% Internal functions +-%%-------------------------------------------------------------------- +- +-%%-------------------------------------------------------------------- +-%% Handles data recvd on the socket +-%%-------------------------------------------------------------------- +-handle_sock_data(Data, #state{status=idle}=State) -> +- do_trace("Data recvd on socket in state idle!. ~1000.p~n", [Data]), +- shutting_down(State), +- do_error_reply(State, data_in_status_idle), +- do_close(State), +- {stop, normal, State}; +- +-handle_sock_data(Data, #state{status = get_header}=State) -> +- case parse_response(Data, State) of +- {error, _Reason} -> +- shutting_down(State), +- {stop, normal, State}; +- stop -> +- shutting_down(State), +- {stop, normal, State}; +- State_1 -> +- active_once(State_1), +- {noreply, State_1, get_inac_timeout(State_1)} +- end; +- +-handle_sock_data(Data, #state{status = get_body, +- content_length = CL, +- http_status_code = StatCode, +- recvd_headers = Headers, +- chunk_size = CSz} = State) -> +- case (CL == undefined) and (CSz == undefined) of +- true -> +- case accumulate_response(Data, State) of +- {error, Reason} -> +- shutting_down(State), +- fail_pipelined_requests(State, +- {error, {Reason, {stat_code, StatCode}, Headers}}), +- {stop, normal, State}; +- State_1 -> +- active_once(State_1), +- {noreply, State_1, get_inac_timeout(State_1)} +- end; +- _ -> +- case parse_11_response(Data, State) of +- {error, Reason} -> +- shutting_down(State), +- fail_pipelined_requests(State, +- {error, {Reason, {stat_code, StatCode}, Headers}}), +- {stop, normal, State}; +- stop -> +- shutting_down(State), +- {stop, normal, State}; +- State_1 -> +- active_once(State_1), +- {noreply, State_1, get_inac_timeout(State_1)} +- end +- end. +- +-accumulate_response(Data, +- #state{ +- cur_req = #request{save_response_to_file = true, +- tmp_file_fd = undefined} = CurReq, +- http_status_code=[$2 | _]}=State) -> +- TmpFilename = make_tmp_filename(), +- case file:open(TmpFilename, [write, delayed_write, raw]) of +- {ok, Fd} -> +- accumulate_response(Data, State#state{ +- cur_req = CurReq#request{ +- tmp_file_fd = Fd, +- tmp_file_name = TmpFilename}}); +- {error, Reason} -> +- {error, {file_open_error, Reason}} +- end; +-accumulate_response(Data, #state{cur_req = #request{save_response_to_file = true, +- tmp_file_fd = Fd}, +- transfer_encoding=chunked, +- reply_buffer = Reply_buf, +- http_status_code=[$2 | _] +- } = State) -> +- case file:write(Fd, [Reply_buf, Data]) of +- ok -> +- State#state{reply_buffer = <<>>}; +- {error, Reason} -> +- {error, {file_write_error, Reason}} +- end; +-accumulate_response(Data, #state{cur_req = #request{save_response_to_file = true, +- tmp_file_fd = Fd}, +- reply_buffer = RepBuf, +- http_status_code=[$2 | _] +- } = State) -> +- case file:write(Fd, [RepBuf, Data]) of +- ok -> +- State#state{reply_buffer = <<>>}; +- {error, Reason} -> +- {error, {file_write_error, Reason}} +- end; +-accumulate_response(<<>>, State) -> +- State; +-accumulate_response(Data, #state{reply_buffer = RepBuf, +- rep_buf_size = RepBufSize, +- streamed_size = Streamed_size, +- cur_req = CurReq}=State) -> +- #request{stream_to=StreamTo, req_id=ReqId, +- stream_chunk_size = Stream_chunk_size, +- response_format = Response_format, +- caller_controls_socket = Caller_controls_socket} = CurReq, +- RepBuf_1 = list_to_binary([RepBuf, Data]), +- New_data_size = RepBufSize - Streamed_size, +- case StreamTo of +- undefined -> +- State#state{reply_buffer = RepBuf_1}; +- _ when Caller_controls_socket == true -> +- do_interim_reply(StreamTo, Response_format, ReqId, RepBuf_1), +- State#state{reply_buffer = <<>>, +- streamed_size = Streamed_size + size(RepBuf_1)}; +- _ when New_data_size >= Stream_chunk_size -> +- {Stream_chunk, Rem_data} = split_binary(RepBuf_1, Stream_chunk_size), +- do_interim_reply(StreamTo, Response_format, ReqId, Stream_chunk), +- accumulate_response( +- Rem_data, +- State#state{ +- reply_buffer = <<>>, +- streamed_size = Streamed_size + Stream_chunk_size}); +- _ -> +- State#state{reply_buffer = RepBuf_1} +- end. +- +-make_tmp_filename() -> +- DownloadDir = ibrowse:get_config_value(download_dir, filename:absname("./")), +- {A,B,C} = now(), +- filename:join([DownloadDir, +- "ibrowse_tmp_file_"++ +- integer_to_list(A) ++ +- integer_to_list(B) ++ +- integer_to_list(C)]). +- +- +-%%-------------------------------------------------------------------- +-%% Handles the case when the server closes the socket +-%%-------------------------------------------------------------------- +-handle_sock_closed(#state{status=get_header}=State) -> +- shutting_down(State), +- do_error_reply(State, connection_closed); +- +-handle_sock_closed(#state{cur_req=undefined} = State) -> +- shutting_down(State); +- +-%% We check for IsClosing because this the server could have sent a +-%% Connection-Close header and has closed the socket to indicate end +-%% of response. There maybe requests pipelined which need a response. +-handle_sock_closed(#state{reply_buffer = Buf, reqs = Reqs, http_status_code = SC, +- is_closing = IsClosing, +- cur_req = #request{tmp_file_name=TmpFilename, +- tmp_file_fd=Fd} = CurReq, +- status = get_body, recvd_headers = Headers}=State) -> +- #request{from=From, stream_to=StreamTo, req_id=ReqId, +- response_format = Resp_format} = CurReq, +- case IsClosing of +- true -> +- {_, Reqs_1} = queue:out(Reqs), +- case TmpFilename of +- undefined -> +- do_reply(State, From, StreamTo, ReqId, Resp_format, +- {ok, SC, Headers, Buf}); +- _ -> +- file:close(Fd), +- do_reply(State, From, StreamTo, ReqId, Resp_format, +- {ok, SC, Headers, {file, TmpFilename}}) +- end, +- do_error_reply(State#state{reqs = Reqs_1}, connection_closed), +- State; +- _ -> +- do_error_reply(State, connection_closed), +- State +- end. +- +-do_connect(Host, Port, _Options, #state{is_ssl=true, ssl_options=SSLOptions}, Timeout) -> +- ssl:connect(Host, Port, +- [binary, {nodelay, true}, {active, false} | SSLOptions], +- Timeout); +-do_connect(Host, Port, _Options, _State, Timeout) -> +- gen_tcp:connect(Host, Port, +- [binary, {nodelay, true}, {active, false}], +- Timeout). +- +-do_send(Req, #state{socket = Sock, is_ssl = true}) -> ssl:send(Sock, Req); +-do_send(Req, #state{socket = Sock, is_ssl = false}) -> gen_tcp:send(Sock, Req). +- +-%% @spec do_send_body(Sock::socket_descriptor(), Source::source_descriptor(), IsSSL::boolean()) -> ok | error() +-%% source_descriptor() = fun_arity_0 | +-%% {fun_arity_0} | +-%% {fun_arity_1, term()} +-%% error() = term() +-do_send_body(Source, State) when is_function(Source) -> +- do_send_body({Source}, State); +-do_send_body({Source}, State) when is_function(Source) -> +- do_send_body1(Source, Source(), State); +-do_send_body({Source, Source_state}, State) when is_function(Source) -> +- do_send_body1(Source, Source(Source_state), State); +-do_send_body(Body, State) -> +- do_send(Body, State). +- +-do_send_body1(Source, Resp, State) -> +- case Resp of +- {ok, Data} -> +- do_send(Data, State), +- do_send_body({Source}, State); +- {ok, Data, New_source_state} -> +- do_send(Data, State), +- do_send_body({Source, New_source_state}, State); +- eof -> +- ok; +- Err -> +- Err +- end. +- +-do_close(#state{socket = undefined}) -> ok; +-do_close(#state{socket = Sock, is_ssl = true}) -> ssl:close(Sock); +-do_close(#state{socket = Sock, is_ssl = false}) -> gen_tcp:close(Sock). +- +-active_once(#state{cur_req = #request{caller_controls_socket = true}}) -> +- ok; +-active_once(#state{socket = Socket, is_ssl = Is_ssl}) -> +- do_setopts(Socket, [{active, once}], Is_ssl). +- +-do_setopts(Sock, Opts, true) -> ssl:setopts(Sock, Opts); +-do_setopts(Sock, Opts, false) -> inet:setopts(Sock, Opts). +- +-check_ssl_options(Options, State) -> +- case get_value(is_ssl, Options, false) of +- false -> +- State; +- true -> +- State#state{is_ssl=true, ssl_options=get_value(ssl_options, Options)} +- end. +- +-send_req_1(From, +- #url{host = Host, +- port = Port} = Url, +- Headers, Method, Body, Options, Timeout, +- #state{socket = undefined} = State) -> +- {Host_1, Port_1, State_1} = +- case get_value(proxy_host, Options, false) of +- false -> +- {Host, Port, State}; +- PHost -> +- ProxyUser = get_value(proxy_user, Options, []), +- ProxyPassword = get_value(proxy_password, Options, []), +- Digest = http_auth_digest(ProxyUser, ProxyPassword), +- {PHost, get_value(proxy_port, Options, 80), +- State#state{use_proxy = true, +- proxy_auth_digest = Digest}} +- end, +- State_2 = check_ssl_options(Options, State_1), +- do_trace("Connecting...~n", []), +- Start_ts = now(), +- Conn_timeout = get_value(connect_timeout, Options, Timeout), +- case do_connect(Host_1, Port_1, Options, State_2, Conn_timeout) of +- {ok, Sock} -> +- do_trace("Connected!~n", []), +- End_ts = now(), +- Timeout_1 = case Timeout of +- infinity -> +- infinity; +- _ -> +- Timeout - trunc(round(timer:now_diff(End_ts, Start_ts) / 1000)) +- end, +- State_3 = State_2#state{socket = Sock}, +- send_req_1(From, Url, Headers, Method, Body, Options, Timeout_1, State_3); +- Err -> +- shutting_down(State_2), +- do_trace("Error connecting. Reason: ~1000.p~n", [Err]), +- gen_server:reply(From, {error, conn_failed}), +- {stop, normal, State_2} +- end; +-send_req_1(From, +- #url{abspath = AbsPath, +- host = Host, +- port = Port, +- path = RelPath} = Url, +- Headers, Method, Body, Options, Timeout, +- #state{status = Status} = State) -> +- ReqId = make_req_id(), +- Resp_format = get_value(response_format, Options, list), +- {StreamTo, Caller_controls_socket} = +- case get_value(stream_to, Options, undefined) of +- {Caller, once} when is_pid(Caller) or +- is_atom(Caller) -> +- Async_pid_rec = {{req_id_pid, ReqId}, self()}, +- true = ets:insert(ibrowse_stream, Async_pid_rec), +- {Caller, true}; +- undefined -> +- {undefined, false}; +- Caller when is_pid(Caller) or +- is_atom(Caller) -> +- {Caller, false}; +- Stream_to_inv -> +- exit({invalid_option, {stream_to, Stream_to_inv}}) +- end, +- SaveResponseToFile = get_value(save_response_to_file, Options, false), +- NewReq = #request{url = Url, +- method = Method, +- stream_to = StreamTo, +- caller_controls_socket = Caller_controls_socket, +- options = Options, +- req_id = ReqId, +- save_response_to_file = SaveResponseToFile, +- stream_chunk_size = get_stream_chunk_size(Options), +- response_format = Resp_format, +- from = From}, +- State_1 = State#state{reqs=queue:in(NewReq, State#state.reqs)}, +- Headers_1 = add_auth_headers(Url, Options, Headers, State), +- HostHeaderValue = case lists:keysearch(host_header, 1, Options) of +- false -> +- case Port of +- 80 -> Host; +- _ -> [Host, ":", integer_to_list(Port)] +- end; +- {value, {_, Host_h_val}} -> +- Host_h_val +- end, +- {Req, Body_1} = make_request(Method, +- [{"Host", HostHeaderValue} | Headers_1], +- AbsPath, RelPath, Body, Options, State#state.use_proxy), +- case get(my_trace_flag) of +- true -> +- %%Avoid the binary operations if trace is not on... +- NReq = binary_to_list(list_to_binary(Req)), +- do_trace("Sending request: ~n" +- "--- Request Begin ---~n~s~n" +- "--- Request End ---~n", [NReq]); +- _ -> ok +- end, +- case do_send(Req, State) of +- ok -> +- case do_send_body(Body_1, State) of +- ok -> +- State_2 = inc_pipeline_counter(State_1), +- active_once(State_1), +- Ref = case Timeout of +- infinity -> +- undefined; +- _ -> +- erlang:send_after(Timeout, self(), {req_timedout, From}) +- end, +- State_3 = case Status of +- idle -> +- State_2#state{status = get_header, +- cur_req = NewReq, +- send_timer = Ref}; +- _ -> +- State_2#state{send_timer = Ref} +- end, +- case StreamTo of +- undefined -> +- ok; +- _ -> +- gen_server:reply(From, {ibrowse_req_id, ReqId}) +- end, +- {noreply, State_3, get_inac_timeout(State_3)}; +- Err -> +- shutting_down(State_1), +- do_trace("Send failed... Reason: ~p~n", [Err]), +- gen_server:reply(From, {error, send_failed}), +- {stop, normal, State_1} +- end; +- Err -> +- shutting_down(State_1), +- do_trace("Send failed... Reason: ~p~n", [Err]), +- gen_server:reply(From, {error, send_failed}), +- {stop, normal, State_1} +- end. +- +-add_auth_headers(#url{username = User, +- password = UPw}, +- Options, +- Headers, +- #state{use_proxy = UseProxy, +- proxy_auth_digest = ProxyAuthDigest}) -> +- Headers_1 = case User of +- undefined -> +- case get_value(basic_auth, Options, undefined) of +- undefined -> +- Headers; +- {U,P} -> +- [{"Authorization", ["Basic ", http_auth_digest(U, P)]} | Headers] +- end; +- _ -> +- [{"Authorization", ["Basic ", http_auth_digest(User, UPw)]} | Headers] +- end, +- case UseProxy of +- false -> +- Headers_1; +- true when ProxyAuthDigest == [] -> +- Headers_1; +- true -> +- [{"Proxy-Authorization", ["Basic ", ProxyAuthDigest]} | Headers_1] +- end. +- +-http_auth_digest([], []) -> +- []; +-http_auth_digest(Username, Password) -> +- encode_base64(Username ++ [$: | Password]). +- +-encode_base64([]) -> +- []; +-encode_base64([A]) -> +- [e(A bsr 2), e((A band 3) bsl 4), $=, $=]; +-encode_base64([A,B]) -> +- [e(A bsr 2), e(((A band 3) bsl 4) bor (B bsr 4)), e((B band 15) bsl 2), $=]; +-encode_base64([A,B,C|Ls]) -> +- encode_base64_do(A,B,C, Ls). +-encode_base64_do(A,B,C, Rest) -> +- BB = (A bsl 16) bor (B bsl 8) bor C, +- [e(BB bsr 18), e((BB bsr 12) band 63), +- e((BB bsr 6) band 63), e(BB band 63)|encode_base64(Rest)]. +- +-e(X) when X >= 0, X < 26 -> X+65; +-e(X) when X>25, X<52 -> X+71; +-e(X) when X>51, X<62 -> X-4; +-e(62) -> $+; +-e(63) -> $/; +-e(X) -> exit({bad_encode_base64_token, X}). +- +-make_request(Method, Headers, AbsPath, RelPath, Body, Options, UseProxy) -> +- HttpVsn = http_vsn_string(get_value(http_vsn, Options, {1,1})), +- Headers_1 = +- case get_value(content_length, Headers, false) of +- false when (Body == []) or +- (Body == <<>>) or +- is_tuple(Body) or +- is_function(Body) -> +- Headers; +- false when is_binary(Body) -> +- [{"content-length", integer_to_list(size(Body))} | Headers]; +- false -> +- [{"content-length", integer_to_list(length(Body))} | Headers]; +- _ -> +- Headers +- end, +- {Headers_2, Body_1} = +- case get_value(transfer_encoding, Options, false) of +- false -> +- {Headers_1, Body}; +- {chunked, ChunkSize} -> +- {[{X, Y} || {X, Y} <- Headers_1, +- X /= "Content-Length", +- X /= "content-length", +- X /= content_length] ++ +- [{"Transfer-Encoding", "chunked"}], +- chunk_request_body(Body, ChunkSize)} +- end, +- Headers_3 = cons_headers(Headers_2), +- Uri = case get_value(use_absolute_uri, Options, false) or UseProxy of +- true -> +- AbsPath; +- false -> +- RelPath +- end, +- {[method(Method), " ", Uri, " ", HttpVsn, crnl(), Headers_3, crnl()], Body_1}. +- +-http_vsn_string({0,9}) -> "HTTP/0.9"; +-http_vsn_string({1,0}) -> "HTTP/1.0"; +-http_vsn_string({1,1}) -> "HTTP/1.1". +- +-cons_headers(Headers) -> +- cons_headers(Headers, []). +-cons_headers([], Acc) -> +- encode_headers(Acc); +-cons_headers([{basic_auth, {U,P}} | T], Acc) -> +- cons_headers(T, [{"Authorization", +- ["Basic ", ibrowse_lib:encode_base64(U++":"++P)]} | Acc]); +-cons_headers([{cookie, Cookie} | T], Acc) -> +- cons_headers(T, [{"Cookie", Cookie} | Acc]); +-cons_headers([{content_length, L} | T], Acc) -> +- cons_headers(T, [{"Content-Length", L} | Acc]); +-cons_headers([{content_type, L} | T], Acc) -> +- cons_headers(T, [{"Content-Type", L} | Acc]); +-cons_headers([H | T], Acc) -> +- cons_headers(T, [H | Acc]). +- +-encode_headers(L) -> +- encode_headers(L, []). +-encode_headers([{http_vsn, _Val} | T], Acc) -> +- encode_headers(T, Acc); +-encode_headers([{Name,Val} | T], Acc) when is_list(Name) -> +- encode_headers(T, [[Name, ": ", fmt_val(Val), crnl()] | Acc]); +-encode_headers([{Name,Val} | T], Acc) when is_atom(Name) -> +- encode_headers(T, [[atom_to_list(Name), ": ", fmt_val(Val), crnl()] | Acc]); +-encode_headers([], Acc) -> +- lists:reverse(Acc). +- +-chunk_request_body(Body, ChunkSize) -> +- chunk_request_body(Body, ChunkSize, []). +- +-chunk_request_body(Body, _ChunkSize, Acc) when Body == <<>>; Body == [] -> +- LastChunk = "0\r\n", +- lists:reverse(["\r\n", LastChunk | Acc]); +-chunk_request_body(Body, ChunkSize, Acc) when is_binary(Body), +- size(Body) >= ChunkSize -> +- <> = Body, +- Chunk = [ibrowse_lib:dec2hex(4, ChunkSize),"\r\n", +- ChunkBody, "\r\n"], +- chunk_request_body(Rest, ChunkSize, [Chunk | Acc]); +-chunk_request_body(Body, _ChunkSize, Acc) when is_binary(Body) -> +- BodySize = size(Body), +- Chunk = [ibrowse_lib:dec2hex(4, BodySize),"\r\n", +- Body, "\r\n"], +- LastChunk = "0\r\n", +- lists:reverse(["\r\n", LastChunk, Chunk | Acc]); +-chunk_request_body(Body, ChunkSize, Acc) when is_list(Body), +- length(Body) >= ChunkSize -> +- {ChunkBody, Rest} = split_list_at(Body, ChunkSize), +- Chunk = [ibrowse_lib:dec2hex(4, ChunkSize),"\r\n", +- ChunkBody, "\r\n"], +- chunk_request_body(Rest, ChunkSize, [Chunk | Acc]); +-chunk_request_body(Body, _ChunkSize, Acc) when is_list(Body) -> +- BodySize = length(Body), +- Chunk = [ibrowse_lib:dec2hex(4, BodySize),"\r\n", +- Body, "\r\n"], +- LastChunk = "0\r\n", +- lists:reverse(["\r\n", LastChunk, Chunk | Acc]). +- +- +-parse_response(_Data, #state{cur_req = undefined}=State) -> +- State#state{status = idle}; +-parse_response(Data, #state{reply_buffer = Acc, reqs = Reqs, +- cur_req = CurReq} = State) -> +- #request{from=From, stream_to=StreamTo, req_id=ReqId, +- method=Method, response_format = Resp_format} = CurReq, +- MaxHeaderSize = ibrowse:get_config_value(max_headers_size, infinity), +- case scan_header(Acc, Data) of +- {yes, Headers, Data_1} -> +- do_trace("Recvd Header Data -> ~s~n----~n", [Headers]), +- do_trace("Recvd headers~n--- Headers Begin ---~n~s~n--- Headers End ---~n~n", [Headers]), +- {HttpVsn, StatCode, Headers_1} = parse_headers(Headers), +- do_trace("HttpVsn: ~p StatusCode: ~p Headers_1 -> ~1000.p~n", [HttpVsn, StatCode, Headers_1]), +- LCHeaders = [{to_lower(X), Y} || {X,Y} <- Headers_1], +- ConnClose = to_lower(get_value("connection", LCHeaders, "false")), +- IsClosing = is_connection_closing(HttpVsn, ConnClose), +- case IsClosing of +- true -> +- shutting_down(State); +- false -> +- ok +- end, +- State_1 = State#state{recvd_headers=Headers_1, status=get_body, +- reply_buffer = <<>>, +- http_status_code=StatCode, is_closing=IsClosing}, +- put(conn_close, ConnClose), +- TransferEncoding = to_lower(get_value("transfer-encoding", LCHeaders, "false")), +- case get_value("content-length", LCHeaders, undefined) of +- _ when Method == head -> +- {_, Reqs_1} = queue:out(Reqs), +- send_async_headers(ReqId, StreamTo, StatCode, Headers_1), +- State_1_1 = do_reply(State_1, From, StreamTo, ReqId, Resp_format, +- {ok, StatCode, Headers_1, []}), +- cancel_timer(State_1_1#state.send_timer, {eat_message, {req_timedout, From}}), +- State_2 = reset_state(State_1_1), +- State_3 = set_cur_request(State_2#state{reqs = Reqs_1}), +- parse_response(Data_1, State_3); +- _ when hd(StatCode) == $1 -> +- %% No message body is expected. Server may send +- %% one or more 1XX responses before a proper +- %% response. +- send_async_headers(ReqId, StreamTo, StatCode, Headers_1), +- do_trace("Recvd a status code of ~p. Ignoring and waiting for a proper response~n", [StatCode]), +- parse_response(Data_1, State_1#state{recvd_headers = [], +- status = get_header}); +- _ when StatCode == "204"; +- StatCode == "304" -> +- %% No message body is expected for these Status Codes. +- %% RFC2616 - Sec 4.4 +- {_, Reqs_1} = queue:out(Reqs), +- send_async_headers(ReqId, StreamTo, StatCode, Headers_1), +- State_1_1 = do_reply(State_1, From, StreamTo, ReqId, Resp_format, +- {ok, StatCode, Headers_1, []}), +- cancel_timer(State_1_1#state.send_timer, {eat_message, {req_timedout, From}}), +- State_2 = reset_state(State_1_1), +- State_3 = set_cur_request(State_2#state{reqs = Reqs_1}), +- parse_response(Data_1, State_3); +- _ when TransferEncoding == "chunked" -> +- do_trace("Chunked encoding detected...~n",[]), +- send_async_headers(ReqId, StreamTo, StatCode, Headers_1), +- case parse_11_response(Data_1, State_1#state{transfer_encoding=chunked, +- chunk_size=chunk_start, +- reply_buffer = <<>>}) of +- {error, Reason} -> +- fail_pipelined_requests(State_1, +- {error, {Reason, +- {stat_code, StatCode}, Headers_1}}), +- {error, Reason}; +- State_2 -> +- State_2 +- end; +- undefined when HttpVsn == "HTTP/1.0"; +- ConnClose == "close" -> +- send_async_headers(ReqId, StreamTo, StatCode, Headers_1), +- State_1#state{reply_buffer = Data_1}; +- undefined -> +- fail_pipelined_requests(State_1, +- {error, {content_length_undefined, +- {stat_code, StatCode}, Headers}}), +- {error, content_length_undefined}; +- V -> +- case catch list_to_integer(V) of +- V_1 when is_integer(V_1), V_1 >= 0 -> +- send_async_headers(ReqId, StreamTo, StatCode, Headers_1), +- do_trace("Recvd Content-Length of ~p~n", [V_1]), +- State_2 = State_1#state{rep_buf_size=0, +- reply_buffer = <<>>, +- content_length=V_1}, +- case parse_11_response(Data_1, State_2) of +- {error, Reason} -> +- fail_pipelined_requests(State_1, +- {error, {Reason, +- {stat_code, StatCode}, Headers_1}}), +- {error, Reason}; +- State_3 -> +- State_3 +- end; +- _ -> +- fail_pipelined_requests(State_1, +- {error, {content_length_undefined, +- {stat_code, StatCode}, Headers}}), +- {error, content_length_undefined} +- end +- end; +- {no, Acc_1} when MaxHeaderSize == infinity -> +- State#state{reply_buffer = Acc_1}; +- {no, Acc_1} when size(Acc_1) < MaxHeaderSize -> +- State#state{reply_buffer = Acc_1}; +- {no, _Acc_1} -> +- fail_pipelined_requests(State, {error, max_headers_size_exceeded}), +- {error, max_headers_size_exceeded} +- end. +- +-is_connection_closing("HTTP/0.9", _) -> true; +-is_connection_closing(_, "close") -> true; +-is_connection_closing("HTTP/1.0", "false") -> true; +-is_connection_closing(_, _) -> false. +- +-%% This clause determines the chunk size when given data from the beginning of the chunk +-parse_11_response(DataRecvd, +- #state{transfer_encoding = chunked, +- chunk_size = chunk_start, +- chunk_size_buffer = Chunk_sz_buf +- } = State) -> +- case scan_crlf(Chunk_sz_buf, DataRecvd) of +- {yes, ChunkHeader, Data_1} -> +- case parse_chunk_header(ChunkHeader) of +- {error, Reason} -> +- {error, Reason}; +- ChunkSize -> +- %% +- %% Do we have to preserve the chunk encoding when +- %% streaming? NO. This should be transparent to the client +- %% process. Chunked encoding was only introduced to make +- %% it efficient for the server. +- %% +- RemLen = size(Data_1), +- do_trace("Determined chunk size: ~p. Already recvd: ~p~n", [ChunkSize, RemLen]), +- parse_11_response(Data_1, State#state{chunk_size_buffer = <<>>, +- deleted_crlf = true, +- recvd_chunk_size = 0, +- chunk_size = ChunkSize}) +- end; +- {no, Data_1} -> +- State#state{chunk_size_buffer = Data_1} +- end; +- +-%% This clause is to remove the CRLF between two chunks +-%% +-parse_11_response(DataRecvd, +- #state{transfer_encoding = chunked, +- chunk_size = tbd, +- chunk_size_buffer = Buf}=State) -> +- case scan_crlf(Buf, DataRecvd) of +- {yes, _, NextChunk} -> +- State_1 = State#state{chunk_size = chunk_start, +- chunk_size_buffer = <<>>, +- deleted_crlf = true}, +- parse_11_response(NextChunk, State_1); +- {no, Data_1} -> +- State#state{chunk_size_buffer = Data_1} +- end; +- +-%% This clause deals with the end of a chunked transfer. ibrowse does +-%% not support Trailers in the Chunked Transfer encoding. Any trailer +-%% received is silently discarded. +-parse_11_response(DataRecvd, +- #state{transfer_encoding = chunked, chunk_size = 0, +- cur_req = CurReq, +- deleted_crlf = DelCrlf, +- chunk_size_buffer = Trailer, reqs = Reqs}=State) -> +- do_trace("Detected end of chunked transfer...~n", []), +- DataRecvd_1 = case DelCrlf of +- false -> +- DataRecvd; +- true -> +- <<$\r, $\n, DataRecvd/binary>> +- end, +- case scan_header(Trailer, DataRecvd_1) of +- {yes, _TEHeaders, Rem} -> +- {_, Reqs_1} = queue:out(Reqs), +- State_1 = handle_response(CurReq, State#state{reqs = Reqs_1}), +- parse_response(Rem, reset_state(State_1)); +- {no, Rem} -> +- State#state{chunk_size_buffer = Rem, deleted_crlf = false} +- end; +- +-%% This clause extracts a chunk, given the size. +-parse_11_response(DataRecvd, +- #state{transfer_encoding = chunked, +- chunk_size = CSz, +- recvd_chunk_size = Recvd_csz, +- rep_buf_size = RepBufSz} = State) -> +- NeedBytes = CSz - Recvd_csz, +- DataLen = size(DataRecvd), +- do_trace("Recvd more data: size: ~p. NeedBytes: ~p~n", [DataLen, NeedBytes]), +- case DataLen >= NeedBytes of +- true -> +- {RemChunk, RemData} = split_binary(DataRecvd, NeedBytes), +- do_trace("Recvd another chunk...~n", []), +- do_trace("RemData -> ~p~n", [RemData]), +- case accumulate_response(RemChunk, State) of +- {error, Reason} -> +- do_trace("Error accumulating response --> ~p~n", [Reason]), +- {error, Reason}; +- #state{} = State_1 -> +- State_2 = State_1#state{chunk_size=tbd}, +- parse_11_response(RemData, State_2) +- end; +- false -> +- accumulate_response(DataRecvd, +- State#state{rep_buf_size = RepBufSz + DataLen, +- recvd_chunk_size = Recvd_csz + DataLen}) +- end; +- +-%% This clause to extract the body when Content-Length is specified +-parse_11_response(DataRecvd, +- #state{content_length=CL, rep_buf_size=RepBufSz, +- reqs=Reqs}=State) -> +- NeedBytes = CL - RepBufSz, +- DataLen = size(DataRecvd), +- case DataLen >= NeedBytes of +- true -> +- {RemBody, Rem} = split_binary(DataRecvd, NeedBytes), +- {_, Reqs_1} = queue:out(Reqs), +- State_1 = accumulate_response(RemBody, State), +- State_2 = handle_response(State_1#state.cur_req, State_1#state{reqs=Reqs_1}), +- State_3 = reset_state(State_2), +- parse_response(Rem, State_3); +- false -> +- accumulate_response(DataRecvd, State#state{rep_buf_size = (RepBufSz+DataLen)}) +- end. +- +-handle_response(#request{from=From, stream_to=StreamTo, req_id=ReqId, +- response_format = Resp_format, +- save_response_to_file = SaveResponseToFile, +- tmp_file_name = TmpFilename, +- tmp_file_fd = Fd +- }, +- #state{http_status_code = SCode, +- send_timer = ReqTimer, +- reply_buffer = RepBuf, +- recvd_headers = RespHeaders}=State) when SaveResponseToFile /= false -> +- Body = RepBuf, +- State_1 = set_cur_request(State), +- file:close(Fd), +- ResponseBody = case TmpFilename of +- undefined -> +- Body; +- _ -> +- {file, TmpFilename} +- end, +- State_2 = do_reply(State_1, From, StreamTo, ReqId, Resp_format, +- {ok, SCode, RespHeaders, ResponseBody}), +- cancel_timer(ReqTimer, {eat_message, {req_timedout, From}}), +- State_2; +-handle_response(#request{from=From, stream_to=StreamTo, req_id=ReqId, +- response_format = Resp_format}, +- #state{http_status_code=SCode, recvd_headers=RespHeaders, +- reply_buffer = RepBuf, +- send_timer=ReqTimer}=State) -> +- Body = RepBuf, +-%% State_1 = set_cur_request(State), +- State_1 = case get(conn_close) of +- "close" -> +- do_reply(State, From, StreamTo, ReqId, Resp_format, +- {ok, SCode, RespHeaders, Body}), +- exit(normal); +- _ -> +- State_1_1 = do_reply(State, From, StreamTo, ReqId, Resp_format, +- {ok, SCode, RespHeaders, Body}), +- cancel_timer(ReqTimer, {eat_message, {req_timedout, From}}), +- State_1_1 +- end, +- set_cur_request(State_1). +- +-reset_state(State) -> +- State#state{status = get_header, +- rep_buf_size = 0, +- streamed_size = 0, +- content_length = undefined, +- reply_buffer = <<>>, +- chunk_size_buffer = <<>>, +- recvd_headers = [], +- deleted_crlf = false, +- http_status_code = undefined, +- chunk_size = undefined, +- transfer_encoding = undefined}. +- +-set_cur_request(#state{reqs = Reqs} = State) -> +- case queue:to_list(Reqs) of +- [] -> +- State#state{cur_req = undefined}; +- [NextReq | _] -> +- State#state{cur_req = NextReq} +- end. +- +-parse_headers(Headers) -> +- case scan_crlf(Headers) of +- {yes, StatusLine, T} -> +- parse_headers(StatusLine, T); +- {no, StatusLine} -> +- parse_headers(StatusLine, <<>>) +- end. +- +-parse_headers(StatusLine, Headers) -> +- Headers_1 = parse_headers_1(Headers), +- case parse_status_line(StatusLine) of +- {ok, HttpVsn, StatCode, _Msg} -> +- put(http_prot_vsn, HttpVsn), +- {HttpVsn, StatCode, Headers_1}; +- _ -> %% A HTTP 0.9 response? +- put(http_prot_vsn, "HTTP/0.9"), +- {"HTTP/0.9", undefined, Headers} +- end. +- +-% From RFC 2616 +-% +-% HTTP/1.1 header field values can be folded onto multiple lines if +-% the continuation line begins with a space or horizontal tab. All +-% linear white space, including folding, has the same semantics as +-% SP. A recipient MAY replace any linear white space with a single +-% SP before interpreting the field value or forwarding the message +-% downstream. +- parse_headers_1(B) when is_binary(B) -> +- parse_headers_1(binary_to_list(B)); +- parse_headers_1(String) -> +- parse_headers_1(String, [], []). +- +-parse_headers_1([$\n, H |T], [$\r | L], Acc) when H == 32; +- H == $\t -> +- parse_headers_1(lists:dropwhile(fun(X) -> +- is_whitespace(X) +- end, T), [32 | L], Acc); +-parse_headers_1([$\n|T], [$\r | L], Acc) -> +- case parse_header(lists:reverse(L)) of +- invalid -> +- parse_headers_1(T, [], Acc); +- NewHeader -> +- parse_headers_1(T, [], [NewHeader | Acc]) +- end; +-parse_headers_1([H|T], L, Acc) -> +- parse_headers_1(T, [H|L], Acc); +-parse_headers_1([], [], Acc) -> +- lists:reverse(Acc); +-parse_headers_1([], L, Acc) -> +- Acc_1 = case parse_header(lists:reverse(L)) of +- invalid -> +- Acc; +- NewHeader -> +- [NewHeader | Acc] +- end, +- lists:reverse(Acc_1). +- +-parse_status_line(Line) when is_binary(Line) -> +- parse_status_line(binary_to_list(Line)); +-parse_status_line(Line) -> +- parse_status_line(Line, get_prot_vsn, [], []). +-parse_status_line([32 | T], get_prot_vsn, ProtVsn, StatCode) -> +- parse_status_line(T, get_status_code, ProtVsn, StatCode); +-parse_status_line([32 | T], get_status_code, ProtVsn, StatCode) -> +- {ok, lists:reverse(ProtVsn), lists:reverse(StatCode), T}; +-parse_status_line([H | T], get_prot_vsn, ProtVsn, StatCode) -> +- parse_status_line(T, get_prot_vsn, [H|ProtVsn], StatCode); +-parse_status_line([H | T], get_status_code, ProtVsn, StatCode) -> +- parse_status_line(T, get_status_code, ProtVsn, [H | StatCode]); +-parse_status_line([], _, _, _) -> +- http_09. +- +-parse_header(B) when is_binary(B) -> +- parse_header(binary_to_list(B)); +-parse_header(L) -> +- parse_header(L, []). +-parse_header([$: | V], Acc) -> +- {lists:reverse(Acc), string:strip(V)}; +-parse_header([H | T], Acc) -> +- parse_header(T, [H | Acc]); +-parse_header([], _) -> +- invalid. +- +-scan_header(Bin) -> +- case get_crlf_crlf_pos(Bin, 0) of +- {yes, Pos} -> +- {Headers, <<_:4/binary, Body/binary>>} = split_binary(Bin, Pos), +- {yes, Headers, Body}; +- no -> +- {no, Bin} +- end. +- +-scan_header(Bin1, Bin2) when size(Bin1) < 4 -> +- scan_header(<>); +-scan_header(Bin1, <<>>) -> +- scan_header(Bin1); +-scan_header(Bin1, Bin2) -> +- Bin1_already_scanned_size = size(Bin1) - 4, +- <> = Bin1, +- Bin_to_scan = <>, +- case get_crlf_crlf_pos(Bin_to_scan, 0) of +- {yes, Pos} -> +- {Headers_suffix, <<_:4/binary, Body/binary>>} = split_binary(Bin_to_scan, Pos), +- {yes, <>, Body}; +- no -> +- {no, <>} +- end. +- +-get_crlf_crlf_pos(<<$\r, $\n, $\r, $\n, _/binary>>, Pos) -> {yes, Pos}; +-get_crlf_crlf_pos(<<_, Rest/binary>>, Pos) -> get_crlf_crlf_pos(Rest, Pos + 1); +-get_crlf_crlf_pos(<<>>, _) -> no. +- +-scan_crlf(Bin) -> +- case get_crlf_pos(Bin) of +- {yes, Pos} -> +- {Prefix, <<_, _, Suffix/binary>>} = split_binary(Bin, Pos), +- {yes, Prefix, Suffix}; +- no -> +- {no, Bin} +- end. +- +-scan_crlf(<<>>, Bin2) -> +- scan_crlf(Bin2); +-scan_crlf(Bin1, Bin2) when size(Bin1) < 2 -> +- scan_crlf(<>); +-scan_crlf(Bin1, Bin2) -> +- scan_crlf_1(size(Bin1) - 2, Bin1, Bin2). +- +-scan_crlf_1(Bin1_head_size, Bin1, Bin2) -> +- <> = Bin1, +- Bin3 = <>, +- case get_crlf_pos(Bin3) of +- {yes, Pos} -> +- {Prefix, <<_, _, Suffix/binary>>} = split_binary(Bin3, Pos), +- {yes, list_to_binary([Bin1_head, Prefix]), Suffix}; +- no -> +- {no, list_to_binary([Bin1, Bin2])} +- end. +- +-get_crlf_pos(Bin) -> +- get_crlf_pos(Bin, 0). +- +-get_crlf_pos(<<$\r, $\n, _/binary>>, Pos) -> {yes, Pos}; +-get_crlf_pos(<<_, Rest/binary>>, Pos) -> get_crlf_pos(Rest, Pos + 1); +-get_crlf_pos(<<>>, _) -> no. +- +-%% scan_crlf(<<$\n, T/binary>>, [$\r | L]) -> {yes, lists:reverse(L), T}; +-%% scan_crlf(<>, L) -> scan_crlf(T, [H|L]); +-%% scan_crlf(<<>>, L) -> {no, L}; +-%% scan_crlf([$\n|T], [$\r | L]) -> {yes, lists:reverse(L), T}; +-%% scan_crlf([H|T], L) -> scan_crlf(T, [H|L]); +-%% scan_crlf([], L) -> {no, L}. +- +-fmt_val(L) when is_list(L) -> L; +-fmt_val(I) when is_integer(I) -> integer_to_list(I); +-fmt_val(A) when is_atom(A) -> atom_to_list(A); +-fmt_val(Term) -> io_lib:format("~p", [Term]). +- +-crnl() -> "\r\n". +- +-method(get) -> "GET"; +-method(post) -> "POST"; +-method(head) -> "HEAD"; +-method(options) -> "OPTIONS"; +-method(put) -> "PUT"; +-method(delete) -> "DELETE"; +-method(trace) -> "TRACE"; +-method(mkcol) -> "MKCOL"; +-method(propfind) -> "PROPFIND"; +-method(proppatch) -> "PROPPATCH"; +-method(lock) -> "LOCK"; +-method(unlock) -> "UNLOCK"; +-method(move) -> "MOVE"; +-method(copy) -> "COPY". +- +-%% From RFC 2616 +-%% +-% The chunked encoding modifies the body of a message in order to +-% transfer it as a series of chunks, each with its own size indicator, +-% followed by an OPTIONAL trailer containing entity-header +-% fields. This allows dynamically produced content to be transferred +-% along with the information necessary for the recipient to verify +-% that it has received the full message. +-% Chunked-Body = *chunk +-% last-chunk +-% trailer +-% CRLF +-% chunk = chunk-size [ chunk-extension ] CRLF +-% chunk-data CRLF +-% chunk-size = 1*HEX +-% last-chunk = 1*("0") [ chunk-extension ] CRLF +-% chunk-extension= *( ";" chunk-ext-name [ "=" chunk-ext-val ] ) +-% chunk-ext-name = token +-% chunk-ext-val = token | quoted-string +-% chunk-data = chunk-size(OCTET) +-% trailer = *(entity-header CRLF) +-% The chunk-size field is a string of hex digits indicating the size +-% of the chunk. The chunked encoding is ended by any chunk whose size +-% is zero, followed by the trailer, which is terminated by an empty +-% line. +-%% +-%% The parsing implemented here discards all chunk extensions. It also +-%% strips trailing spaces from the chunk size fields as Apache 1.3.27 was +-%% sending them. +-parse_chunk_header([]) -> +- throw({error, invalid_chunk_size}); +-parse_chunk_header(ChunkHeader) -> +- parse_chunk_header(ChunkHeader, []). +- +-parse_chunk_header(<<$;, _/binary>>, Acc) -> +- hexlist_to_integer(lists:reverse(Acc)); +-parse_chunk_header(<>, Acc) -> +- case is_whitespace(H) of +- true -> +- parse_chunk_header(T, Acc); +- false -> +- parse_chunk_header(T, [H | Acc]) +- end; +-parse_chunk_header(<<>>, Acc) -> +- hexlist_to_integer(lists:reverse(Acc)). +- +-is_whitespace($\s) -> true; +-is_whitespace($\r) -> true; +-is_whitespace($\n) -> true; +-is_whitespace($\t) -> true; +-is_whitespace(_) -> false. +- +- +-send_async_headers(_ReqId, undefined, _StatCode, _Headers) -> +- ok; +-send_async_headers(ReqId, StreamTo, StatCode, Headers) -> +- catch StreamTo ! {ibrowse_async_headers, ReqId, StatCode, Headers}. +- +-format_response_data(Resp_format, Body) -> +- case Resp_format of +- list when is_list(Body) -> +- flatten(Body); +- list when is_binary(Body) -> +- binary_to_list(Body); +- binary when is_list(Body) -> +- list_to_binary(Body); +- _ -> +- %% This is to cater for sending messages such as +- %% {chunk_start, _}, chunk_end etc +- Body +- end. +- +-do_reply(State, From, undefined, _, Resp_format, {ok, St_code, Headers, Body}) -> +- Msg_1 = {ok, St_code, Headers, format_response_data(Resp_format, Body)}, +- gen_server:reply(From, Msg_1), +- dec_pipeline_counter(State); +-do_reply(State, From, undefined, _, _, Msg) -> +- gen_server:reply(From, Msg), +- dec_pipeline_counter(State); +-do_reply(#state{prev_req_id = Prev_req_id} = State, +- _From, StreamTo, ReqId, Resp_format, {ok, _, _, Body}) -> +- State_1 = dec_pipeline_counter(State), +- case Body of +- [] -> +- ok; +- _ -> +- Body_1 = format_response_data(Resp_format, Body), +- catch StreamTo ! {ibrowse_async_response, ReqId, Body_1} +- end, +- catch StreamTo ! {ibrowse_async_response_end, ReqId}, +- %% We don't want to delete the Req-id to Pid mapping straightaway +- %% as the client may send a stream_next message just while we are +- %% sending back this ibrowse_async_response_end message. If we +- %% deleted this mapping straightaway, the caller will see a +- %% {error, unknown_req_id} when it calls ibrowse:stream_next/1. To +- %% get around this, we store the req id, and clear it after the +- %% next request. If there are wierd combinations of stream, +- %% stream_once and sync requests on the same connection, it will +- %% take a while for the req_id-pid mapping to get cleared, but it +- %% should do no harm. +- ets:delete(ibrowse_stream, {req_id_pid, Prev_req_id}), +- State_1#state{prev_req_id = ReqId}; +-do_reply(State, _From, StreamTo, ReqId, Resp_format, Msg) -> +- State_1 = dec_pipeline_counter(State), +- Msg_1 = format_response_data(Resp_format, Msg), +- catch StreamTo ! {ibrowse_async_response, ReqId, Msg_1}, +- State_1. +- +-do_interim_reply(undefined, _, _ReqId, _Msg) -> +- ok; +-do_interim_reply(StreamTo, Response_format, ReqId, Msg) -> +- Msg_1 = format_response_data(Response_format, Msg), +- catch StreamTo ! {ibrowse_async_response, ReqId, Msg_1}. +- +-do_error_reply(#state{reqs = Reqs} = State, Err) -> +- ReqList = queue:to_list(Reqs), +- lists:foreach(fun(#request{from=From, stream_to=StreamTo, req_id=ReqId, +- response_format = Resp_format}) -> +- ets:delete(ibrowse_stream, {req_id_pid, ReqId}), +- do_reply(State, From, StreamTo, ReqId, Resp_format, {error, Err}) +- end, ReqList). +- +-fail_pipelined_requests(#state{reqs = Reqs, cur_req = CurReq} = State, Reply) -> +- {_, Reqs_1} = queue:out(Reqs), +- #request{from=From, stream_to=StreamTo, req_id=ReqId, +- response_format = Resp_format} = CurReq, +- do_reply(State, From, StreamTo, ReqId, Resp_format, Reply), +- do_error_reply(State#state{reqs = Reqs_1}, previous_request_failed). +- +-split_list_at(List, N) -> +- split_list_at(List, N, []). +-split_list_at([], _, Acc) -> +- {lists:reverse(Acc), []}; +-split_list_at(List2, 0, List1) -> +- {lists:reverse(List1), List2}; +-split_list_at([H | List2], N, List1) -> +- split_list_at(List2, N-1, [H | List1]). +- +-hexlist_to_integer(List) -> +- hexlist_to_integer(lists:reverse(List), 1, 0). +-hexlist_to_integer([H | T], Multiplier, Acc) -> +- hexlist_to_integer(T, Multiplier*16, Multiplier*to_ascii(H) + Acc); +-hexlist_to_integer([], _, Acc) -> +- Acc. +- +-to_ascii($A) -> 10; +-to_ascii($a) -> 10; +-to_ascii($B) -> 11; +-to_ascii($b) -> 11; +-to_ascii($C) -> 12; +-to_ascii($c) -> 12; +-to_ascii($D) -> 13; +-to_ascii($d) -> 13; +-to_ascii($E) -> 14; +-to_ascii($e) -> 14; +-to_ascii($F) -> 15; +-to_ascii($f) -> 15; +-to_ascii($1) -> 1; +-to_ascii($2) -> 2; +-to_ascii($3) -> 3; +-to_ascii($4) -> 4; +-to_ascii($5) -> 5; +-to_ascii($6) -> 6; +-to_ascii($7) -> 7; +-to_ascii($8) -> 8; +-to_ascii($9) -> 9; +-to_ascii($0) -> 0. +- +-cancel_timer(undefined) -> ok; +-cancel_timer(Ref) -> erlang:cancel_timer(Ref). +- +-cancel_timer(Ref, {eat_message, Msg}) -> +- cancel_timer(Ref), +- receive +- Msg -> +- ok +- after 0 -> +- ok +- end. +- +-make_req_id() -> +- now(). +- +-to_lower(Str) -> +- to_lower(Str, []). +-to_lower([H|T], Acc) when H >= $A, H =< $Z -> +- to_lower(T, [H+32|Acc]); +-to_lower([H|T], Acc) -> +- to_lower(T, [H|Acc]); +-to_lower([], Acc) -> +- lists:reverse(Acc). +- +-shutting_down(#state{lb_ets_tid = undefined}) -> +- ok; +-shutting_down(#state{lb_ets_tid = Tid, +- cur_pipeline_size = Sz}) -> +- catch ets:delete(Tid, {Sz, self()}). +- +-inc_pipeline_counter(#state{is_closing = true} = State) -> +- State; +-inc_pipeline_counter(#state{cur_pipeline_size = Pipe_sz} = State) -> +- State#state{cur_pipeline_size = Pipe_sz + 1}. +- +-dec_pipeline_counter(#state{is_closing = true} = State) -> +- State; +-dec_pipeline_counter(#state{lb_ets_tid = undefined} = State) -> +- State; +-dec_pipeline_counter(#state{cur_pipeline_size = Pipe_sz, +- lb_ets_tid = Tid} = State) -> +- ets:delete(Tid, {Pipe_sz, self()}), +- ets:insert(Tid, {{Pipe_sz - 1, self()}, []}), +- State#state{cur_pipeline_size = Pipe_sz - 1}. +- +-flatten([H | _] = L) when is_integer(H) -> +- L; +-flatten([H | _] = L) when is_list(H) -> +- lists:flatten(L); +-flatten([]) -> +- []. +- +-get_stream_chunk_size(Options) -> +- case lists:keysearch(stream_chunk_size, 1, Options) of +- {value, {_, V}} when V > 0 -> +- V; +- _ -> +- ?DEFAULT_STREAM_CHUNK_SIZE +- end. +- +-get_inac_timeout(#state{cur_req = #request{options = Opts}}) -> +- get_value(inactivity_timeout, Opts, infinity); +-get_inac_timeout(#state{cur_req = undefined}) -> +- infinity. +diff --git a/src/ibrowse/ibrowse_lb.erl b/src/ibrowse/ibrowse_lb.erl +deleted file mode 100644 +index 834054a..0000000 +--- a/src/ibrowse/ibrowse_lb.erl ++++ /dev/null +@@ -1,216 +0,0 @@ +-%%%------------------------------------------------------------------- +-%%% File : ibrowse_lb.erl +-%%% Author : chandru +-%%% Description : +-%%% +-%%% Created : 6 Mar 2008 by chandru +-%%%------------------------------------------------------------------- +--module(ibrowse_lb). +- +--vsn('$Id: ibrowse_lb.erl,v 1.2 2009/07/01 22:43:19 chandrusf Exp $ '). +--author(chandru). +--behaviour(gen_server). +-%%-------------------------------------------------------------------- +-%% Include files +-%%-------------------------------------------------------------------- +- +-%%-------------------------------------------------------------------- +-%% External exports +--export([ +- start_link/1, +- spawn_connection/5 +- ]). +- +-%% gen_server callbacks +--export([ +- init/1, +- handle_call/3, +- handle_cast/2, +- handle_info/2, +- terminate/2, +- code_change/3 +- ]). +- +--record(state, {parent_pid, +- ets_tid, +- host, +- port, +- max_sessions, +- max_pipeline_size, +- num_cur_sessions = 0}). +- +--include("ibrowse.hrl"). +- +-%%==================================================================== +-%% External functions +-%%==================================================================== +-%%-------------------------------------------------------------------- +-%% Function: start_link/0 +-%% Description: Starts the server +-%%-------------------------------------------------------------------- +-start_link(Args) -> +- gen_server:start_link(?MODULE, Args, []). +- +-%%==================================================================== +-%% Server functions +-%%==================================================================== +- +-%%-------------------------------------------------------------------- +-%% Function: init/1 +-%% Description: Initiates the server +-%% Returns: {ok, State} | +-%% {ok, State, Timeout} | +-%% ignore | +-%% {stop, Reason} +-%%-------------------------------------------------------------------- +-init([Host, Port]) -> +- process_flag(trap_exit, true), +- Max_sessions = ibrowse:get_config_value({max_sessions, Host, Port}, 10), +- Max_pipe_sz = ibrowse:get_config_value({max_pipeline_size, Host, Port}, 10), +- put(my_trace_flag, ibrowse_lib:get_trace_status(Host, Port)), +- put(ibrowse_trace_token, ["LB: ", Host, $:, integer_to_list(Port)]), +- Tid = ets:new(ibrowse_lb, [public, ordered_set]), +- {ok, #state{parent_pid = whereis(ibrowse), +- host = Host, +- port = Port, +- ets_tid = Tid, +- max_pipeline_size = Max_pipe_sz, +- max_sessions = Max_sessions}}. +- +-spawn_connection(Lb_pid, Url, +- Max_sessions, +- Max_pipeline_size, +- SSL_options) +- when is_pid(Lb_pid), +- is_record(Url, url), +- is_integer(Max_pipeline_size), +- is_integer(Max_sessions) -> +- gen_server:call(Lb_pid, +- {spawn_connection, Url, Max_sessions, Max_pipeline_size, SSL_options}). +-%%-------------------------------------------------------------------- +-%% Function: handle_call/3 +-%% Description: Handling call messages +-%% Returns: {reply, Reply, State} | +-%% {reply, Reply, State, Timeout} | +-%% {noreply, State} | +-%% {noreply, State, Timeout} | +-%% {stop, Reason, Reply, State} | (terminate/2 is called) +-%% {stop, Reason, State} (terminate/2 is called) +-%%-------------------------------------------------------------------- +-% handle_call({spawn_connection, _Url, Max_sess, Max_pipe, _}, _From, +-% #state{max_sessions = Max_sess, +-% ets_tid = Tid, +-% max_pipeline_size = Max_pipe_sz, +-% num_cur_sessions = Num} = State) +-% when Num >= Max -> +-% Reply = find_best_connection(Tid), +-% {reply, sorry_dude_reuse, State}; +- +-%% Update max_sessions in #state with supplied value +-handle_call({spawn_connection, _Url, Max_sess, Max_pipe, _}, _From, +- #state{num_cur_sessions = Num} = State) +- when Num >= Max_sess -> +- State_1 = maybe_create_ets(State), +- Reply = find_best_connection(State_1#state.ets_tid, Max_pipe), +- {reply, Reply, State_1#state{max_sessions = Max_sess}}; +- +-handle_call({spawn_connection, Url, _Max_sess, _Max_pipe, SSL_options}, _From, +- #state{num_cur_sessions = Cur} = State) -> +- State_1 = maybe_create_ets(State), +- Tid = State_1#state.ets_tid, +- {ok, Pid} = ibrowse_http_client:start_link({Tid, Url, SSL_options}), +- ets:insert(Tid, {{1, Pid}, []}), +- {reply, {ok, Pid}, State_1#state{num_cur_sessions = Cur + 1}}; +- +-handle_call(Request, _From, State) -> +- Reply = {unknown_request, Request}, +- {reply, Reply, State}. +- +-%%-------------------------------------------------------------------- +-%% Function: handle_cast/2 +-%% Description: Handling cast messages +-%% Returns: {noreply, State} | +-%% {noreply, State, Timeout} | +-%% {stop, Reason, State} (terminate/2 is called) +-%%-------------------------------------------------------------------- +-handle_cast(_Msg, State) -> +- {noreply, State}. +- +-%%-------------------------------------------------------------------- +-%% Function: handle_info/2 +-%% Description: Handling all non call/cast messages +-%% Returns: {noreply, State} | +-%% {noreply, State, Timeout} | +-%% {stop, Reason, State} (terminate/2 is called) +-%%-------------------------------------------------------------------- +-handle_info({'EXIT', Parent, _Reason}, #state{parent_pid = Parent} = State) -> +- {stop, normal, State}; +- +-handle_info({'EXIT', _Pid, _Reason}, #state{ets_tid = undefined} = State) -> +- {noreply, State}; +- +-handle_info({'EXIT', Pid, _Reason}, +- #state{num_cur_sessions = Cur, +- ets_tid = Tid} = State) -> +- ets:match_delete(Tid, {{'_', Pid}, '_'}), +- Cur_1 = Cur - 1, +- State_1 = case Cur_1 of +- 0 -> +- ets:delete(Tid), +- State#state{ets_tid = undefined}; +- _ -> +- State +- end, +- {noreply, State_1#state{num_cur_sessions = Cur_1}}; +- +-handle_info({trace, Bool}, #state{ets_tid = undefined} = State) -> +- put(my_trace_flag, Bool), +- {noreply, State}; +- +-handle_info({trace, Bool}, #state{ets_tid = Tid} = State) -> +- ets:foldl(fun({{_, Pid}, _}, Acc) when is_pid(Pid) -> +- catch Pid ! {trace, Bool}, +- Acc; +- (_, Acc) -> +- Acc +- end, undefined, Tid), +- put(my_trace_flag, Bool), +- {noreply, State}; +- +-handle_info(_Info, State) -> +- {noreply, State}. +- +-%%-------------------------------------------------------------------- +-%% Function: terminate/2 +-%% Description: Shutdown the server +-%% Returns: any (ignored by gen_server) +-%%-------------------------------------------------------------------- +-terminate(_Reason, _State) -> +- ok. +- +-%%-------------------------------------------------------------------- +-%% Func: code_change/3 +-%% Purpose: Convert process state when code is changed +-%% Returns: {ok, NewState} +-%%-------------------------------------------------------------------- +-code_change(_OldVsn, State, _Extra) -> +- {ok, State}. +- +-%%-------------------------------------------------------------------- +-%%% Internal functions +-%%-------------------------------------------------------------------- +-find_best_connection(Tid, Max_pipe) -> +- case ets:first(Tid) of +- {Cur_sz, Pid} when Cur_sz < Max_pipe -> +- ets:delete(Tid, {Cur_sz, Pid}), +- ets:insert(Tid, {{Cur_sz + 1, Pid}, []}), +- {ok, Pid}; +- _ -> +- {error, retry_later} +- end. +- +-maybe_create_ets(#state{ets_tid = undefined} = State) -> +- Tid = ets:new(ibrowse_lb, [public, ordered_set]), +- State#state{ets_tid = Tid}; +-maybe_create_ets(State) -> +- State. +diff --git a/src/ibrowse/ibrowse_lib.erl b/src/ibrowse/ibrowse_lib.erl +deleted file mode 100644 +index 6c7b154..0000000 +--- a/src/ibrowse/ibrowse_lib.erl ++++ /dev/null +@@ -1,399 +0,0 @@ +-%%% File : ibrowse_lib.erl +-%%% Author : Chandrashekhar Mullaparthi +-%%% Description : +-%%% Created : 27 Feb 2004 by Chandrashekhar Mullaparthi +-%% @doc Module with a few useful functions +- +--module(ibrowse_lib). +--vsn('$Id: ibrowse_lib.erl,v 1.6 2008/03/27 01:35:50 chandrusf Exp $ '). +--author('chandru'). +--ifdef(debug). +--compile(export_all). +--endif. +- +--include("ibrowse.hrl"). +- +--export([ +- get_trace_status/2, +- do_trace/2, +- do_trace/3, +- url_encode/1, +- decode_rfc822_date/1, +- status_code/1, +- dec2hex/2, +- drv_ue/1, +- drv_ue/2, +- encode_base64/1, +- decode_base64/1, +- get_value/2, +- get_value/3, +- parse_url/1, +- printable_date/0 +- ]). +- +-get_trace_status(Host, Port) -> +- ibrowse:get_config_value({trace, Host, Port}, false). +- +-drv_ue(Str) -> +- [{port, Port}| _] = ets:lookup(ibrowse_table, port), +- drv_ue(Str, Port). +-drv_ue(Str, Port) -> +- case erlang:port_control(Port, 1, Str) of +- [] -> +- Str; +- Res -> +- Res +- end. +- +-%% @doc URL-encodes a string based on RFC 1738. Returns a flat list. +-%% @spec url_encode(Str) -> UrlEncodedStr +-%% Str = string() +-%% UrlEncodedStr = string() +-url_encode(Str) when is_list(Str) -> +- url_encode_char(lists:reverse(Str), []). +- +-url_encode_char([X | T], Acc) when X >= $0, X =< $9 -> +- url_encode_char(T, [X | Acc]); +-url_encode_char([X | T], Acc) when X >= $a, X =< $z -> +- url_encode_char(T, [X | Acc]); +-url_encode_char([X | T], Acc) when X >= $A, X =< $Z -> +- url_encode_char(T, [X | Acc]); +-url_encode_char([X | T], Acc) when X == $-; X == $_; X == $. -> +- url_encode_char(T, [X | Acc]); +-url_encode_char([32 | T], Acc) -> +- url_encode_char(T, [$+ | Acc]); +-url_encode_char([X | T], Acc) -> +- url_encode_char(T, [$%, d2h(X bsr 4), d2h(X band 16#0f) | Acc]); +-url_encode_char([], Acc) -> +- Acc. +- +-d2h(N) when N<10 -> N+$0; +-d2h(N) -> N+$a-10. +- +-decode_rfc822_date(String) when is_list(String) -> +- case catch decode_rfc822_date_1(string:tokens(String, ", \t\r\n")) of +- {'EXIT', _} -> +- {error, invalid_date}; +- Res -> +- Res +- end. +- +-% TODO: Have to handle the Zone +-decode_rfc822_date_1([_,DayInt,Month,Year, Time,Zone]) -> +- decode_rfc822_date_1([DayInt,Month,Year, Time,Zone]); +-decode_rfc822_date_1([Day,Month,Year, Time,_Zone]) -> +- DayI = list_to_integer(Day), +- MonthI = month_int(Month), +- YearI = list_to_integer(Year), +- TimeTup = case string:tokens(Time, ":") of +- [H,M] -> +- {list_to_integer(H), +- list_to_integer(M), +- 0}; +- [H,M,S] -> +- {list_to_integer(H), +- list_to_integer(M), +- list_to_integer(S)} +- end, +- {{YearI,MonthI,DayI}, TimeTup}. +- +-month_int("Jan") -> 1; +-month_int("Feb") -> 2; +-month_int("Mar") -> 3; +-month_int("Apr") -> 4; +-month_int("May") -> 5; +-month_int("Jun") -> 6; +-month_int("Jul") -> 7; +-month_int("Aug") -> 8; +-month_int("Sep") -> 9; +-month_int("Oct") -> 10; +-month_int("Nov") -> 11; +-month_int("Dec") -> 12. +- +-%% @doc Given a status code, returns an atom describing the status code. +-%% @spec status_code(StatusCode::status_code()) -> StatusDescription +-%% status_code() = string() | integer() +-%% StatusDescription = atom() +-status_code(100) -> continue; +-status_code(101) -> switching_protocols; +-status_code(102) -> processing; +-status_code(200) -> ok; +-status_code(201) -> created; +-status_code(202) -> accepted; +-status_code(203) -> non_authoritative_information; +-status_code(204) -> no_content; +-status_code(205) -> reset_content; +-status_code(206) -> partial_content; +-status_code(207) -> multi_status; +-status_code(300) -> multiple_choices; +-status_code(301) -> moved_permanently; +-status_code(302) -> found; +-status_code(303) -> see_other; +-status_code(304) -> not_modified; +-status_code(305) -> use_proxy; +-status_code(306) -> unused; +-status_code(307) -> temporary_redirect; +-status_code(400) -> bad_request; +-status_code(401) -> unauthorized; +-status_code(402) -> payment_required; +-status_code(403) -> forbidden; +-status_code(404) -> not_found; +-status_code(405) -> method_not_allowed; +-status_code(406) -> not_acceptable; +-status_code(407) -> proxy_authentication_required; +-status_code(408) -> request_timeout; +-status_code(409) -> conflict; +-status_code(410) -> gone; +-status_code(411) -> length_required; +-status_code(412) -> precondition_failed; +-status_code(413) -> request_entity_too_large; +-status_code(414) -> request_uri_too_long; +-status_code(415) -> unsupported_media_type; +-status_code(416) -> requested_range_not_satisfiable; +-status_code(417) -> expectation_failed; +-status_code(422) -> unprocessable_entity; +-status_code(423) -> locked; +-status_code(424) -> failed_dependency; +-status_code(500) -> internal_server_error; +-status_code(501) -> not_implemented; +-status_code(502) -> bad_gateway; +-status_code(503) -> service_unavailable; +-status_code(504) -> gateway_timeout; +-status_code(505) -> http_version_not_supported; +-status_code(507) -> insufficient_storage; +-status_code(X) when is_list(X) -> status_code(list_to_integer(X)); +-status_code(_) -> unknown_status_code. +- +-%% @doc dec2hex taken from gtk.erl in std dist +-%% M = integer() -- number of hex digits required +-%% N = integer() -- the number to represent as hex +-%% @spec dec2hex(M::integer(), N::integer()) -> string() +-dec2hex(M,N) -> dec2hex(M,N,[]). +- +-dec2hex(0,_N,Ack) -> Ack; +-dec2hex(M,N,Ack) -> dec2hex(M-1,N bsr 4,[d2h(N band 15)|Ack]). +- +-%% @doc Implements the base64 encoding algorithm. The output data type matches in the input data type. +-%% @spec encode_base64(In) -> Out +-%% In = string() | binary() +-%% Out = string() | binary() +-encode_base64(List) when is_list(List) -> +- encode_base64_1(list_to_binary(List)); +-encode_base64(Bin) when is_binary(Bin) -> +- List = encode_base64_1(Bin), +- list_to_binary(List). +- +-encode_base64_1(<>) -> +- [int_to_b64(A), int_to_b64(B), +- int_to_b64(C), int_to_b64(D) | encode_base64_1(Rest)]; +-encode_base64_1(<>) -> +- [int_to_b64(A), int_to_b64(B), int_to_b64(C bsl 2), $=]; +-encode_base64_1(<>) -> +- [int_to_b64(A), int_to_b64(B bsl 4), $=, $=]; +-encode_base64_1(<<>>) -> +- []. +- +-%% @doc Implements the base64 decoding algorithm. The output data type matches in the input data type. +-%% @spec decode_base64(In) -> Out | exit({error, invalid_input}) +-%% In = string() | binary() +-%% Out = string() | binary() +-decode_base64(List) when is_list(List) -> +- decode_base64_1(List, []); +-decode_base64(Bin) when is_binary(Bin) -> +- List = decode_base64_1(binary_to_list(Bin), []), +- list_to_binary(List). +- +-decode_base64_1([H | T], Acc) when ((H == $\t) or +- (H == 32) or +- (H == $\r) or +- (H == $\n)) -> +- decode_base64_1(T, Acc); +- +-decode_base64_1([$=, $=], Acc) -> +- lists:reverse(Acc); +-decode_base64_1([$=, _ | _], _Acc) -> +- exit({error, invalid_input}); +- +-decode_base64_1([A1, B1, $=, $=], Acc) -> +- A = b64_to_int(A1), +- B = b64_to_int(B1), +- Oct1 = (A bsl 2) bor (B bsr 4), +- decode_base64_1([], [Oct1 | Acc]); +-decode_base64_1([A1, B1, C1, $=], Acc) -> +- A = b64_to_int(A1), +- B = b64_to_int(B1), +- C = b64_to_int(C1), +- Oct1 = (A bsl 2) bor (B bsr 4), +- Oct2 = ((B band 16#f) bsl 6) bor (C bsr 2), +- decode_base64_1([], [Oct2, Oct1 | Acc]); +-decode_base64_1([A1, B1, C1, D1 | T], Acc) -> +- A = b64_to_int(A1), +- B = b64_to_int(B1), +- C = b64_to_int(C1), +- D = b64_to_int(D1), +- Oct1 = (A bsl 2) bor (B bsr 4), +- Oct2 = ((B band 16#f) bsl 4) bor (C bsr 2), +- Oct3 = ((C band 2#11) bsl 6) bor D, +- decode_base64_1(T, [Oct3, Oct2, Oct1 | Acc]); +-decode_base64_1([], Acc) -> +- lists:reverse(Acc). +- +-%% Taken from httpd_util.erl +-int_to_b64(X) when X >= 0, X =< 25 -> X + $A; +-int_to_b64(X) when X >= 26, X =< 51 -> X - 26 + $a; +-int_to_b64(X) when X >= 52, X =< 61 -> X - 52 + $0; +-int_to_b64(62) -> $+; +-int_to_b64(63) -> $/. +- +-%% Taken from httpd_util.erl +-b64_to_int(X) when X >= $A, X =< $Z -> X - $A; +-b64_to_int(X) when X >= $a, X =< $z -> X - $a + 26; +-b64_to_int(X) when X >= $0, X =< $9 -> X - $0 + 52; +-b64_to_int($+) -> 62; +-b64_to_int($/) -> 63. +- +-get_value(Tag, TVL, DefVal) -> +- case lists:keysearch(Tag, 1, TVL) of +- false -> +- DefVal; +- {value, {_, Val}} -> +- Val +- end. +- +-get_value(Tag, TVL) -> +- {value, {_, V}} = lists:keysearch(Tag,1,TVL), +- V. +- +-parse_url(Url) -> +- parse_url(Url, get_protocol, #url{abspath=Url}, []). +- +-parse_url([$:, $/, $/ | _], get_protocol, Url, []) -> +- {invalid_uri_1, Url}; +-parse_url([$:, $/, $/ | T], get_protocol, Url, TmpAcc) -> +- Prot = list_to_atom(lists:reverse(TmpAcc)), +- parse_url(T, get_username, +- Url#url{protocol = Prot}, +- []); +-parse_url([$/ | T], get_username, Url, TmpAcc) -> +- %% No username/password. No port number +- Url#url{host = lists:reverse(TmpAcc), +- port = default_port(Url#url.protocol), +- path = [$/ | T]}; +-parse_url([$: | T], get_username, Url, TmpAcc) -> +- %% It is possible that no username/password has been +- %% specified. But we'll continue with the assumption that there is +- %% a username/password. If we encounter a '@' later on, there is a +- %% username/password indeed. If we encounter a '/', it was +- %% actually the hostname +- parse_url(T, get_password, +- Url#url{username = lists:reverse(TmpAcc)}, +- []); +-parse_url([$@ | T], get_username, Url, TmpAcc) -> +- parse_url(T, get_host, +- Url#url{username = lists:reverse(TmpAcc), +- password = ""}, +- []); +-parse_url([$@ | T], get_password, Url, TmpAcc) -> +- parse_url(T, get_host, +- Url#url{password = lists:reverse(TmpAcc)}, +- []); +-parse_url([$/ | T], get_password, Url, TmpAcc) -> +- %% Ok, what we thought was the username/password was the hostname +- %% and portnumber +- #url{username=User} = Url, +- Port = list_to_integer(lists:reverse(TmpAcc)), +- Url#url{host = User, +- port = Port, +- username = undefined, +- password = undefined, +- path = [$/ | T]}; +-parse_url([$: | T], get_host, #url{} = Url, TmpAcc) -> +- parse_url(T, get_port, +- Url#url{host = lists:reverse(TmpAcc)}, +- []); +-parse_url([$/ | T], get_host, #url{protocol=Prot} = Url, TmpAcc) -> +- Url#url{host = lists:reverse(TmpAcc), +- port = default_port(Prot), +- path = [$/ | T]}; +-parse_url([$/ | T], get_port, #url{protocol=Prot} = Url, TmpAcc) -> +- Port = case TmpAcc of +- [] -> +- default_port(Prot); +- _ -> +- list_to_integer(lists:reverse(TmpAcc)) +- end, +- Url#url{port = Port, path = [$/ | T]}; +-parse_url([H | T], State, Url, TmpAcc) -> +- parse_url(T, State, Url, [H | TmpAcc]); +-parse_url([], get_host, Url, TmpAcc) when TmpAcc /= [] -> +- Url#url{host = lists:reverse(TmpAcc), +- port = default_port(Url#url.protocol), +- path = "/"}; +-parse_url([], get_username, Url, TmpAcc) when TmpAcc /= [] -> +- Url#url{host = lists:reverse(TmpAcc), +- port = default_port(Url#url.protocol), +- path = "/"}; +-parse_url([], get_port, #url{protocol=Prot} = Url, TmpAcc) -> +- Port = case TmpAcc of +- [] -> +- default_port(Prot); +- _ -> +- list_to_integer(lists:reverse(TmpAcc)) +- end, +- Url#url{port = Port, +- path = "/"}; +-parse_url([], get_password, Url, TmpAcc) -> +- %% Ok, what we thought was the username/password was the hostname +- %% and portnumber +- #url{username=User} = Url, +- Port = case TmpAcc of +- [] -> +- default_port(Url#url.protocol); +- _ -> +- list_to_integer(lists:reverse(TmpAcc)) +- end, +- Url#url{host = User, +- port = Port, +- username = undefined, +- password = undefined, +- path = "/"}; +-parse_url([], State, Url, TmpAcc) -> +- {invalid_uri_2, State, Url, TmpAcc}. +- +-default_port(http) -> 80; +-default_port(https) -> 443; +-default_port(ftp) -> 21. +- +-printable_date() -> +- {{Y,Mo,D},{H, M, S}} = calendar:local_time(), +- {_,_,MicroSecs} = now(), +- [integer_to_list(Y), +- $-, +- integer_to_list(Mo), +- $-, +- integer_to_list(D), +- $_, +- integer_to_list(H), +- $:, +- integer_to_list(M), +- $:, +- integer_to_list(S), +- $:, +- integer_to_list(MicroSecs div 1000)]. +- +-do_trace(Fmt, Args) -> +- do_trace(get(my_trace_flag), Fmt, Args). +- +--ifdef(DEBUG). +-do_trace(_, Fmt, Args) -> +- io:format("~s -- (~s) - "++Fmt, +- [printable_date(), +- get(ibrowse_trace_token) | Args]). +--else. +-do_trace(true, Fmt, Args) -> +- io:format("~s -- (~s) - "++Fmt, +- [printable_date(), +- get(ibrowse_trace_token) | Args]); +-do_trace(_, _, _) -> +- ok. +--endif. +diff --git a/src/ibrowse/ibrowse_sup.erl b/src/ibrowse/ibrowse_sup.erl +deleted file mode 100644 +index 1b9b863..0000000 +--- a/src/ibrowse/ibrowse_sup.erl ++++ /dev/null +@@ -1,65 +0,0 @@ +-%%%------------------------------------------------------------------- +-%%% File : ibrowse_sup.erl +-%%% Author : Chandrashekhar Mullaparthi +-%%% Description : +-%%% +-%%% Created : 15 Oct 2003 by Chandrashekhar Mullaparthi +-%%%------------------------------------------------------------------- +--module(ibrowse_sup). +--vsn('$Id: ibrowse_sup.erl,v 1.1 2005/05/05 22:28:28 chandrusf Exp $ '). +- +--behaviour(supervisor). +-%%-------------------------------------------------------------------- +-%% Include files +-%%-------------------------------------------------------------------- +- +-%%-------------------------------------------------------------------- +-%% External exports +-%%-------------------------------------------------------------------- +--export([ +- start_link/0 +- ]). +- +-%%-------------------------------------------------------------------- +-%% Internal exports +-%%-------------------------------------------------------------------- +--export([ +- init/1 +- ]). +- +-%%-------------------------------------------------------------------- +-%% Macros +-%%-------------------------------------------------------------------- +--define(SERVER, ?MODULE). +- +-%%-------------------------------------------------------------------- +-%% Records +-%%-------------------------------------------------------------------- +- +-%%==================================================================== +-%% External functions +-%%==================================================================== +-%%-------------------------------------------------------------------- +-%% Function: start_link/0 +-%% Description: Starts the supervisor +-%%-------------------------------------------------------------------- +-start_link() -> +- supervisor:start_link({local, ?SERVER}, ?MODULE, []). +- +-%%==================================================================== +-%% Server functions +-%%==================================================================== +-%%-------------------------------------------------------------------- +-%% Func: init/1 +-%% Returns: {ok, {SupFlags, [ChildSpec]}} | +-%% ignore | +-%% {error, Reason} +-%%-------------------------------------------------------------------- +-init([]) -> +- AChild = {ibrowse,{ibrowse,start_link,[]}, +- permanent,2000,worker,[ibrowse, ibrowse_http_client]}, +- {ok,{{one_for_all,10,1}, [AChild]}}. +- +-%%==================================================================== +-%% Internal functions +-%%==================================================================== +diff --git a/src/ibrowse/ibrowse_test.erl b/src/ibrowse/ibrowse_test.erl +deleted file mode 100644 +index 3dc66ec..0000000 +--- a/src/ibrowse/ibrowse_test.erl ++++ /dev/null +@@ -1,377 +0,0 @@ +-%%% File : ibrowse_test.erl +-%%% Author : Chandrashekhar Mullaparthi +-%%% Description : Test ibrowse +-%%% Created : 14 Oct 2003 by Chandrashekhar Mullaparthi +- +--module(ibrowse_test). +--vsn('$Id: ibrowse_test.erl,v 1.4 2009/07/01 22:43:19 chandrusf Exp $ '). +--export([ +- load_test/3, +- send_reqs_1/3, +- do_send_req/2, +- unit_tests/0, +- unit_tests/1, +- unit_tests_1/2, +- drv_ue_test/0, +- drv_ue_test/1, +- ue_test/0, +- ue_test/1, +- verify_chunked_streaming/0, +- verify_chunked_streaming/1, +- i_do_async_req_list/4, +- test_stream_once/3, +- test_stream_once/4 +- ]). +- +-test_stream_once(Url, Method, Options) -> +- test_stream_once(Url, Method, Options, 5000). +- +-test_stream_once(Url, Method, Options, Timeout) -> +- case ibrowse:send_req(Url, [], Method, [], [{stream_to, {self(), once}} | Options], Timeout) of +- {ibrowse_req_id, Req_id} -> +- case ibrowse:stream_next(Req_id) of +- ok -> +- test_stream_once(Req_id); +- Err -> +- Err +- end; +- Err -> +- Err +- end. +- +-test_stream_once(Req_id) -> +- receive +- {ibrowse_async_headers, Req_id, StatCode, Headers} -> +- io:format("Recvd headers~n~p~n", [{ibrowse_async_headers, Req_id, StatCode, Headers}]), +- case ibrowse:stream_next(Req_id) of +- ok -> +- test_stream_once(Req_id); +- Err -> +- Err +- end; +- {ibrowse_async_response, Req_id, {error, Err}} -> +- io:format("Recvd error: ~p~n", [Err]); +- {ibrowse_async_response, Req_id, Body_1} -> +- io:format("Recvd body part: ~n~p~n", [{ibrowse_async_response, Req_id, Body_1}]), +- case ibrowse:stream_next(Req_id) of +- ok -> +- test_stream_once(Req_id); +- Err -> +- Err +- end; +- {ibrowse_async_response_end, Req_id} -> +- ok +- end. +-%% Use ibrowse:set_max_sessions/3 and ibrowse:set_max_pipeline_size/3 to +-%% tweak settings before running the load test. The defaults are 10 and 10. +-load_test(Url, NumWorkers, NumReqsPerWorker) when is_list(Url), +- is_integer(NumWorkers), +- is_integer(NumReqsPerWorker), +- NumWorkers > 0, +- NumReqsPerWorker > 0 -> +- proc_lib:spawn(?MODULE, send_reqs_1, [Url, NumWorkers, NumReqsPerWorker]). +- +-send_reqs_1(Url, NumWorkers, NumReqsPerWorker) -> +- Start_time = now(), +- ets:new(pid_table, [named_table, public]), +- ets:new(ibrowse_test_results, [named_table, public]), +- ets:new(ibrowse_errors, [named_table, public, ordered_set]), +- init_results(), +- process_flag(trap_exit, true), +- log_msg("Starting spawning of workers...~n", []), +- spawn_workers(Url, NumWorkers, NumReqsPerWorker), +- log_msg("Finished spawning workers...~n", []), +- do_wait(), +- End_time = now(), +- log_msg("All workers are done...~n", []), +- log_msg("ibrowse_test_results table: ~n~p~n", [ets:tab2list(ibrowse_test_results)]), +- log_msg("Start time: ~1000.p~n", [calendar:now_to_local_time(Start_time)]), +- log_msg("End time : ~1000.p~n", [calendar:now_to_local_time(End_time)]), +- Elapsed_time_secs = trunc(timer:now_diff(End_time, Start_time) / 1000000), +- log_msg("Elapsed : ~p~n", [Elapsed_time_secs]), +- log_msg("Reqs/sec : ~p~n", [round(trunc((NumWorkers*NumReqsPerWorker) / Elapsed_time_secs))]), +- dump_errors(). +- +-init_results() -> +- ets:insert(ibrowse_test_results, {crash, 0}), +- ets:insert(ibrowse_test_results, {send_failed, 0}), +- ets:insert(ibrowse_test_results, {other_error, 0}), +- ets:insert(ibrowse_test_results, {success, 0}), +- ets:insert(ibrowse_test_results, {retry_later, 0}), +- ets:insert(ibrowse_test_results, {trid_mismatch, 0}), +- ets:insert(ibrowse_test_results, {success_no_trid, 0}), +- ets:insert(ibrowse_test_results, {failed, 0}), +- ets:insert(ibrowse_test_results, {timeout, 0}), +- ets:insert(ibrowse_test_results, {req_id, 0}). +- +-spawn_workers(_Url, 0, _) -> +- ok; +-spawn_workers(Url, NumWorkers, NumReqsPerWorker) -> +- Pid = proc_lib:spawn_link(?MODULE, do_send_req, [Url, NumReqsPerWorker]), +- ets:insert(pid_table, {Pid, []}), +- spawn_workers(Url, NumWorkers - 1, NumReqsPerWorker). +- +-do_wait() -> +- receive +- {'EXIT', _, normal} -> +- do_wait(); +- {'EXIT', Pid, Reason} -> +- ets:delete(pid_table, Pid), +- ets:insert(ibrowse_errors, {Pid, Reason}), +- ets:update_counter(ibrowse_test_results, crash, 1), +- do_wait(); +- Msg -> +- io:format("Recvd unknown message...~p~n", [Msg]), +- do_wait() +- after 1000 -> +- case ets:info(pid_table, size) of +- 0 -> +- done; +- _ -> +- do_wait() +- end +- end. +- +-do_send_req(Url, NumReqs) -> +- do_send_req_1(Url, NumReqs). +- +-do_send_req_1(_Url, 0) -> +- ets:delete(pid_table, self()); +-do_send_req_1(Url, NumReqs) -> +- Counter = integer_to_list(ets:update_counter(ibrowse_test_results, req_id, 1)), +- case ibrowse:send_req(Url, [{"ib_req_id", Counter}], get, [], [], 10000) of +- {ok, _Status, Headers, _Body} -> +- case lists:keysearch("ib_req_id", 1, Headers) of +- {value, {_, Counter}} -> +- ets:update_counter(ibrowse_test_results, success, 1); +- {value, _} -> +- ets:update_counter(ibrowse_test_results, trid_mismatch, 1); +- false -> +- ets:update_counter(ibrowse_test_results, success_no_trid, 1) +- end; +- {error, req_timedout} -> +- ets:update_counter(ibrowse_test_results, timeout, 1); +- {error, send_failed} -> +- ets:update_counter(ibrowse_test_results, send_failed, 1); +- {error, retry_later} -> +- ets:update_counter(ibrowse_test_results, retry_later, 1); +- Err -> +- ets:insert(ibrowse_errors, {now(), Err}), +- ets:update_counter(ibrowse_test_results, other_error, 1), +- ok +- end, +- do_send_req_1(Url, NumReqs-1). +- +-dump_errors() -> +- case ets:info(ibrowse_errors, size) of +- 0 -> +- ok; +- _ -> +- {A, B, C} = now(), +- Filename = lists:flatten( +- io_lib:format("ibrowse_errors_~p_~p_~p.txt" , [A, B, C])), +- case file:open(Filename, [write, delayed_write, raw]) of +- {ok, Iod} -> +- dump_errors(ets:first(ibrowse_errors), Iod); +- Err -> +- io:format("failed to create file ~s. Reason: ~p~n", [Filename, Err]), +- ok +- end +- end. +- +-dump_errors('$end_of_table', Iod) -> +- file:close(Iod); +-dump_errors(Key, Iod) -> +- [{_, Term}] = ets:lookup(ibrowse_errors, Key), +- file:write(Iod, io_lib:format("~p~n", [Term])), +- dump_errors(ets:next(ibrowse_errors, Key), Iod). +- +-%%------------------------------------------------------------------------------ +-%% Unit Tests +-%%------------------------------------------------------------------------------ +--define(TEST_LIST, [{"http://intranet/messenger", get}, +- {"http://www.google.co.uk", get}, +- {"http://www.google.com", get}, +- {"http://www.google.com", options}, +- {"http://www.sun.com", get}, +- {"http://www.oracle.com", get}, +- {"http://www.bbc.co.uk", get}, +- {"http://www.bbc.co.uk", trace}, +- {"http://www.bbc.co.uk", options}, +- {"http://yaws.hyber.org", get}, +- {"http://jigsaw.w3.org/HTTP/ChunkedScript", get}, +- {"http://jigsaw.w3.org/HTTP/TE/foo.txt", get}, +- {"http://jigsaw.w3.org/HTTP/TE/bar.txt", get}, +- {"http://jigsaw.w3.org/HTTP/connection.html", get}, +- {"http://jigsaw.w3.org/HTTP/cc.html", get}, +- {"http://jigsaw.w3.org/HTTP/cc-private.html", get}, +- {"http://jigsaw.w3.org/HTTP/cc-proxy-revalidate.html", get}, +- {"http://jigsaw.w3.org/HTTP/cc-nocache.html", get}, +- {"http://jigsaw.w3.org/HTTP/h-content-md5.html", get}, +- {"http://jigsaw.w3.org/HTTP/h-retry-after.html", get}, +- {"http://jigsaw.w3.org/HTTP/h-retry-after-date.html", get}, +- {"http://jigsaw.w3.org/HTTP/neg", get}, +- {"http://jigsaw.w3.org/HTTP/negbad", get}, +- {"http://jigsaw.w3.org/HTTP/400/toolong/", get}, +- {"http://jigsaw.w3.org/HTTP/300/", get}, +- {"http://jigsaw.w3.org/HTTP/Basic/", get, [{basic_auth, {"guest", "guest"}}]}, +- {"http://jigsaw.w3.org/HTTP/CL/", get}, +- {"http://www.httpwatch.com/httpgallery/chunked/", get} +- ]). +- +-unit_tests() -> +- unit_tests([]). +- +-unit_tests(Options) -> +- Options_1 = Options ++ [{connect_timeout, 5000}], +- {Pid, Ref} = erlang:spawn_monitor(?MODULE, unit_tests_1, [self(), Options_1]), +- receive +- {done, Pid} -> +- ok; +- {'DOWN', Ref, _, _, Info} -> +- io:format("Test process crashed: ~p~n", [Info]) +- after 60000 -> +- exit(Pid, kill), +- io:format("Timed out waiting for tests to complete~n", []) +- end. +- +-unit_tests_1(Parent, Options) -> +- lists:foreach(fun({Url, Method}) -> +- execute_req(Url, Method, Options); +- ({Url, Method, X_Opts}) -> +- execute_req(Url, Method, X_Opts ++ Options) +- end, ?TEST_LIST), +- Parent ! {done, self()}. +- +-verify_chunked_streaming() -> +- verify_chunked_streaming([]). +- +-verify_chunked_streaming(Options) -> +- Url = "http://www.httpwatch.com/httpgallery/chunked/", +- io:format("URL: ~s~n", [Url]), +- io:format("Fetching data without streaming...~n", []), +- Result_without_streaming = ibrowse:send_req( +- Url, [], get, [], +- [{response_format, binary} | Options]), +- io:format("Fetching data with streaming as list...~n", []), +- Async_response_list = do_async_req_list( +- Url, get, [{response_format, list} | Options]), +- io:format("Fetching data with streaming as binary...~n", []), +- Async_response_bin = do_async_req_list( +- Url, get, [{response_format, binary} | Options]), +- compare_responses(Result_without_streaming, Async_response_list, Async_response_bin). +- +-compare_responses({ok, St_code, _, Body}, {ok, St_code, _, Body}, {ok, St_code, _, Body}) -> +- success; +-compare_responses({ok, St_code, _, Body_1}, {ok, St_code, _, Body_2}, {ok, St_code, _, Body_3}) -> +- case Body_1 of +- Body_2 -> +- io:format("Body_1 and Body_2 match~n", []); +- Body_3 -> +- io:format("Body_1 and Body_3 match~n", []); +- _ when Body_2 == Body_3 -> +- io:format("Body_2 and Body_3 match~n", []); +- _ -> +- io:format("All three bodies are different!~n", []) +- end, +- io:format("Body_1 -> ~p~n", [Body_1]), +- io:format("Body_2 -> ~p~n", [Body_2]), +- io:format("Body_3 -> ~p~n", [Body_3]), +- fail_bodies_mismatch; +-compare_responses(R1, R2, R3) -> +- io:format("R1 -> ~p~n", [R1]), +- io:format("R2 -> ~p~n", [R2]), +- io:format("R3 -> ~p~n", [R3]), +- fail. +- +-%% do_async_req_list(Url) -> +-%% do_async_req_list(Url, get). +- +-%% do_async_req_list(Url, Method) -> +-%% do_async_req_list(Url, Method, [{stream_to, self()}, +-%% {stream_chunk_size, 1000}]). +- +-do_async_req_list(Url, Method, Options) -> +- {Pid,_} = erlang:spawn_monitor(?MODULE, i_do_async_req_list, +- [self(), Url, Method, +- Options ++ [{stream_chunk_size, 1000}]]), +- io:format("Spawned process ~p~n", [Pid]), +- wait_for_resp(Pid). +- +-wait_for_resp(Pid) -> +- receive +- {async_result, Pid, Res} -> +- Res; +- {async_result, Other_pid, _} -> +- io:format("~p: Waiting for result from ~p: got from ~p~n", [self(), Pid, Other_pid]), +- wait_for_resp(Pid); +- {'DOWN', _, _, Pid, Reason} -> +- {'EXIT', Reason}; +- {'DOWN', _, _, _, _} -> +- wait_for_resp(Pid); +- Msg -> +- io:format("Recvd unknown message: ~p~n", [Msg]), +- wait_for_resp(Pid) +- after 10000 -> +- {error, timeout} +- end. +- +-i_do_async_req_list(Parent, Url, Method, Options) -> +- Res = ibrowse:send_req(Url, [], Method, [], [{stream_to, self()} | Options]), +- case Res of +- {ibrowse_req_id, Req_id} -> +- Result = wait_for_async_resp(Req_id, undefined, undefined, []), +- Parent ! {async_result, self(), Result}; +- Err -> +- Parent ! {async_result, self(), Err} +- end. +- +-wait_for_async_resp(Req_id, Acc_Stat_code, Acc_Headers, Body) -> +- receive +- {ibrowse_async_headers, Req_id, StatCode, Headers} -> +- wait_for_async_resp(Req_id, StatCode, Headers, Body); +- {ibrowse_async_response_end, Req_id} -> +- Body_1 = list_to_binary(lists:reverse(Body)), +- {ok, Acc_Stat_code, Acc_Headers, Body_1}; +- {ibrowse_async_response, Req_id, Data} -> +- wait_for_async_resp(Req_id, Acc_Stat_code, Acc_Headers, [Data | Body]); +- Err -> +- {ok, Acc_Stat_code, Acc_Headers, Err} +- end. +- +-execute_req(Url, Method, Options) -> +- io:format("~7.7w, ~50.50s: ", [Method, Url]), +- Result = (catch ibrowse:send_req(Url, [], Method, [], Options)), +- case Result of +- {ok, SCode, _H, _B} -> +- io:format("Status code: ~p~n", [SCode]); +- Err -> +- io:format("Err -> ~p~n", [Err]) +- end. +- +-drv_ue_test() -> +- drv_ue_test(lists:duplicate(1024, 127)). +-drv_ue_test(Data) -> +- [{port, Port}| _] = ets:lookup(ibrowse_table, port), +-% erl_ddll:unload_driver("ibrowse_drv"), +-% timer:sleep(1000), +-% erl_ddll:load_driver("../priv", "ibrowse_drv"), +-% Port = open_port({spawn, "ibrowse_drv"}, []), +- {Time, Res} = timer:tc(ibrowse_lib, drv_ue, [Data, Port]), +- io:format("Time -> ~p~n", [Time]), +- io:format("Data Length -> ~p~n", [length(Data)]), +- io:format("Res Length -> ~p~n", [length(Res)]). +-% io:format("Result -> ~s~n", [Res]). +- +-ue_test() -> +- ue_test(lists:duplicate(1024, $?)). +-ue_test(Data) -> +- {Time, Res} = timer:tc(ibrowse_lib, url_encode, [Data]), +- io:format("Time -> ~p~n", [Time]), +- io:format("Data Length -> ~p~n", [length(Data)]), +- io:format("Res Length -> ~p~n", [length(Res)]). +-% io:format("Result -> ~s~n", [Res]). +- +-log_msg(Fmt, Args) -> +- io:format("~s -- " ++ Fmt, +- [ibrowse_lib:printable_date() | Args]). +diff --git a/test/etap/test_util.erl.in b/test/etap/test_util.erl.in +index 948958c..2650fbb 100644 +--- a/test/etap/test_util.erl.in ++++ b/test/etap/test_util.erl.in +@@ -22,7 +22,7 @@ builddir() -> + "@abs_top_builddir@". + + init_code_path() -> +- Paths = ["couchdb", "ibrowse"], ++ Paths = ["couchdb"], + lists:foreach(fun(Name) -> + code:add_pathz(filename:join([builddir(), "src", Name])) + end, Paths). +-- +1.7.1.1 + diff --git a/couchdb-1.0.0-0007-Workaround-for-system-wide-ibrowse.patch b/couchdb-1.0.0-0007-Workaround-for-system-wide-ibrowse.patch new file mode 100644 index 0000000..8bc2965 --- /dev/null +++ b/couchdb-1.0.0-0007-Workaround-for-system-wide-ibrowse.patch @@ -0,0 +1,68 @@ +From 96e4e5057365e642fa983c7bac4eec0177b386f9 Mon Sep 17 00:00:00 2001 +From: Peter Lemenkov +Date: Tue, 8 Jun 2010 17:30:49 +0400 +Subject: [PATCH 7/9] Workaround for system-wide ibrowse + +--- + src/couchdb/couch_rep_changes_feed.erl | 2 +- + src/couchdb/couch_rep_httpc.erl | 6 +++--- + src/couchdb/couch_rep_reader.erl | 2 +- + 3 files changed, 5 insertions(+), 5 deletions(-) + +diff --git a/src/couchdb/couch_rep_changes_feed.erl b/src/couchdb/couch_rep_changes_feed.erl +index b1d3960..be7bd4c 100644 +--- a/src/couchdb/couch_rep_changes_feed.erl ++++ b/src/couchdb/couch_rep_changes_feed.erl +@@ -20,7 +20,7 @@ + -define(BUFFER_SIZE, 1000). + + -include("couch_db.hrl"). +--include("../ibrowse/ibrowse.hrl"). ++-include_lib("ibrowse/include/ibrowse.hrl"). + + -record (state, { + changes_from = nil, +diff --git a/src/couchdb/couch_rep_httpc.erl b/src/couchdb/couch_rep_httpc.erl +index 53e318d..3ac62ed 100644 +--- a/src/couchdb/couch_rep_httpc.erl ++++ b/src/couchdb/couch_rep_httpc.erl +@@ -12,7 +12,7 @@ + + -module(couch_rep_httpc). + -include("couch_db.hrl"). +--include("../ibrowse/ibrowse.hrl"). ++-include_lib("ibrowse/include/ibrowse.hrl"). + + -export([db_exists/1, db_exists/2, full_url/1, request/1, redirected_request/2, + spawn_worker_process/1, spawn_link_worker_process/1]). +@@ -188,12 +188,12 @@ redirected_request(Req, RedirectUrl) -> + + spawn_worker_process(Req) -> + Url = ibrowse_lib:parse_url(Req#http_db.url), +- {ok, Pid} = ibrowse_http_client:start(Url), ++ {ok, Pid} = ibrowse_http_client:start({undefined, Url, {[{ssl_imp, new}], (Url#url.protocol == https)}}), + Pid. + + spawn_link_worker_process(Req) -> + Url = ibrowse_lib:parse_url(Req#http_db.url), +- {ok, Pid} = ibrowse_http_client:start_link(Url), ++ {ok, Pid} = ibrowse_http_client:start_link({undefined, Url, {[{ssl_imp, new}], (Url#url.protocol == https)}}), + Pid. + + maybe_decompress(Headers, Body) -> +diff --git a/src/couchdb/couch_rep_reader.erl b/src/couchdb/couch_rep_reader.erl +index 3edc1f3..8722f3f 100644 +--- a/src/couchdb/couch_rep_reader.erl ++++ b/src/couchdb/couch_rep_reader.erl +@@ -25,7 +25,7 @@ + -define (MAX_PIPELINE_SIZE, 50). + + -include("couch_db.hrl"). +--include("../ibrowse/ibrowse.hrl"). ++-include_lib("ibrowse/include/ibrowse.hrl"). + + -record (state, { + parent, +-- +1.7.1.1 + diff --git a/couchdb-1.0.0-0008-Remove-pid-file-after-stop.patch b/couchdb-1.0.0-0008-Remove-pid-file-after-stop.patch new file mode 100644 index 0000000..efda3cf --- /dev/null +++ b/couchdb-1.0.0-0008-Remove-pid-file-after-stop.patch @@ -0,0 +1,28 @@ +From e45565ae2fb6a4074c45ddf45b0c7f7a630de490 Mon Sep 17 00:00:00 2001 +From: Peter Lemenkov +Date: Mon, 7 Jun 2010 15:08:42 +0400 +Subject: [PATCH 8/9] Remove pid-file after stop + +--- + bin/couchdb.tpl.in | 4 +--- + 1 files changed, 1 insertions(+), 3 deletions(-) + +diff --git a/bin/couchdb.tpl.in b/bin/couchdb.tpl.in +index 94d4743..af5cb01 100644 +--- a/bin/couchdb.tpl.in ++++ b/bin/couchdb.tpl.in +@@ -262,10 +262,8 @@ EOF + + stop_couchdb () { + PID=`_get_pid` ++ rm -f $PID_FILE + if test -n "$PID"; then +- if test "$1" = "false"; then +- echo > $PID_FILE +- fi + if kill -0 $PID 2> /dev/null; then + if kill -1 $PID 2> /dev/null; then + if test "$1" = "false"; then +-- +1.7.1.1 + diff --git a/couchdb-1.0.0-0009-deleting-a-DB-while-it-was-being-opened-would-crash-.patch b/couchdb-1.0.0-0009-deleting-a-DB-while-it-was-being-opened-would-crash-.patch new file mode 100644 index 0000000..ba70b20 --- /dev/null +++ b/couchdb-1.0.0-0009-deleting-a-DB-while-it-was-being-opened-would-crash-.patch @@ -0,0 +1,26 @@ +From 4a51eaedeacc36127444db2dac1a37901cd34691 Mon Sep 17 00:00:00 2001 +From: Adam Kocoloski +Date: Sun, 11 Jul 2010 01:00:50 +0000 +Subject: [PATCH 9/9] deleting a DB while it was being opened would crash couch_server + +git-svn-id: https://svn.apache.org/repos/asf/couchdb/trunk@962964 13f79535-47bb-0310-9956-ffa450edef68 +--- + src/couchdb/couch_server.erl | 2 +- + 1 files changed, 1 insertions(+), 1 deletions(-) + +diff --git a/src/couchdb/couch_server.erl b/src/couchdb/couch_server.erl +index 43fd904..88bd610 100644 +--- a/src/couchdb/couch_server.erl ++++ b/src/couchdb/couch_server.erl +@@ -326,7 +326,7 @@ handle_call({delete, DbName, _Options}, _From, Server) -> + couch_util:shutdown_sync(Pid), + true = ets:delete(couch_dbs_by_name, DbName), + true = ets:delete(couch_dbs_by_pid, Pid), +- [gen_server:send_result(F, not_found) || F <- Froms], ++ [gen_server:reply(F, not_found) || F <- Froms], + true; + [{_, {opened, Pid, LruTime}}] -> + couch_util:shutdown_sync(Pid), +-- +1.7.1.1 + diff --git a/couchdb.spec b/couchdb.spec index ca33aaa..58e5699 100644 --- a/couchdb.spec +++ b/couchdb.spec @@ -3,7 +3,7 @@ %define couchdb_home %{_localstatedir}/lib/couchdb Name: couchdb -Version: 0.11.1 +Version: 1.0.0 Release: 1%{?dist} Summary: A document database server, accessible via a RESTful JSON API @@ -12,15 +12,15 @@ License: ASL 2.0 URL: http://couchdb.apache.org/ Source0: http://www.apache.org/dist/%{name}/%{version}/apache-%{name}-%{version}.tar.gz Source1: %{name}.init -Patch1: couchdb0.11.1-0001-Force-init-script-installation.patch -Patch2: couchdb0.11.1-0002-Install-into-erllibdir-by-default.patch -Patch3: couchdb0.11.1-0003-Remove-bundled-erlang-oauth-library.patch -Patch4: couchdb0.11.1-0004-Remove-bundled-erlang-etap-library.patch -Patch5: couchdb0.11.1-0005-Remove-bundled-mochiweb-library.patch -Patch6: couchdb0.11.1-0006-Remove-bundled-ibrowse-library.patch -Patch7: couchdb0.11.1-0007-Workaround-for-system-wide-ibrowse.patch -Patch8: couchdb0.11.1-0008-Remove-pid-file-after-stop.patch -Patch9: couchdb0.11.1-0009-deleting-a-DB-while-it-was-being-opened-would-crash-.patch +Patch1: couchdb-1.0.0-0001-Force-init-script-installation.patch +Patch2: couchdb-1.0.0-0002-Install-into-erllibdir-by-default.patch +Patch3: couchdb-1.0.0-0003-Remove-bundled-erlang-oauth-library.patch +Patch4: couchdb-1.0.0-0004-Remove-bundled-erlang-etap-library.patch +Patch5: couchdb-1.0.0-0005-Remove-bundled-mochiweb-library.patch +Patch6: couchdb-1.0.0-0006-Remove-bundled-ibrowse-library.patch +Patch7: couchdb-1.0.0-0007-Workaround-for-system-wide-ibrowse.patch +Patch8: couchdb-1.0.0-0008-Remove-pid-file-after-stop.patch +Patch9: couchdb-1.0.0-0009-deleting-a-DB-while-it-was-being-opened-would-crash-.patch BuildRoot: %{_tmppath}/%{name}-%{version}-%{release}-root-%(%{__id_u} -n) BuildRequires: curl-devel @@ -154,6 +154,9 @@ fi %changelog +* Thu Jul 15 2010 Peter Lemenkov 1.0.0-1 +- Ver. 1.0.0 + * Wed Jul 14 2010 Peter Lemenkov 0.11.1-1 - Ver. 0.11.1 - Removed patch for compatibility with Erlang/OTP R14A (merged upstream) diff --git a/couchdb0.11.1-0001-Force-init-script-installation.patch b/couchdb0.11.1-0001-Force-init-script-installation.patch deleted file mode 100644 index 199dfc4..0000000 --- a/couchdb0.11.1-0001-Force-init-script-installation.patch +++ /dev/null @@ -1,39 +0,0 @@ -From 3965421f5754d6b73bf2f04e135e5b732af354fb Mon Sep 17 00:00:00 2001 -From: Peter Lemenkov -Date: Thu, 27 May 2010 15:17:20 +0400 -Subject: [PATCH 1/9] Force init script installation - ---- - configure.ac | 16 +++------------- - 1 files changed, 3 insertions(+), 13 deletions(-) - -diff --git a/configure.ac b/configure.ac -index 0c08952..08a1c1c 100644 ---- a/configure.ac -+++ b/configure.ac -@@ -294,19 +294,9 @@ launchd_enabled=false - - if test "$use_init" = "yes"; then - AC_MSG_CHECKING(location of init directory) -- if test -d /etc/rc.d; then -- init_enabled=true -- AC_SUBST([initdir], ['${sysconfdir}/rc.d']) -- AC_MSG_RESULT(${initdir}) -- else -- if test -d /etc/init.d; then -- init_enabled=true -- AC_SUBST([initdir], ['${sysconfdir}/init.d']) -- AC_MSG_RESULT(${initdir}) -- else -- AC_MSG_RESULT(not found) -- fi -- fi -+ init_enabled=true -+ AC_SUBST([initdir], ['${sysconfdir}/rc.d']) -+ AC_MSG_RESULT(${initdir}) - fi - - if test "$use_launchd" = "yes"; then --- -1.7.1.1 - diff --git a/couchdb0.11.1-0002-Install-into-erllibdir-by-default.patch b/couchdb0.11.1-0002-Install-into-erllibdir-by-default.patch deleted file mode 100644 index 3194925..0000000 --- a/couchdb0.11.1-0002-Install-into-erllibdir-by-default.patch +++ /dev/null @@ -1,34 +0,0 @@ -From 028e72d7b46b69eb8d9b95aaf4e90e4175bbb588 Mon Sep 17 00:00:00 2001 -From: Peter Lemenkov -Date: Thu, 27 May 2010 15:18:29 +0400 -Subject: [PATCH 2/9] Install into erllibdir by default - ---- - configure | 4 ++-- - 1 files changed, 2 insertions(+), 2 deletions(-) - -diff --git a/configure b/configure -index 42a7489..4eca719 100755 ---- a/configure -+++ b/configure -@@ -12193,7 +12193,7 @@ localdatadir=${datadir}/${package_identifier} - - localdocdir=${datadir}/doc/${package_identifier} - --locallibdir=${libdir}/${package_identifier} -+locallibdir=${libdir} - - localstatelibdir=${localstatedir}/lib/${package_identifier} - -@@ -12209,7 +12209,7 @@ if test x${IS_WINDOWS} = xTRUE; then - localerlanglibdir=${libdir} - - else -- locallibbindir=${locallibdir}/bin -+ locallibbindir=${locallibdir}/erlang/lib/couch-${version}/priv - - localerlanglibdir=${locallibdir}/erlang/lib - --- -1.7.1.1 - diff --git a/couchdb0.11.1-0003-Remove-bundled-erlang-oauth-library.patch b/couchdb0.11.1-0003-Remove-bundled-erlang-oauth-library.patch deleted file mode 100644 index 0c62f1d..0000000 --- a/couchdb0.11.1-0003-Remove-bundled-erlang-oauth-library.patch +++ /dev/null @@ -1,964 +0,0 @@ -From f04d36e140eaecf2695a541a1b3f7f9fe761df21 Mon Sep 17 00:00:00 2001 -From: Peter Lemenkov -Date: Wed, 14 Jul 2010 17:57:54 +0400 -Subject: [PATCH 3/9] Remove bundled erlang-oauth library - ---- - configure | 3 - - configure.ac | 1 - - src/Makefile.am | 2 +- - src/Makefile.in | 2 +- - src/erlang-oauth/Makefile.am | 49 ---- - src/erlang-oauth/Makefile.in | 484 ---------------------------------- - src/erlang-oauth/oauth.app.in | 20 -- - src/erlang-oauth/oauth.erl | 107 -------- - src/erlang-oauth/oauth_hmac_sha1.erl | 11 - - src/erlang-oauth/oauth_http.erl | 22 -- - src/erlang-oauth/oauth_plaintext.erl | 10 - - src/erlang-oauth/oauth_unix.erl | 16 -- - src/erlang-oauth/oauth_uri.erl | 88 ------ - test/etap/test_util.erl.in | 2 +- - 14 files changed, 3 insertions(+), 814 deletions(-) - delete mode 100644 src/erlang-oauth/Makefile.am - delete mode 100644 src/erlang-oauth/Makefile.in - delete mode 100644 src/erlang-oauth/oauth.app.in - delete mode 100644 src/erlang-oauth/oauth.erl - delete mode 100644 src/erlang-oauth/oauth_hmac_sha1.erl - delete mode 100644 src/erlang-oauth/oauth_http.erl - delete mode 100644 src/erlang-oauth/oauth_plaintext.erl - delete mode 100644 src/erlang-oauth/oauth_unix.erl - delete mode 100644 src/erlang-oauth/oauth_uri.erl - -diff --git a/configure b/configure -index 4eca719..316b8c3 100755 ---- a/configure -+++ b/configure -@@ -12261,8 +12261,6 @@ ac_config_files="$ac_config_files src/couchdb/Makefile" - - ac_config_files="$ac_config_files src/couchdb/priv/Makefile" - --ac_config_files="$ac_config_files src/erlang-oauth/Makefile" -- - ac_config_files="$ac_config_files src/etap/Makefile" - - ac_config_files="$ac_config_files src/ibrowse/Makefile" -@@ -13290,7 +13288,6 @@ do - "src/couchdb/couch.app.tpl") CONFIG_FILES="$CONFIG_FILES src/couchdb/couch.app.tpl" ;; - "src/couchdb/Makefile") CONFIG_FILES="$CONFIG_FILES src/couchdb/Makefile" ;; - "src/couchdb/priv/Makefile") CONFIG_FILES="$CONFIG_FILES src/couchdb/priv/Makefile" ;; -- "src/erlang-oauth/Makefile") CONFIG_FILES="$CONFIG_FILES src/erlang-oauth/Makefile" ;; - "src/etap/Makefile") CONFIG_FILES="$CONFIG_FILES src/etap/Makefile" ;; - "src/ibrowse/Makefile") CONFIG_FILES="$CONFIG_FILES src/ibrowse/Makefile" ;; - "src/mochiweb/Makefile") CONFIG_FILES="$CONFIG_FILES src/mochiweb/Makefile" ;; -diff --git a/configure.ac b/configure.ac -index 08a1c1c..84a38d3 100644 ---- a/configure.ac -+++ b/configure.ac -@@ -387,7 +387,6 @@ AC_CONFIG_FILES([src/Makefile]) - AC_CONFIG_FILES([src/couchdb/couch.app.tpl]) - AC_CONFIG_FILES([src/couchdb/Makefile]) - AC_CONFIG_FILES([src/couchdb/priv/Makefile]) --AC_CONFIG_FILES([src/erlang-oauth/Makefile]) - AC_CONFIG_FILES([src/etap/Makefile]) - AC_CONFIG_FILES([src/ibrowse/Makefile]) - AC_CONFIG_FILES([src/mochiweb/Makefile]) -diff --git a/src/Makefile.am b/src/Makefile.am -index b9529f9..e577138 100644 ---- a/src/Makefile.am -+++ b/src/Makefile.am -@@ -10,4 +10,4 @@ - ## License for the specific language governing permissions and limitations under - ## the License. - --SUBDIRS = couchdb erlang-oauth etap ibrowse mochiweb -+SUBDIRS = couchdb etap ibrowse mochiweb -diff --git a/src/Makefile.in b/src/Makefile.in -index daff9c3..3fec89c 100644 ---- a/src/Makefile.in -+++ b/src/Makefile.in -@@ -248,7 +248,7 @@ version_minor = @version_minor@ - version_release = @version_release@ - version_revision = @version_revision@ - version_stage = @version_stage@ --SUBDIRS = couchdb erlang-oauth etap ibrowse mochiweb -+SUBDIRS = couchdb etap ibrowse mochiweb - all: all-recursive - - .SUFFIXES: -diff --git a/src/erlang-oauth/Makefile.am b/src/erlang-oauth/Makefile.am -deleted file mode 100644 -index 1d12339..0000000 ---- a/src/erlang-oauth/Makefile.am -+++ /dev/null -@@ -1,49 +0,0 @@ --## 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. -- --oauthebindir = $(localerlanglibdir)/erlang-oauth/ebin -- --# Removed oauth_rsa_sha1.erl until we require R12B5 or --# we add a ./configure option to enable it. -- --oauth_file_collection = \ -- oauth.app.in \ -- oauth.erl \ -- oauth_hmac_sha1.erl \ -- oauth_http.erl \ -- oauth_plaintext.erl \ -- oauth_unix.erl \ -- oauth_uri.erl -- --oauthebin_make_generated_file_list = \ -- oauth.app \ -- oauth.beam \ -- oauth_hmac_sha1.beam \ -- oauth_http.beam \ -- oauth_plaintext.beam \ -- oauth_unix.beam \ -- oauth_uri.beam -- --oauthebin_DATA = \ -- $(oauthebin_make_generated_file_list) -- --EXTRA_DIST = \ -- $(oauth_file_collection) -- --CLEANFILES = \ -- $(oauthebin_make_generated_file_list) -- --%.app: %.app.in -- cp $< $@ -- --%.beam: %.erl -- $(ERLC) $(ERLC_FLAGS) $< -diff --git a/src/erlang-oauth/Makefile.in b/src/erlang-oauth/Makefile.in -deleted file mode 100644 -index 282883b..0000000 ---- a/src/erlang-oauth/Makefile.in -+++ /dev/null -@@ -1,484 +0,0 @@ --# Makefile.in generated by automake 1.11 from Makefile.am. --# @configure_input@ -- --# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, --# 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation, --# Inc. --# This Makefile.in is free software; the Free Software Foundation --# gives unlimited permission to copy and/or distribute it, --# with or without modifications, as long as this notice is preserved. -- --# This program is distributed in the hope that it will be useful, --# but WITHOUT ANY WARRANTY, to the extent permitted by law; without --# even the implied warranty of MERCHANTABILITY or FITNESS FOR A --# PARTICULAR PURPOSE. -- --@SET_MAKE@ -- --VPATH = @srcdir@ --pkgdatadir = $(datadir)/@PACKAGE@ --pkgincludedir = $(includedir)/@PACKAGE@ --pkglibdir = $(libdir)/@PACKAGE@ --pkglibexecdir = $(libexecdir)/@PACKAGE@ --am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd --install_sh_DATA = $(install_sh) -c -m 644 --install_sh_PROGRAM = $(install_sh) -c --install_sh_SCRIPT = $(install_sh) -c --INSTALL_HEADER = $(INSTALL_DATA) --transform = $(program_transform_name) --NORMAL_INSTALL = : --PRE_INSTALL = : --POST_INSTALL = : --NORMAL_UNINSTALL = : --PRE_UNINSTALL = : --POST_UNINSTALL = : --build_triplet = @build@ --host_triplet = @host@ --subdir = src/erlang-oauth --DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in --ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 --am__aclocal_m4_deps = $(top_srcdir)/m4/ac_check_curl.m4 \ -- $(top_srcdir)/m4/ac_check_icu.m4 $(top_srcdir)/m4/libtool.m4 \ -- $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \ -- $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \ -- $(top_srcdir)/acinclude.m4 $(top_srcdir)/configure.ac --am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ -- $(ACLOCAL_M4) --mkinstalldirs = $(install_sh) -d --CONFIG_HEADER = $(top_builddir)/config.h --CONFIG_CLEAN_FILES = --CONFIG_CLEAN_VPATH_FILES = --SOURCES = --DIST_SOURCES = --am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; --am__vpath_adj = case $$p in \ -- $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ -- *) f=$$p;; \ -- esac; --am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; --am__install_max = 40 --am__nobase_strip_setup = \ -- srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` --am__nobase_strip = \ -- for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" --am__nobase_list = $(am__nobase_strip_setup); \ -- for p in $$list; do echo "$$p $$p"; done | \ -- sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ -- $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ -- if (++n[$$2] == $(am__install_max)) \ -- { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ -- END { for (dir in files) print dir, files[dir] }' --am__base_list = \ -- sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ -- sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' --am__installdirs = "$(DESTDIR)$(oauthebindir)" --DATA = $(oauthebin_DATA) --DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) --ACLOCAL = @ACLOCAL@ --AMTAR = @AMTAR@ --AR = @AR@ --AUTOCONF = @AUTOCONF@ --AUTOHEADER = @AUTOHEADER@ --AUTOMAKE = @AUTOMAKE@ --AWK = @AWK@ --CC = @CC@ --CCDEPMODE = @CCDEPMODE@ --CFLAGS = @CFLAGS@ --CPP = @CPP@ --CPPFLAGS = @CPPFLAGS@ --CURL_CFLAGS = @CURL_CFLAGS@ --CURL_CONFIG = @CURL_CONFIG@ --CURL_LDFLAGS = @CURL_LDFLAGS@ --CURL_LIBS = @CURL_LIBS@ --CYGPATH_W = @CYGPATH_W@ --DEFS = @DEFS@ --DEPDIR = @DEPDIR@ --DSYMUTIL = @DSYMUTIL@ --DUMPBIN = @DUMPBIN@ --ECHO_C = @ECHO_C@ --ECHO_N = @ECHO_N@ --ECHO_T = @ECHO_T@ --EGREP = @EGREP@ --ERL = @ERL@ --ERLC = @ERLC@ --ERLC_FLAGS = @ERLC_FLAGS@ --EXEEXT = @EXEEXT@ --FGREP = @FGREP@ --FLAGS = @FLAGS@ --GREP = @GREP@ --HELP2MAN_EXECUTABLE = @HELP2MAN_EXECUTABLE@ --ICU_CFLAGS = @ICU_CFLAGS@ --ICU_CONFIG = @ICU_CONFIG@ --ICU_CXXFLAGS = @ICU_CXXFLAGS@ --ICU_LIBS = @ICU_LIBS@ --ICU_LOCAL_BIN = @ICU_LOCAL_BIN@ --ICU_LOCAL_CFLAGS = @ICU_LOCAL_CFLAGS@ --ICU_LOCAL_LDFLAGS = @ICU_LOCAL_LDFLAGS@ --INNO_COMPILER_EXECUTABLE = @INNO_COMPILER_EXECUTABLE@ --INSTALL = @INSTALL@ --INSTALL_DATA = @INSTALL_DATA@ --INSTALL_PROGRAM = @INSTALL_PROGRAM@ --INSTALL_SCRIPT = @INSTALL_SCRIPT@ --INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ --JSLIB = @JSLIB@ --JS_LIB_BASE = @JS_LIB_BASE@ --JS_LIB_BINARY = @JS_LIB_BINARY@ --JS_LIB_DIR = @JS_LIB_DIR@ --LD = @LD@ --LDFLAGS = @LDFLAGS@ --LIBOBJS = @LIBOBJS@ --LIBS = @LIBS@ --LIBTOOL = @LIBTOOL@ --LIPO = @LIPO@ --LN_S = @LN_S@ --LTLIBOBJS = @LTLIBOBJS@ --MAKEINFO = @MAKEINFO@ --MKDIR_P = @MKDIR_P@ --NM = @NM@ --NMEDIT = @NMEDIT@ --OBJDUMP = @OBJDUMP@ --OBJEXT = @OBJEXT@ --OTOOL = @OTOOL@ --OTOOL64 = @OTOOL64@ --PACKAGE = @PACKAGE@ --PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ --PACKAGE_NAME = @PACKAGE_NAME@ --PACKAGE_STRING = @PACKAGE_STRING@ --PACKAGE_TARNAME = @PACKAGE_TARNAME@ --PACKAGE_URL = @PACKAGE_URL@ --PACKAGE_VERSION = @PACKAGE_VERSION@ --PATH_SEPARATOR = @PATH_SEPARATOR@ --RANLIB = @RANLIB@ --SED = @SED@ --SET_MAKE = @SET_MAKE@ --SHELL = @SHELL@ --STRIP = @STRIP@ --VERSION = @VERSION@ --abs_builddir = @abs_builddir@ --abs_srcdir = @abs_srcdir@ --abs_top_builddir = @abs_top_builddir@ --abs_top_srcdir = @abs_top_srcdir@ --ac_ct_CC = @ac_ct_CC@ --ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ --am__include = @am__include@ --am__leading_dot = @am__leading_dot@ --am__quote = @am__quote@ --am__tar = @am__tar@ --am__untar = @am__untar@ --bindir = @bindir@ --bug_uri = @bug_uri@ --build = @build@ --build_alias = @build_alias@ --build_cpu = @build_cpu@ --build_os = @build_os@ --build_vendor = @build_vendor@ --builddir = @builddir@ --datadir = @datadir@ --datarootdir = @datarootdir@ --docdir = @docdir@ --dvidir = @dvidir@ --exec_prefix = @exec_prefix@ --host = @host@ --host_alias = @host_alias@ --host_cpu = @host_cpu@ --host_os = @host_os@ --host_vendor = @host_vendor@ --htmldir = @htmldir@ --includedir = @includedir@ --infodir = @infodir@ --initdir = @initdir@ --install_sh = @install_sh@ --launchddir = @launchddir@ --libdir = @libdir@ --libexecdir = @libexecdir@ --localconfdir = @localconfdir@ --localdatadir = @localdatadir@ --localdocdir = @localdocdir@ --localedir = @localedir@ --localerlanglibdir = @localerlanglibdir@ --locallibbindir = @locallibbindir@ --locallibdir = @locallibdir@ --localstatedir = @localstatedir@ --localstatelibdir = @localstatelibdir@ --localstatelogdir = @localstatelogdir@ --localstaterundir = @localstaterundir@ --lt_ECHO = @lt_ECHO@ --mandir = @mandir@ --mkdir_p = @mkdir_p@ --msvc_redist_dir = @msvc_redist_dir@ --msvc_redist_name = @msvc_redist_name@ --oldincludedir = @oldincludedir@ --openssl_bin_dir = @openssl_bin_dir@ --package_author_address = @package_author_address@ --package_author_name = @package_author_name@ --package_identifier = @package_identifier@ --package_name = @package_name@ --package_tarname = @package_tarname@ --pdfdir = @pdfdir@ --prefix = @prefix@ --program_transform_name = @program_transform_name@ --psdir = @psdir@ --sbindir = @sbindir@ --sharedstatedir = @sharedstatedir@ --srcdir = @srcdir@ --sysconfdir = @sysconfdir@ --target_alias = @target_alias@ --top_build_prefix = @top_build_prefix@ --top_builddir = @top_builddir@ --top_srcdir = @top_srcdir@ --version = @version@ --version_major = @version_major@ --version_minor = @version_minor@ --version_release = @version_release@ --version_revision = @version_revision@ --version_stage = @version_stage@ --oauthebindir = $(localerlanglibdir)/erlang-oauth/ebin -- --# Removed oauth_rsa_sha1.erl until we require R12B5 or --# we add a ./configure option to enable it. --oauth_file_collection = \ -- oauth.app.in \ -- oauth.erl \ -- oauth_hmac_sha1.erl \ -- oauth_http.erl \ -- oauth_plaintext.erl \ -- oauth_unix.erl \ -- oauth_uri.erl -- --oauthebin_make_generated_file_list = \ -- oauth.app \ -- oauth.beam \ -- oauth_hmac_sha1.beam \ -- oauth_http.beam \ -- oauth_plaintext.beam \ -- oauth_unix.beam \ -- oauth_uri.beam -- --oauthebin_DATA = \ -- $(oauthebin_make_generated_file_list) -- --EXTRA_DIST = \ -- $(oauth_file_collection) -- --CLEANFILES = \ -- $(oauthebin_make_generated_file_list) -- --all: all-am -- --.SUFFIXES: --$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) -- @for dep in $?; do \ -- case '$(am__configure_deps)' in \ -- *$$dep*) \ -- ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ -- && { if test -f $@; then exit 0; else break; fi; }; \ -- exit 1;; \ -- esac; \ -- done; \ -- echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/erlang-oauth/Makefile'; \ -- $(am__cd) $(top_srcdir) && \ -- $(AUTOMAKE) --foreign src/erlang-oauth/Makefile --.PRECIOUS: Makefile --Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status -- @case '$?' in \ -- *config.status*) \ -- cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ -- *) \ -- echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ -- cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ -- esac; -- --$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) -- cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh -- --$(top_srcdir)/configure: $(am__configure_deps) -- cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh --$(ACLOCAL_M4): $(am__aclocal_m4_deps) -- cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh --$(am__aclocal_m4_deps): -- --mostlyclean-libtool: -- -rm -f *.lo -- --clean-libtool: -- -rm -rf .libs _libs --install-oauthebinDATA: $(oauthebin_DATA) -- @$(NORMAL_INSTALL) -- test -z "$(oauthebindir)" || $(MKDIR_P) "$(DESTDIR)$(oauthebindir)" -- @list='$(oauthebin_DATA)'; test -n "$(oauthebindir)" || list=; \ -- for p in $$list; do \ -- if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ -- echo "$$d$$p"; \ -- done | $(am__base_list) | \ -- while read files; do \ -- echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(oauthebindir)'"; \ -- $(INSTALL_DATA) $$files "$(DESTDIR)$(oauthebindir)" || exit $$?; \ -- done -- --uninstall-oauthebinDATA: -- @$(NORMAL_UNINSTALL) -- @list='$(oauthebin_DATA)'; test -n "$(oauthebindir)" || list=; \ -- files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ -- test -n "$$files" || exit 0; \ -- echo " ( cd '$(DESTDIR)$(oauthebindir)' && rm -f" $$files ")"; \ -- cd "$(DESTDIR)$(oauthebindir)" && rm -f $$files --tags: TAGS --TAGS: -- --ctags: CTAGS --CTAGS: -- -- --distdir: $(DISTFILES) -- @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ -- topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ -- list='$(DISTFILES)'; \ -- dist_files=`for file in $$list; do echo $$file; done | \ -- sed -e "s|^$$srcdirstrip/||;t" \ -- -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ -- case $$dist_files in \ -- */*) $(MKDIR_P) `echo "$$dist_files" | \ -- sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ -- sort -u` ;; \ -- esac; \ -- for file in $$dist_files; do \ -- if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ -- if test -d $$d/$$file; then \ -- dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ -- if test -d "$(distdir)/$$file"; then \ -- find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ -- fi; \ -- if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ -- cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ -- find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ -- fi; \ -- cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ -- else \ -- test -f "$(distdir)/$$file" \ -- || cp -p $$d/$$file "$(distdir)/$$file" \ -- || exit 1; \ -- fi; \ -- done --check-am: all-am --check: check-am --all-am: Makefile $(DATA) --installdirs: -- for dir in "$(DESTDIR)$(oauthebindir)"; do \ -- test -z "$$dir" || $(MKDIR_P) "$$dir"; \ -- done --install: install-am --install-exec: install-exec-am --install-data: install-data-am --uninstall: uninstall-am -- --install-am: all-am -- @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am -- --installcheck: installcheck-am --install-strip: -- $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ -- install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ -- `test -z '$(STRIP)' || \ -- echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install --mostlyclean-generic: -- --clean-generic: -- -test -z "$(CLEANFILES)" || rm -f $(CLEANFILES) -- --distclean-generic: -- -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -- -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) -- --maintainer-clean-generic: -- @echo "This command is intended for maintainers to use" -- @echo "it deletes files that may require special tools to rebuild." --clean: clean-am -- --clean-am: clean-generic clean-libtool mostlyclean-am -- --distclean: distclean-am -- -rm -f Makefile --distclean-am: clean-am distclean-generic -- --dvi: dvi-am -- --dvi-am: -- --html: html-am -- --html-am: -- --info: info-am -- --info-am: -- --install-data-am: install-oauthebinDATA -- --install-dvi: install-dvi-am -- --install-dvi-am: -- --install-exec-am: -- --install-html: install-html-am -- --install-html-am: -- --install-info: install-info-am -- --install-info-am: -- --install-man: -- --install-pdf: install-pdf-am -- --install-pdf-am: -- --install-ps: install-ps-am -- --install-ps-am: -- --installcheck-am: -- --maintainer-clean: maintainer-clean-am -- -rm -f Makefile --maintainer-clean-am: distclean-am maintainer-clean-generic -- --mostlyclean: mostlyclean-am -- --mostlyclean-am: mostlyclean-generic mostlyclean-libtool -- --pdf: pdf-am -- --pdf-am: -- --ps: ps-am -- --ps-am: -- --uninstall-am: uninstall-oauthebinDATA -- --.MAKE: install-am install-strip -- --.PHONY: all all-am check check-am clean clean-generic clean-libtool \ -- distclean distclean-generic distclean-libtool distdir dvi \ -- dvi-am html html-am info info-am install install-am \ -- install-data install-data-am install-dvi install-dvi-am \ -- install-exec install-exec-am install-html install-html-am \ -- install-info install-info-am install-man install-oauthebinDATA \ -- install-pdf install-pdf-am install-ps install-ps-am \ -- install-strip installcheck installcheck-am installdirs \ -- maintainer-clean maintainer-clean-generic mostlyclean \ -- mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ -- uninstall uninstall-am uninstall-oauthebinDATA -- -- --%.app: %.app.in -- cp $< $@ -- --%.beam: %.erl -- $(ERLC) $(ERLC_FLAGS) $< -- --# Tell versions [3.59,3.63) of GNU make to not export all variables. --# Otherwise a system limit (for SysV at least) may be exceeded. --.NOEXPORT: -diff --git a/src/erlang-oauth/oauth.app.in b/src/erlang-oauth/oauth.app.in -deleted file mode 100644 -index 6357b9b..0000000 ---- a/src/erlang-oauth/oauth.app.in -+++ /dev/null -@@ -1,20 +0,0 @@ --{application, oauth, [ -- {description, "Erlang OAuth implementation"}, -- {vsn, "dev"}, -- {modules, [ -- oauth, -- oauth_hmac_sha1, -- oauth_http, -- oauth_plaintext, -- oauth_rsa_sha1, -- oauth_unix, -- oauth_uri -- ]}, -- {registered, []}, -- {applications, [ -- kernel, -- stdlib, -- crypto, -- inets -- ]} --]}. -diff --git a/src/erlang-oauth/oauth.erl b/src/erlang-oauth/oauth.erl -deleted file mode 100644 -index 866655c..0000000 ---- a/src/erlang-oauth/oauth.erl -+++ /dev/null -@@ -1,107 +0,0 @@ ---module(oauth). -- ---export( -- [ get/5 -- , header/1 -- , post/5 -- , signature/5 -- , signature_base_string/3 -- , signed_params/6 -- , token/1 -- , token_secret/1 -- , uri/2 -- , verify/6 -- ]). -- -- --get(URL, ExtraParams, Consumer, Token, TokenSecret) -> -- SignedParams = signed_params("GET", URL, ExtraParams, Consumer, Token, TokenSecret), -- oauth_http:get(uri(URL, SignedParams)). -- --post(URL, ExtraParams, Consumer, Token, TokenSecret) -> -- SignedParams = signed_params("POST", URL, ExtraParams, Consumer, Token, TokenSecret), -- oauth_http:post(URL, oauth_uri:params_to_string(SignedParams)). -- --uri(Base, []) -> -- Base; --uri(Base, Params) -> -- lists:concat([Base, "?", oauth_uri:params_to_string(Params)]). -- --header(Params) -> -- {"Authorization", "OAuth " ++ oauth_uri:params_to_header_string(Params)}. -- --token(Params) -> -- proplists:get_value("oauth_token", Params). -- --token_secret(Params) -> -- proplists:get_value("oauth_token_secret", Params). -- --verify(Signature, HttpMethod, URL, Params, Consumer, TokenSecret) -> -- case signature_method(Consumer) of -- plaintext -> -- oauth_plaintext:verify(Signature, consumer_secret(Consumer), TokenSecret); -- hmac_sha1 -> -- BaseString = signature_base_string(HttpMethod, URL, Params), -- oauth_hmac_sha1:verify(Signature, BaseString, consumer_secret(Consumer), TokenSecret); -- rsa_sha1 -> -- BaseString = signature_base_string(HttpMethod, URL, Params), -- oauth_rsa_sha1:verify(Signature, BaseString, consumer_secret(Consumer)) -- end. -- --signed_params(HttpMethod, URL, ExtraParams, Consumer, Token, TokenSecret) -> -- Params = token_param(Token, params(Consumer, ExtraParams)), -- [{"oauth_signature", signature(HttpMethod, URL, Params, Consumer, TokenSecret)}|Params]. -- --signature(HttpMethod, URL, Params, Consumer, TokenSecret) -> -- case signature_method(Consumer) of -- plaintext -> -- oauth_plaintext:signature(consumer_secret(Consumer), TokenSecret); -- hmac_sha1 -> -- BaseString = signature_base_string(HttpMethod, URL, Params), -- oauth_hmac_sha1:signature(BaseString, consumer_secret(Consumer), TokenSecret); -- rsa_sha1 -> -- BaseString = signature_base_string(HttpMethod, URL, Params), -- oauth_rsa_sha1:signature(BaseString, consumer_secret(Consumer)) -- end. -- --signature_base_string(HttpMethod, URL, Params) -> -- NormalizedURL = oauth_uri:normalize(URL), -- NormalizedParams = oauth_uri:params_to_string(lists:sort(Params)), -- oauth_uri:calate("&", [HttpMethod, NormalizedURL, NormalizedParams]). -- --token_param("", Params) -> -- Params; --token_param(Token, Params) -> -- [{"oauth_token", Token}|Params]. -- --params(Consumer, Params) -> -- Nonce = base64:encode_to_string(crypto:rand_bytes(32)), % cf. ruby-oauth -- params(Consumer, oauth_unix:timestamp(), Nonce, Params). -- --params(Consumer, Timestamp, Nonce, Params) -> -- [ {"oauth_version", "1.0"} -- , {"oauth_nonce", Nonce} -- , {"oauth_timestamp", integer_to_list(Timestamp)} -- , {"oauth_signature_method", signature_method_string(Consumer)} -- , {"oauth_consumer_key", consumer_key(Consumer)} -- | Params -- ]. -- --signature_method_string(Consumer) -> -- case signature_method(Consumer) of -- plaintext -> -- "PLAINTEXT"; -- hmac_sha1 -> -- "HMAC-SHA1"; -- rsa_sha1 -> -- "RSA-SHA1" -- end. -- --signature_method(_Consumer={_, _, Method}) -> -- Method. -- --consumer_secret(_Consumer={_, Secret, _}) -> -- Secret. -- --consumer_key(_Consumer={Key, _, _}) -> -- Key. -diff --git a/src/erlang-oauth/oauth_hmac_sha1.erl b/src/erlang-oauth/oauth_hmac_sha1.erl -deleted file mode 100644 -index 79d59f3..0000000 ---- a/src/erlang-oauth/oauth_hmac_sha1.erl -+++ /dev/null -@@ -1,11 +0,0 @@ ---module(oauth_hmac_sha1). -- ---export([signature/3, verify/4]). -- -- --signature(BaseString, CS, TS) -> -- Key = oauth_uri:calate("&", [CS, TS]), -- base64:encode_to_string(crypto:sha_mac(Key, BaseString)). -- --verify(Signature, BaseString, CS, TS) -> -- couch_util:verify(signature(BaseString, CS, TS), Signature). -diff --git a/src/erlang-oauth/oauth_http.erl b/src/erlang-oauth/oauth_http.erl -deleted file mode 100644 -index bf5a4ba..0000000 ---- a/src/erlang-oauth/oauth_http.erl -+++ /dev/null -@@ -1,22 +0,0 @@ ---module(oauth_http). -- ---export([get/1, post/2, response_params/1, response_body/1, response_code/1]). -- -- --get(URL) -> -- request(get, {URL, []}). -- --post(URL, Data) -> -- request(post, {URL, [], "application/x-www-form-urlencoded", Data}). -- --request(Method, Request) -> -- http:request(Method, Request, [{autoredirect, false}], []). -- --response_params(Response) -> -- oauth_uri:params_from_string(response_body(Response)). -- --response_body({{_, _, _}, _, Body}) -> -- Body. -- --response_code({{_, Code, _}, _, _}) -> -- Code. -diff --git a/src/erlang-oauth/oauth_plaintext.erl b/src/erlang-oauth/oauth_plaintext.erl -deleted file mode 100644 -index 41a1e9b..0000000 ---- a/src/erlang-oauth/oauth_plaintext.erl -+++ /dev/null -@@ -1,10 +0,0 @@ ---module(oauth_plaintext). -- ---export([signature/2, verify/3]). -- -- --signature(CS, TS) -> -- oauth_uri:calate("&", [CS, TS]). -- --verify(Signature, CS, TS) -> -- couch_util:verify(signature(CS, TS), Signature). -diff --git a/src/erlang-oauth/oauth_unix.erl b/src/erlang-oauth/oauth_unix.erl -deleted file mode 100644 -index 73ca314..0000000 ---- a/src/erlang-oauth/oauth_unix.erl -+++ /dev/null -@@ -1,16 +0,0 @@ ---module(oauth_unix). -- ---export([timestamp/0]). -- -- --timestamp() -> -- timestamp(calendar:universal_time()). -- --timestamp(DateTime) -> -- seconds(DateTime) - epoch(). -- --epoch() -> -- seconds({{1970,1,1},{00,00,00}}). -- --seconds(DateTime) -> -- calendar:datetime_to_gregorian_seconds(DateTime). -diff --git a/src/erlang-oauth/oauth_uri.erl b/src/erlang-oauth/oauth_uri.erl -deleted file mode 100644 -index fb27ae7..0000000 ---- a/src/erlang-oauth/oauth_uri.erl -+++ /dev/null -@@ -1,88 +0,0 @@ ---module(oauth_uri). -- ---export([normalize/1, calate/2, encode/1]). ---export([params_from_string/1, params_to_string/1, -- params_from_header_string/1, params_to_header_string/1]). -- ---import(lists, [concat/1]). -- ---define(is_uppercase_alpha(C), C >= $A, C =< $Z). ---define(is_lowercase_alpha(C), C >= $a, C =< $z). ---define(is_alpha(C), ?is_uppercase_alpha(C); ?is_lowercase_alpha(C)). ---define(is_digit(C), C >= $0, C =< $9). ---define(is_alphanumeric(C), ?is_alpha(C); ?is_digit(C)). ---define(is_unreserved(C), ?is_alphanumeric(C); C =:= $-; C =:= $_; C =:= $.; C =:= $~). ---define(is_hex(C), ?is_digit(C); C >= $A, C =< $F). -- -- --normalize(URI) -> -- case http_uri:parse(URI) of -- {Scheme, UserInfo, Host, Port, Path, _Query} -> -- normalize(Scheme, UserInfo, string:to_lower(Host), Port, [Path]); -- Else -> -- Else -- end. -- --normalize(http, UserInfo, Host, 80, Acc) -> -- normalize(http, UserInfo, [Host|Acc]); --normalize(https, UserInfo, Host, 443, Acc) -> -- normalize(https, UserInfo, [Host|Acc]); --normalize(Scheme, UserInfo, Host, Port, Acc) -> -- normalize(Scheme, UserInfo, [Host, ":", Port|Acc]). -- --normalize(Scheme, [], Acc) -> -- concat([Scheme, "://"|Acc]); --normalize(Scheme, UserInfo, Acc) -> -- concat([Scheme, "://", UserInfo, "@"|Acc]). -- --params_to_header_string(Params) -> -- intercalate(", ", [concat([encode(K), "=\"", encode(V), "\""]) || {K, V} <- Params]). -- --params_from_header_string(String) -> -- [param_from_header_string(Param) || Param <- re:split(String, ",\\s*", [{return, list}]), Param =/= ""]. -- --param_from_header_string(Param) -> -- [Key, QuotedValue] = string:tokens(Param, "="), -- Value = string:substr(QuotedValue, 2, length(QuotedValue) - 2), -- {decode(Key), decode(Value)}. -- --params_from_string(Params) -> -- [param_from_string(Param) || Param <- string:tokens(Params, "&")]. -- --param_from_string(Param) -> -- list_to_tuple([decode(Value) || Value <- string:tokens(Param, "=")]). -- --params_to_string(Params) -> -- intercalate("&", [calate("=", [K, V]) || {K, V} <- Params]). -- --calate(Sep, Xs) -> -- intercalate(Sep, [encode(X) || X <- Xs]). -- --intercalate(Sep, Xs) -> -- concat(intersperse(Sep, Xs)). -- --intersperse(_, []) -> []; --intersperse(_, [X]) -> [X]; --intersperse(Sep, [X|Xs]) -> -- [X, Sep|intersperse(Sep, Xs)]. -- --decode(Chars) -> -- decode(Chars, []). -- --decode([], Decoded) -> -- lists:reverse(Decoded); --decode([$%,A,B|Etc], Decoded) when ?is_hex(A), ?is_hex(B) -> -- decode(Etc, [erlang:list_to_integer([A,B], 16)|Decoded]); --decode([C|Etc], Decoded) when ?is_unreserved(C) -> -- decode(Etc, [C|Decoded]). -- --encode(Chars) -> -- encode(Chars, []). -- --encode([], Encoded) -> -- lists:flatten(lists:reverse(Encoded)); --encode([C|Etc], Encoded) when ?is_unreserved(C) -> -- encode(Etc, [C|Encoded]); --encode([C|Etc], Encoded) -> -- Value = io_lib:format("%~2.1.0s", [erlang:integer_to_list(C, 16)]), -- encode(Etc, [Value|Encoded]). -diff --git a/test/etap/test_util.erl.in b/test/etap/test_util.erl.in -index 4c42edb..79b0417 100644 ---- a/test/etap/test_util.erl.in -+++ b/test/etap/test_util.erl.in -@@ -22,7 +22,7 @@ builddir() -> - "@abs_top_builddir@". - - init_code_path() -> -- Paths = ["etap", "couchdb", "erlang-oauth", "ibrowse", "mochiweb"], -+ Paths = ["etap", "couchdb", "ibrowse", "mochiweb"], - lists:foreach(fun(Name) -> - code:add_pathz(filename:join([builddir(), "src", Name])) - end, Paths). --- -1.7.1.1 - diff --git a/couchdb0.11.1-0004-Remove-bundled-erlang-etap-library.patch b/couchdb0.11.1-0004-Remove-bundled-erlang-etap-library.patch deleted file mode 100644 index 8d8bf6f..0000000 --- a/couchdb0.11.1-0004-Remove-bundled-erlang-etap-library.patch +++ /dev/null @@ -1,1912 +0,0 @@ -From 24cd0159c5221340fe511ca146ce62ceb62f7a1c Mon Sep 17 00:00:00 2001 -From: Peter Lemenkov -Date: Wed, 14 Jul 2010 18:00:03 +0400 -Subject: [PATCH 4/9] Remove bundled erlang-etap library - ---- - configure | 3 - - configure.ac | 1 - - src/Makefile.am | 2 +- - src/Makefile.in | 2 +- - src/etap/Makefile.am | 44 ---- - src/etap/Makefile.in | 476 ----------------------------------------- - src/etap/etap.erl | 416 ----------------------------------- - src/etap/etap_application.erl | 72 ------ - src/etap/etap_can.erl | 79 ------- - src/etap/etap_exception.erl | 66 ------ - src/etap/etap_process.erl | 42 ---- - src/etap/etap_report.erl | 343 ----------------------------- - src/etap/etap_request.erl | 89 -------- - src/etap/etap_string.erl | 47 ---- - src/etap/etap_web.erl | 65 ------ - test/etap/test_util.erl.in | 2 +- - 16 files changed, 3 insertions(+), 1746 deletions(-) - delete mode 100644 src/etap/Makefile.am - delete mode 100644 src/etap/Makefile.in - delete mode 100644 src/etap/etap.erl - delete mode 100644 src/etap/etap_application.erl - delete mode 100644 src/etap/etap_can.erl - delete mode 100644 src/etap/etap_exception.erl - delete mode 100644 src/etap/etap_process.erl - delete mode 100644 src/etap/etap_report.erl - delete mode 100644 src/etap/etap_request.erl - delete mode 100644 src/etap/etap_string.erl - delete mode 100644 src/etap/etap_web.erl - -diff --git a/configure b/configure -index 316b8c3..68c8db9 100755 ---- a/configure -+++ b/configure -@@ -12261,8 +12261,6 @@ ac_config_files="$ac_config_files src/couchdb/Makefile" - - ac_config_files="$ac_config_files src/couchdb/priv/Makefile" - --ac_config_files="$ac_config_files src/etap/Makefile" -- - ac_config_files="$ac_config_files src/ibrowse/Makefile" - - ac_config_files="$ac_config_files src/mochiweb/Makefile" -@@ -13288,7 +13286,6 @@ do - "src/couchdb/couch.app.tpl") CONFIG_FILES="$CONFIG_FILES src/couchdb/couch.app.tpl" ;; - "src/couchdb/Makefile") CONFIG_FILES="$CONFIG_FILES src/couchdb/Makefile" ;; - "src/couchdb/priv/Makefile") CONFIG_FILES="$CONFIG_FILES src/couchdb/priv/Makefile" ;; -- "src/etap/Makefile") CONFIG_FILES="$CONFIG_FILES src/etap/Makefile" ;; - "src/ibrowse/Makefile") CONFIG_FILES="$CONFIG_FILES src/ibrowse/Makefile" ;; - "src/mochiweb/Makefile") CONFIG_FILES="$CONFIG_FILES src/mochiweb/Makefile" ;; - "test/Makefile") CONFIG_FILES="$CONFIG_FILES test/Makefile" ;; -diff --git a/configure.ac b/configure.ac -index 84a38d3..e9375be 100644 ---- a/configure.ac -+++ b/configure.ac -@@ -387,7 +387,6 @@ AC_CONFIG_FILES([src/Makefile]) - AC_CONFIG_FILES([src/couchdb/couch.app.tpl]) - AC_CONFIG_FILES([src/couchdb/Makefile]) - AC_CONFIG_FILES([src/couchdb/priv/Makefile]) --AC_CONFIG_FILES([src/etap/Makefile]) - AC_CONFIG_FILES([src/ibrowse/Makefile]) - AC_CONFIG_FILES([src/mochiweb/Makefile]) - AC_CONFIG_FILES([test/Makefile]) -diff --git a/src/Makefile.am b/src/Makefile.am -index e577138..19a5d20 100644 ---- a/src/Makefile.am -+++ b/src/Makefile.am -@@ -10,4 +10,4 @@ - ## License for the specific language governing permissions and limitations under - ## the License. - --SUBDIRS = couchdb etap ibrowse mochiweb -+SUBDIRS = couchdb ibrowse mochiweb -diff --git a/src/Makefile.in b/src/Makefile.in -index 3fec89c..ae1b828 100644 ---- a/src/Makefile.in -+++ b/src/Makefile.in -@@ -248,7 +248,7 @@ version_minor = @version_minor@ - version_release = @version_release@ - version_revision = @version_revision@ - version_stage = @version_stage@ --SUBDIRS = couchdb etap ibrowse mochiweb -+SUBDIRS = couchdb ibrowse mochiweb - all: all-recursive - - .SUFFIXES: -diff --git a/src/etap/Makefile.am b/src/etap/Makefile.am -deleted file mode 100644 -index 732347b..0000000 ---- a/src/etap/Makefile.am -+++ /dev/null -@@ -1,44 +0,0 @@ --## 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. -- --etapebindir = $(localerlanglibdir)/etap/ebin -- --etap_file_collection = \ -- etap.erl \ -- etap_application.erl \ -- etap_can.erl \ -- etap_exception.erl \ -- etap_process.erl \ -- etap_report.erl \ -- etap_request.erl \ -- etap_string.erl \ -- etap_web.erl -- --etapebin_make_generated_file_list = \ -- etap.beam \ -- etap_application.beam \ -- etap_can.beam \ -- etap_exception.beam \ -- etap_process.beam \ -- etap_report.beam \ -- etap_request.beam \ -- etap_string.beam \ -- etap_web.beam -- --etapebin_DATA = $(etapebin_make_generated_file_list) -- --EXTRA_DIST = $(etap_file_collection) -- --CLEANFILES = $(etapebin_make_generated_file_list) -- --%.beam: %.erl -- $(ERLC) $(ERLC_FLAGS) $< -diff --git a/src/etap/Makefile.in b/src/etap/Makefile.in -deleted file mode 100644 -index df91ff7..0000000 ---- a/src/etap/Makefile.in -+++ /dev/null -@@ -1,476 +0,0 @@ --# Makefile.in generated by automake 1.11 from Makefile.am. --# @configure_input@ -- --# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, --# 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation, --# Inc. --# This Makefile.in is free software; the Free Software Foundation --# gives unlimited permission to copy and/or distribute it, --# with or without modifications, as long as this notice is preserved. -- --# This program is distributed in the hope that it will be useful, --# but WITHOUT ANY WARRANTY, to the extent permitted by law; without --# even the implied warranty of MERCHANTABILITY or FITNESS FOR A --# PARTICULAR PURPOSE. -- --@SET_MAKE@ -- --VPATH = @srcdir@ --pkgdatadir = $(datadir)/@PACKAGE@ --pkgincludedir = $(includedir)/@PACKAGE@ --pkglibdir = $(libdir)/@PACKAGE@ --pkglibexecdir = $(libexecdir)/@PACKAGE@ --am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd --install_sh_DATA = $(install_sh) -c -m 644 --install_sh_PROGRAM = $(install_sh) -c --install_sh_SCRIPT = $(install_sh) -c --INSTALL_HEADER = $(INSTALL_DATA) --transform = $(program_transform_name) --NORMAL_INSTALL = : --PRE_INSTALL = : --POST_INSTALL = : --NORMAL_UNINSTALL = : --PRE_UNINSTALL = : --POST_UNINSTALL = : --build_triplet = @build@ --host_triplet = @host@ --subdir = src/etap --DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in --ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 --am__aclocal_m4_deps = $(top_srcdir)/m4/ac_check_curl.m4 \ -- $(top_srcdir)/m4/ac_check_icu.m4 $(top_srcdir)/m4/libtool.m4 \ -- $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \ -- $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \ -- $(top_srcdir)/acinclude.m4 $(top_srcdir)/configure.ac --am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ -- $(ACLOCAL_M4) --mkinstalldirs = $(install_sh) -d --CONFIG_HEADER = $(top_builddir)/config.h --CONFIG_CLEAN_FILES = --CONFIG_CLEAN_VPATH_FILES = --SOURCES = --DIST_SOURCES = --am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; --am__vpath_adj = case $$p in \ -- $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ -- *) f=$$p;; \ -- esac; --am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; --am__install_max = 40 --am__nobase_strip_setup = \ -- srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` --am__nobase_strip = \ -- for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" --am__nobase_list = $(am__nobase_strip_setup); \ -- for p in $$list; do echo "$$p $$p"; done | \ -- sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ -- $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ -- if (++n[$$2] == $(am__install_max)) \ -- { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ -- END { for (dir in files) print dir, files[dir] }' --am__base_list = \ -- sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ -- sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' --am__installdirs = "$(DESTDIR)$(etapebindir)" --DATA = $(etapebin_DATA) --DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) --ACLOCAL = @ACLOCAL@ --AMTAR = @AMTAR@ --AR = @AR@ --AUTOCONF = @AUTOCONF@ --AUTOHEADER = @AUTOHEADER@ --AUTOMAKE = @AUTOMAKE@ --AWK = @AWK@ --CC = @CC@ --CCDEPMODE = @CCDEPMODE@ --CFLAGS = @CFLAGS@ --CPP = @CPP@ --CPPFLAGS = @CPPFLAGS@ --CURL_CFLAGS = @CURL_CFLAGS@ --CURL_CONFIG = @CURL_CONFIG@ --CURL_LDFLAGS = @CURL_LDFLAGS@ --CURL_LIBS = @CURL_LIBS@ --CYGPATH_W = @CYGPATH_W@ --DEFS = @DEFS@ --DEPDIR = @DEPDIR@ --DSYMUTIL = @DSYMUTIL@ --DUMPBIN = @DUMPBIN@ --ECHO_C = @ECHO_C@ --ECHO_N = @ECHO_N@ --ECHO_T = @ECHO_T@ --EGREP = @EGREP@ --ERL = @ERL@ --ERLC = @ERLC@ --ERLC_FLAGS = @ERLC_FLAGS@ --EXEEXT = @EXEEXT@ --FGREP = @FGREP@ --FLAGS = @FLAGS@ --GREP = @GREP@ --HELP2MAN_EXECUTABLE = @HELP2MAN_EXECUTABLE@ --ICU_CFLAGS = @ICU_CFLAGS@ --ICU_CONFIG = @ICU_CONFIG@ --ICU_CXXFLAGS = @ICU_CXXFLAGS@ --ICU_LIBS = @ICU_LIBS@ --ICU_LOCAL_BIN = @ICU_LOCAL_BIN@ --ICU_LOCAL_CFLAGS = @ICU_LOCAL_CFLAGS@ --ICU_LOCAL_LDFLAGS = @ICU_LOCAL_LDFLAGS@ --INNO_COMPILER_EXECUTABLE = @INNO_COMPILER_EXECUTABLE@ --INSTALL = @INSTALL@ --INSTALL_DATA = @INSTALL_DATA@ --INSTALL_PROGRAM = @INSTALL_PROGRAM@ --INSTALL_SCRIPT = @INSTALL_SCRIPT@ --INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ --JSLIB = @JSLIB@ --JS_LIB_BASE = @JS_LIB_BASE@ --JS_LIB_BINARY = @JS_LIB_BINARY@ --JS_LIB_DIR = @JS_LIB_DIR@ --LD = @LD@ --LDFLAGS = @LDFLAGS@ --LIBOBJS = @LIBOBJS@ --LIBS = @LIBS@ --LIBTOOL = @LIBTOOL@ --LIPO = @LIPO@ --LN_S = @LN_S@ --LTLIBOBJS = @LTLIBOBJS@ --MAKEINFO = @MAKEINFO@ --MKDIR_P = @MKDIR_P@ --NM = @NM@ --NMEDIT = @NMEDIT@ --OBJDUMP = @OBJDUMP@ --OBJEXT = @OBJEXT@ --OTOOL = @OTOOL@ --OTOOL64 = @OTOOL64@ --PACKAGE = @PACKAGE@ --PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ --PACKAGE_NAME = @PACKAGE_NAME@ --PACKAGE_STRING = @PACKAGE_STRING@ --PACKAGE_TARNAME = @PACKAGE_TARNAME@ --PACKAGE_URL = @PACKAGE_URL@ --PACKAGE_VERSION = @PACKAGE_VERSION@ --PATH_SEPARATOR = @PATH_SEPARATOR@ --RANLIB = @RANLIB@ --SED = @SED@ --SET_MAKE = @SET_MAKE@ --SHELL = @SHELL@ --STRIP = @STRIP@ --VERSION = @VERSION@ --abs_builddir = @abs_builddir@ --abs_srcdir = @abs_srcdir@ --abs_top_builddir = @abs_top_builddir@ --abs_top_srcdir = @abs_top_srcdir@ --ac_ct_CC = @ac_ct_CC@ --ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ --am__include = @am__include@ --am__leading_dot = @am__leading_dot@ --am__quote = @am__quote@ --am__tar = @am__tar@ --am__untar = @am__untar@ --bindir = @bindir@ --bug_uri = @bug_uri@ --build = @build@ --build_alias = @build_alias@ --build_cpu = @build_cpu@ --build_os = @build_os@ --build_vendor = @build_vendor@ --builddir = @builddir@ --datadir = @datadir@ --datarootdir = @datarootdir@ --docdir = @docdir@ --dvidir = @dvidir@ --exec_prefix = @exec_prefix@ --host = @host@ --host_alias = @host_alias@ --host_cpu = @host_cpu@ --host_os = @host_os@ --host_vendor = @host_vendor@ --htmldir = @htmldir@ --includedir = @includedir@ --infodir = @infodir@ --initdir = @initdir@ --install_sh = @install_sh@ --launchddir = @launchddir@ --libdir = @libdir@ --libexecdir = @libexecdir@ --localconfdir = @localconfdir@ --localdatadir = @localdatadir@ --localdocdir = @localdocdir@ --localedir = @localedir@ --localerlanglibdir = @localerlanglibdir@ --locallibbindir = @locallibbindir@ --locallibdir = @locallibdir@ --localstatedir = @localstatedir@ --localstatelibdir = @localstatelibdir@ --localstatelogdir = @localstatelogdir@ --localstaterundir = @localstaterundir@ --lt_ECHO = @lt_ECHO@ --mandir = @mandir@ --mkdir_p = @mkdir_p@ --msvc_redist_dir = @msvc_redist_dir@ --msvc_redist_name = @msvc_redist_name@ --oldincludedir = @oldincludedir@ --openssl_bin_dir = @openssl_bin_dir@ --package_author_address = @package_author_address@ --package_author_name = @package_author_name@ --package_identifier = @package_identifier@ --package_name = @package_name@ --package_tarname = @package_tarname@ --pdfdir = @pdfdir@ --prefix = @prefix@ --program_transform_name = @program_transform_name@ --psdir = @psdir@ --sbindir = @sbindir@ --sharedstatedir = @sharedstatedir@ --srcdir = @srcdir@ --sysconfdir = @sysconfdir@ --target_alias = @target_alias@ --top_build_prefix = @top_build_prefix@ --top_builddir = @top_builddir@ --top_srcdir = @top_srcdir@ --version = @version@ --version_major = @version_major@ --version_minor = @version_minor@ --version_release = @version_release@ --version_revision = @version_revision@ --version_stage = @version_stage@ --etapebindir = $(localerlanglibdir)/etap/ebin --etap_file_collection = \ -- etap.erl \ -- etap_application.erl \ -- etap_can.erl \ -- etap_exception.erl \ -- etap_process.erl \ -- etap_report.erl \ -- etap_request.erl \ -- etap_string.erl \ -- etap_web.erl -- --etapebin_make_generated_file_list = \ -- etap.beam \ -- etap_application.beam \ -- etap_can.beam \ -- etap_exception.beam \ -- etap_process.beam \ -- etap_report.beam \ -- etap_request.beam \ -- etap_string.beam \ -- etap_web.beam -- --etapebin_DATA = $(etapebin_make_generated_file_list) --EXTRA_DIST = $(etap_file_collection) --CLEANFILES = $(etapebin_make_generated_file_list) --all: all-am -- --.SUFFIXES: --$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) -- @for dep in $?; do \ -- case '$(am__configure_deps)' in \ -- *$$dep*) \ -- ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ -- && { if test -f $@; then exit 0; else break; fi; }; \ -- exit 1;; \ -- esac; \ -- done; \ -- echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/etap/Makefile'; \ -- $(am__cd) $(top_srcdir) && \ -- $(AUTOMAKE) --foreign src/etap/Makefile --.PRECIOUS: Makefile --Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status -- @case '$?' in \ -- *config.status*) \ -- cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ -- *) \ -- echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ -- cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ -- esac; -- --$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) -- cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh -- --$(top_srcdir)/configure: $(am__configure_deps) -- cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh --$(ACLOCAL_M4): $(am__aclocal_m4_deps) -- cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh --$(am__aclocal_m4_deps): -- --mostlyclean-libtool: -- -rm -f *.lo -- --clean-libtool: -- -rm -rf .libs _libs --install-etapebinDATA: $(etapebin_DATA) -- @$(NORMAL_INSTALL) -- test -z "$(etapebindir)" || $(MKDIR_P) "$(DESTDIR)$(etapebindir)" -- @list='$(etapebin_DATA)'; test -n "$(etapebindir)" || list=; \ -- for p in $$list; do \ -- if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ -- echo "$$d$$p"; \ -- done | $(am__base_list) | \ -- while read files; do \ -- echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(etapebindir)'"; \ -- $(INSTALL_DATA) $$files "$(DESTDIR)$(etapebindir)" || exit $$?; \ -- done -- --uninstall-etapebinDATA: -- @$(NORMAL_UNINSTALL) -- @list='$(etapebin_DATA)'; test -n "$(etapebindir)" || list=; \ -- files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ -- test -n "$$files" || exit 0; \ -- echo " ( cd '$(DESTDIR)$(etapebindir)' && rm -f" $$files ")"; \ -- cd "$(DESTDIR)$(etapebindir)" && rm -f $$files --tags: TAGS --TAGS: -- --ctags: CTAGS --CTAGS: -- -- --distdir: $(DISTFILES) -- @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ -- topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ -- list='$(DISTFILES)'; \ -- dist_files=`for file in $$list; do echo $$file; done | \ -- sed -e "s|^$$srcdirstrip/||;t" \ -- -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ -- case $$dist_files in \ -- */*) $(MKDIR_P) `echo "$$dist_files" | \ -- sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ -- sort -u` ;; \ -- esac; \ -- for file in $$dist_files; do \ -- if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ -- if test -d $$d/$$file; then \ -- dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ -- if test -d "$(distdir)/$$file"; then \ -- find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ -- fi; \ -- if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ -- cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ -- find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ -- fi; \ -- cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ -- else \ -- test -f "$(distdir)/$$file" \ -- || cp -p $$d/$$file "$(distdir)/$$file" \ -- || exit 1; \ -- fi; \ -- done --check-am: all-am --check: check-am --all-am: Makefile $(DATA) --installdirs: -- for dir in "$(DESTDIR)$(etapebindir)"; do \ -- test -z "$$dir" || $(MKDIR_P) "$$dir"; \ -- done --install: install-am --install-exec: install-exec-am --install-data: install-data-am --uninstall: uninstall-am -- --install-am: all-am -- @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am -- --installcheck: installcheck-am --install-strip: -- $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ -- install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ -- `test -z '$(STRIP)' || \ -- echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install --mostlyclean-generic: -- --clean-generic: -- -test -z "$(CLEANFILES)" || rm -f $(CLEANFILES) -- --distclean-generic: -- -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -- -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) -- --maintainer-clean-generic: -- @echo "This command is intended for maintainers to use" -- @echo "it deletes files that may require special tools to rebuild." --clean: clean-am -- --clean-am: clean-generic clean-libtool mostlyclean-am -- --distclean: distclean-am -- -rm -f Makefile --distclean-am: clean-am distclean-generic -- --dvi: dvi-am -- --dvi-am: -- --html: html-am -- --html-am: -- --info: info-am -- --info-am: -- --install-data-am: install-etapebinDATA -- --install-dvi: install-dvi-am -- --install-dvi-am: -- --install-exec-am: -- --install-html: install-html-am -- --install-html-am: -- --install-info: install-info-am -- --install-info-am: -- --install-man: -- --install-pdf: install-pdf-am -- --install-pdf-am: -- --install-ps: install-ps-am -- --install-ps-am: -- --installcheck-am: -- --maintainer-clean: maintainer-clean-am -- -rm -f Makefile --maintainer-clean-am: distclean-am maintainer-clean-generic -- --mostlyclean: mostlyclean-am -- --mostlyclean-am: mostlyclean-generic mostlyclean-libtool -- --pdf: pdf-am -- --pdf-am: -- --ps: ps-am -- --ps-am: -- --uninstall-am: uninstall-etapebinDATA -- --.MAKE: install-am install-strip -- --.PHONY: all all-am check check-am clean clean-generic clean-libtool \ -- distclean distclean-generic distclean-libtool distdir dvi \ -- dvi-am html html-am info info-am install install-am \ -- install-data install-data-am install-dvi install-dvi-am \ -- install-etapebinDATA install-exec install-exec-am install-html \ -- install-html-am install-info install-info-am install-man \ -- install-pdf install-pdf-am install-ps install-ps-am \ -- install-strip installcheck installcheck-am installdirs \ -- maintainer-clean maintainer-clean-generic mostlyclean \ -- mostlyclean-generic mostlyclean-libtool pdf pdf-am ps ps-am \ -- uninstall uninstall-am uninstall-etapebinDATA -- -- --%.beam: %.erl -- $(ERLC) $(ERLC_FLAGS) $< -- --# Tell versions [3.59,3.63) of GNU make to not export all variables. --# Otherwise a system limit (for SysV at least) may be exceeded. --.NOEXPORT: -diff --git a/src/etap/etap.erl b/src/etap/etap.erl -deleted file mode 100644 -index 5ad5dba..0000000 ---- a/src/etap/etap.erl -+++ /dev/null -@@ -1,416 +0,0 @@ --%% Copyright (c) 2008-2009 Nick Gerakines --%% --%% 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. --%% --%% @author Nick Gerakines [http://socklabs.com/] --%% @author Jeremy Wall --%% @version 0.3.4 --%% @copyright 2007-2008 Jeremy Wall, 2008-2009 Nick Gerakines --%% @reference http://testanything.org/wiki/index.php/Main_Page --%% @reference http://en.wikipedia.org/wiki/Test_Anything_Protocol --%% @todo Finish implementing the skip directive. --%% @todo Document the messages handled by this receive loop. --%% @todo Explain in documentation why we use a process to handle test input. --%% @doc etap is a TAP testing module for Erlang components and applications. --%% This module allows developers to test their software using the TAP method. --%% --%%

--%% TAP, the Test Anything Protocol, is a simple text-based interface between --%% testing modules in a test harness. TAP started life as part of the test --%% harness for Perl but now has implementations in C/C++, Python, PHP, Perl --%% and probably others by the time you read this. --%%

--%% --%% The testing process begins by defining a plan using etap:plan/1, running --%% a number of etap tests and then calling eta:end_tests/0. Please refer to --%% the Erlang modules in the t directory of this project for example tests. ---module(etap). ---export([ -- ensure_test_server/0, start_etap_server/0, test_server/1, -- diag/1, diag/2, plan/1, end_tests/0, not_ok/2, ok/2, is/3, isnt/3, -- any/3, none/3, fun_is/3, is_greater/3, skip/1, skip/2, -- ensure_coverage_starts/0, ensure_coverage_ends/0, coverage_report/0, -- datetime/1, skip/3, bail/0, bail/1 --]). ---record(test_state, {planned = 0, count = 0, pass = 0, fail = 0, skip = 0, skip_reason = ""}). ---vsn("0.3.4"). -- --%% @spec plan(N) -> Result --%% N = unknown | skip | {skip, string()} | integer() --%% Result = ok --%% @doc Create a test plan and boot strap the test server. --plan(unknown) -> -- ensure_coverage_starts(), -- ensure_test_server(), -- etap_server ! {self(), plan, unknown}, -- ok; --plan(skip) -> -- io:format("1..0 # skip~n"); --plan({skip, Reason}) -> -- io:format("1..0 # skip ~s~n", [Reason]); --plan(N) when is_integer(N), N > 0 -> -- ensure_coverage_starts(), -- ensure_test_server(), -- etap_server ! {self(), plan, N}, -- ok. -- --%% @spec end_tests() -> ok --%% @doc End the current test plan and output test results. --%% @todo This should probably be done in the test_server process. --end_tests() -> -- ensure_coverage_ends(), -- etap_server ! {self(), state}, -- State = receive X -> X end, -- if -- State#test_state.planned == -1 -> -- io:format("1..~p~n", [State#test_state.count]); -- true -> -- ok -- end, -- case whereis(etap_server) of -- undefined -> ok; -- _ -> etap_server ! done, ok -- end. -- --%% @private --ensure_coverage_starts() -> -- case os:getenv("COVER") of -- false -> ok; -- _ -> -- BeamDir = case os:getenv("COVER_BIN") of false -> "ebin"; X -> X end, -- cover:compile_beam_directory(BeamDir) -- end. -- --%% @private --%% @doc Attempts to write out any collected coverage data to the cover/ --%% directory. This function should not be called externally, but it could be. --ensure_coverage_ends() -> -- case os:getenv("COVER") of -- false -> ok; -- _ -> -- filelib:ensure_dir("cover/"), -- Name = lists:flatten([ -- io_lib:format("~.16b", [X]) || X <- binary_to_list(erlang:md5( -- term_to_binary({make_ref(), now()}) -- )) -- ]), -- cover:export("cover/" ++ Name ++ ".coverdata") -- end. -- --%% @spec coverage_report() -> ok --%% @doc Use the cover module's covreage report builder to create code coverage --%% reports from recently created coverdata files. --coverage_report() -> -- [cover:import(File) || File <- filelib:wildcard("cover/*.coverdata")], -- lists:foreach( -- fun(Mod) -> -- cover:analyse_to_file(Mod, atom_to_list(Mod) ++ "_coverage.txt", []) -- end, -- cover:imported_modules() -- ), -- ok. -- --bail() -> -- bail(""). -- --bail(Reason) -> -- etap_server ! {self(), diag, "Bail out! " ++ Reason}, -- ensure_coverage_ends(), -- etap_server ! done, ok, -- ok. -- -- --%% @spec diag(S) -> ok --%% S = string() --%% @doc Print a debug/status message related to the test suite. --diag(S) -> etap_server ! {self(), diag, "# " ++ S}, ok. -- --%% @spec diag(Format, Data) -> ok --%% Format = atom() | string() | binary() --%% Data = [term()] --%% UnicodeList = [Unicode] --%% Unicode = int() --%% @doc Print a debug/status message related to the test suite. --%% Function arguments are passed through io_lib:format/2. --diag(Format, Data) -> diag(io_lib:format(Format, Data)). -- --%% @spec ok(Expr, Desc) -> Result --%% Expr = true | false --%% Desc = string() --%% Result = true | false --%% @doc Assert that a statement is true. --ok(Expr, Desc) -> mk_tap(Expr == true, Desc). -- --%% @spec not_ok(Expr, Desc) -> Result --%% Expr = true | false --%% Desc = string() --%% Result = true | false --%% @doc Assert that a statement is false. --not_ok(Expr, Desc) -> mk_tap(Expr == false, Desc). -- --%% @spec is(Got, Expected, Desc) -> Result --%% Got = any() --%% Expected = any() --%% Desc = string() --%% Result = true | false --%% @doc Assert that two values are the same. --is(Got, Expected, Desc) -> -- case mk_tap(Got == Expected, Desc) of -- false -> -- etap_server ! {self(), diag, " ---"}, -- etap_server ! {self(), diag, io_lib:format(" description: ~p", [Desc])}, -- etap_server ! {self(), diag, io_lib:format(" found: ~p", [Got])}, -- etap_server ! {self(), diag, io_lib:format(" wanted: ~p", [Expected])}, -- etap_server ! {self(), diag, " ..."}, -- false; -- true -> true -- end. -- --%% @spec isnt(Got, Expected, Desc) -> Result --%% Got = any() --%% Expected = any() --%% Desc = string() --%% Result = true | false --%% @doc Assert that two values are not the same. --isnt(Got, Expected, Desc) -> mk_tap(Got /= Expected, Desc). -- --%% @spec is_greater(ValueA, ValueB, Desc) -> Result --%% ValueA = number() --%% ValueB = number() --%% Desc = string() --%% Result = true | false --%% @doc Assert that an integer is greater than another. --is_greater(ValueA, ValueB, Desc) when is_integer(ValueA), is_integer(ValueB) -> -- mk_tap(ValueA > ValueB, Desc). -- --%% @spec any(Got, Items, Desc) -> Result --%% Got = any() --%% Items = [any()] --%% Desc = string() --%% Result = true | false --%% @doc Assert that an item is in a list. --any(Got, Items, Desc) -> -- is(lists:member(Got, Items), true, Desc). -- --%% @spec none(Got, Items, Desc) -> Result --%% Got = any() --%% Items = [any()] --%% Desc = string() --%% Result = true | false --%% @doc Assert that an item is not in a list. --none(Got, Items, Desc) -> -- is(lists:member(Got, Items), false, Desc). -- --%% @spec fun_is(Fun, Expected, Desc) -> Result --%% Fun = function() --%% Expected = any() --%% Desc = string() --%% Result = true | false --%% @doc Use an anonymous function to assert a pattern match. --fun_is(Fun, Expected, Desc) when is_function(Fun) -> -- is(Fun(Expected), true, Desc). -- --%% @equiv skip(TestFun, "") --skip(TestFun) when is_function(TestFun) -> -- skip(TestFun, ""). -- --%% @spec skip(TestFun, Reason) -> ok --%% TestFun = function() --%% Reason = string() --%% @doc Skip a test. --skip(TestFun, Reason) when is_function(TestFun), is_list(Reason) -> -- begin_skip(Reason), -- catch TestFun(), -- end_skip(), -- ok. -- --%% @spec skip(Q, TestFun, Reason) -> ok --%% Q = true | false | function() --%% TestFun = function() --%% Reason = string() --%% @doc Skips a test conditionally. The first argument to this function can --%% either be the 'true' or 'false' atoms or a function that returns 'true' or --%% 'false'. --skip(QFun, TestFun, Reason) when is_function(QFun), is_function(TestFun), is_list(Reason) -> -- case QFun() of -- true -> begin_skip(Reason), TestFun(), end_skip(); -- _ -> TestFun() -- end, -- ok; -- --skip(Q, TestFun, Reason) when is_function(TestFun), is_list(Reason), Q == true -> -- begin_skip(Reason), -- TestFun(), -- end_skip(), -- ok; -- --skip(_, TestFun, Reason) when is_function(TestFun), is_list(Reason) -> -- TestFun(), -- ok. -- --%% @private --begin_skip(Reason) -> -- etap_server ! {self(), begin_skip, Reason}. -- --%% @private --end_skip() -> -- etap_server ! {self(), end_skip}. -- --% --- --% Internal / Private functions -- --%% @private --%% @doc Start the etap_server process if it is not running already. --ensure_test_server() -> -- case whereis(etap_server) of -- undefined -> -- proc_lib:start(?MODULE, start_etap_server,[]); -- _ -> -- diag("The test server is already running.") -- end. -- --%% @private --%% @doc Start the etap_server loop and register itself as the etap_server --%% process. --start_etap_server() -> -- catch register(etap_server, self()), -- proc_lib:init_ack(ok), -- etap:test_server(#test_state{ -- planned = 0, -- count = 0, -- pass = 0, -- fail = 0, -- skip = 0, -- skip_reason = "" -- }). -- -- --%% @private --%% @doc The main etap_server receive/run loop. The etap_server receive loop --%% responds to seven messages apperatining to failure or passing of tests. --%% It is also used to initiate the testing process with the {_, plan, _} --%% message that clears the current test state. --test_server(State) -> -- NewState = receive -- {_From, plan, unknown} -> -- io:format("# Current time local ~s~n", [datetime(erlang:localtime())]), -- io:format("# Using etap version ~p~n", [ proplists:get_value(vsn, proplists:get_value(attributes, etap:module_info())) ]), -- State#test_state{ -- planned = -1, -- count = 0, -- pass = 0, -- fail = 0, -- skip = 0, -- skip_reason = "" -- }; -- {_From, plan, N} -> -- io:format("# Current time local ~s~n", [datetime(erlang:localtime())]), -- io:format("# Using etap version ~p~n", [ proplists:get_value(vsn, proplists:get_value(attributes, etap:module_info())) ]), -- io:format("1..~p~n", [N]), -- State#test_state{ -- planned = N, -- count = 0, -- pass = 0, -- fail = 0, -- skip = 0, -- skip_reason = "" -- }; -- {_From, begin_skip, Reason} -> -- State#test_state{ -- skip = 1, -- skip_reason = Reason -- }; -- {_From, end_skip} -> -- State#test_state{ -- skip = 0, -- skip_reason = "" -- }; -- {_From, pass, Desc} -> -- FullMessage = skip_diag( -- " - " ++ Desc, -- State#test_state.skip, -- State#test_state.skip_reason -- ), -- io:format("ok ~p ~s~n", [State#test_state.count + 1, FullMessage]), -- State#test_state{ -- count = State#test_state.count + 1, -- pass = State#test_state.pass + 1 -- }; -- -- {_From, fail, Desc} -> -- FullMessage = skip_diag( -- " - " ++ Desc, -- State#test_state.skip, -- State#test_state.skip_reason -- ), -- io:format("not ok ~p ~s~n", [State#test_state.count + 1, FullMessage]), -- State#test_state{ -- count = State#test_state.count + 1, -- fail = State#test_state.fail + 1 -- }; -- {From, state} -> -- From ! State, -- State; -- {_From, diag, Message} -> -- io:format("~s~n", [Message]), -- State; -- {From, count} -> -- From ! State#test_state.count, -- State; -- {From, is_skip} -> -- From ! State#test_state.skip, -- State; -- done -> -- exit(normal) -- end, -- test_server(NewState). -- --%% @private --%% @doc Process the result of a test and send it to the etap_server process. --mk_tap(Result, Desc) -> -- IsSkip = lib:sendw(etap_server, is_skip), -- case [IsSkip, Result] of -- [_, true] -> -- etap_server ! {self(), pass, Desc}, -- true; -- [1, _] -> -- etap_server ! {self(), pass, Desc}, -- true; -- _ -> -- etap_server ! {self(), fail, Desc}, -- false -- end. -- --%% @private --%% @doc Format a date/time string. --datetime(DateTime) -> -- {{Year, Month, Day}, {Hour, Min, Sec}} = DateTime, -- io_lib:format("~4.10.0B-~2.10.0B-~2.10.0B ~2.10.0B:~2.10.0B:~2.10.0B", [Year, Month, Day, Hour, Min, Sec]). -- --%% @private --%% @doc Craft an output message taking skip/todo into consideration. --skip_diag(Message, 0, _) -> -- Message; --skip_diag(_Message, 1, "") -> -- " # SKIP"; --skip_diag(_Message, 1, Reason) -> -- " # SKIP : " ++ Reason. -diff --git a/src/etap/etap_application.erl b/src/etap/etap_application.erl -deleted file mode 100644 -index 98b5275..0000000 ---- a/src/etap/etap_application.erl -+++ /dev/null -@@ -1,72 +0,0 @@ --%% Copyright (c) 2008-2009 Nick Gerakines --%% --%% 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. --%% --%% @author Nick Gerakines [http://socklabs.com/] --%% @copyright 2008 Nick Gerakines --%% @reference http://testanything.org/wiki/index.php/Main_Page --%% @reference http://en.wikipedia.org/wiki/Test_Anything_Protocol --%% @todo Explain in documentation why we use a process to handle test input. --%% @todo Add test to verify the number of members in a pg2 group. --%% @doc Provide test functionality to the application and related behaviors. ---module(etap_application). ---export([ -- start_ok/2, ensure_loaded/3, load_ok/2, -- pg2_group_exists/2, pg2_group_doesntexist/2 --]). -- --%% @spec load_ok(string(), string()) -> true | false --%% @doc Assert that an application can be loaded successfully. --load_ok(AppName, Desc) -> -- etap:ok(application:load(AppName) == ok, Desc). -- --%% @spec start_ok(string(), string()) -> true | false --%% @doc Assert that an application can be started successfully. --start_ok(AppName, Desc) -> -- etap:ok(application:start(AppName) == ok, Desc). -- --%% @spec ensure_loaded(string(), string(), string()) -> true | false --%% @doc Assert that an application has been loaded successfully. --ensure_loaded(AppName, AppVsn, Desc) -> -- etap:any( -- fun(Match) -> case Match of {AppName, _, AppVsn} -> true; _ -> false end end, -- application:loaded_applications(), -- Desc -- ). -- --%% @spec pg2_group_exists(string(), string()) -> true | false --%% @doc Assert that a pg2 group exists. --pg2_group_exists(GroupName, Desc) -> -- etap:any( -- fun(Match) -> Match == GroupName end, -- pg2:which_groups(), -- Desc -- ). -- --%% @spec pg2_group_doesntexist(string(), string()) -> true | false --%% @doc Assert that a pg2 group does not exists. --pg2_group_doesntexist(GroupName, Desc) -> -- etap:none( -- fun(Match) -> Match == GroupName end, -- pg2:which_groups(), -- Desc -- ). -diff --git a/src/etap/etap_can.erl b/src/etap/etap_can.erl -deleted file mode 100644 -index 552b717..0000000 ---- a/src/etap/etap_can.erl -+++ /dev/null -@@ -1,79 +0,0 @@ --%% Copyright (c) 2008-2009 Nick Gerakines --%% --%% 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. --%% --%% @reference http://testanything.org/wiki/index.php/Main_Page --%% @reference http://en.wikipedia.org/wiki/Test_Anything_Protocol --%% @doc Provide test functionality modules ---module(etap_can). -- ---export([ -- loaded_ok/2, can_ok/2, can_ok/3, -- has_attrib/2, is_attrib/3, is_behaviour/2 --]). -- --%% @spec loaded_ok(atom(), string()) -> true | false --%% @doc Assert that a module has been loaded successfully. --loaded_ok(M, Desc) when is_atom(M) -> -- etap:fun_is(fun({module, _}) -> true; (_) -> false end, code:load_file(M), Desc). -- --%% @spec can_ok(atom(), atom()) -> true | false --%% @doc Assert that a module exports a given function. --can_ok(M, F) when is_atom(M), is_atom(F) -> -- Matches = [X || {X, _} <- M:module_info(exports), X == F], -- etap:ok(Matches > 0, lists:concat([M, " can ", F])). -- --%% @spec can_ok(atom(), atom(), integer()) -> true | false --%% @doc Assert that a module exports a given function with a given arity. --can_ok(M, F, A) when is_atom(M); is_atom(F), is_number(A) -> -- Matches = [X || X <- M:module_info(exports), X == {F, A}], -- etap:ok(Matches > 0, lists:concat([M, " can ", F, "/", A])). -- --%% @spec has_attrib(M, A) -> true | false --%% M = atom() --%% A = atom() --%% @doc Asserts that a module has a given attribute. --has_attrib(M, A) when is_atom(M), is_atom(A) -> -- etap:isnt( -- proplists:get_value(A, M:module_info(attributes), 'asdlkjasdlkads'), -- 'asdlkjasdlkads', -- lists:concat([M, " has attribute ", A]) -- ). -- --%% @spec has_attrib(M, A. V) -> true | false --%% M = atom() --%% A = atom() --%% V = any() --%% @doc Asserts that a module has a given attribute with a given value. --is_attrib(M, A, V) when is_atom(M) andalso is_atom(A) -> -- etap:is( -- proplists:get_value(A, M:module_info(attributes)), -- [V], -- lists:concat([M, "'s ", A, " is ", V]) -- ). -- --%% @spec is_behavior(M, B) -> true | false --%% M = atom() --%% B = atom() --%% @doc Asserts that a given module has a specific behavior. --is_behaviour(M, B) when is_atom(M) andalso is_atom(B) -> -- is_attrib(M, behaviour, B). -diff --git a/src/etap/etap_exception.erl b/src/etap/etap_exception.erl -deleted file mode 100644 -index ba66072..0000000 ---- a/src/etap/etap_exception.erl -+++ /dev/null -@@ -1,66 +0,0 @@ --%% Copyright (c) 2008-2009 Nick Gerakines --%% --%% 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. --%% --%% @reference http://testanything.org/wiki/index.php/Main_Page --%% @reference http://en.wikipedia.org/wiki/Test_Anything_Protocol --%% @doc Adds exception based testing to the etap suite. ---module(etap_exception). -- ---export([dies_ok/2, lives_ok/2, throws_ok/3]). -- --% --- --% External / Public functions -- --%% @doc Assert that an exception is raised when running a given function. --dies_ok(F, Desc) -> -- case (catch F()) of -- {'EXIT', _} -> etap:ok(true, Desc); -- _ -> etap:ok(false, Desc) -- end. -- --%% @doc Assert that an exception is not raised when running a given function. --lives_ok(F, Desc) -> -- etap:is(try_this(F), success, Desc). -- --%% @doc Assert that the exception thrown by a function matches the given exception. --throws_ok(F, Exception, Desc) -> -- try F() of -- _ -> etap:ok(nok, Desc) -- catch -- _:E -> -- etap:is(E, Exception, Desc) -- end. -- --% --- --% Internal / Private functions -- --%% @private --%% @doc Run a function and catch any exceptions. --try_this(F) when is_function(F, 0) -> -- try F() of -- _ -> success -- catch -- throw:E -> {throw, E}; -- error:E -> {error, E}; -- exit:E -> {exit, E} -- end. -diff --git a/src/etap/etap_process.erl b/src/etap/etap_process.erl -deleted file mode 100644 -index 69f5ba0..0000000 ---- a/src/etap/etap_process.erl -+++ /dev/null -@@ -1,42 +0,0 @@ --%% Copyright (c) 2008-2009 Nick Gerakines --%% --%% 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. --%% --%% @doc Adds process/pid testing to the etap suite. ---module(etap_process). -- ---export([is_pid/2, is_alive/2, is_mfa/3]). -- --% --- --% External / Public functions -- --%% @doc Assert that a given variable is a pid. --is_pid(Pid, Desc) when is_pid(Pid) -> etap:ok(true, Desc); --is_pid(_, Desc) -> etap:ok(false, Desc). -- --%% @doc Assert that a given process/pid is alive. --is_alive(Pid, Desc) -> -- etap:ok(erlang:is_process_alive(Pid), Desc). -- --%% @doc Assert that the current function of a pid is a given {M, F, A} tuple. --is_mfa(Pid, MFA, Desc) -> -- etap:is({current_function, MFA}, erlang:process_info(Pid, current_function), Desc). -diff --git a/src/etap/etap_report.erl b/src/etap/etap_report.erl -deleted file mode 100644 -index 6d692fb..0000000 ---- a/src/etap/etap_report.erl -+++ /dev/null -@@ -1,343 +0,0 @@ --%% Copyright (c) 2008-2009 Nick Gerakines --%% --%% 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. --%% --%% @doc A module for creating nice looking code coverage reports. ---module(etap_report). ---export([create/0]). -- --%% @spec create() -> ok --%% @doc Create html code coverage reports for each module that code coverage --%% data exists for. --create() -> -- [cover:import(File) || File <- filelib:wildcard("cover/*.coverdata")], -- Modules = lists:foldl( -- fun(Module, Acc) -> -- [{Module, file_report(Module)} | Acc] -- end, -- [], -- cover:imported_modules() -- ), -- index(Modules). -- --%% @private --index(Modules) -> -- {ok, IndexFD} = file:open("cover/index.html", [write]), -- io:format(IndexFD, "", []), -- io:format(IndexFD, "", []), -- lists:foldl( -- fun({Module, {Good, Bad, Source}}, LastRow) -> -- case {Good + Bad, Source} of -- {0, _} -> LastRow; -- {_, none} -> LastRow; -- _ -> -- CovPer = round((Good / (Good + Bad)) * 100), -- UnCovPer = round((Bad / (Good + Bad)) * 100), -- RowClass = case LastRow of 1 -> "odd"; _ -> "even" end, -- io:format(IndexFD, "
", [RowClass]), -- io:format(IndexFD, "~s", [atom_to_list(Module) ++ "_report.html", atom_to_list(Module)]), -- io:format(IndexFD, " -- -- -- -- --
~p%  -- -- --
--
-- ", [CovPer, CovPer, UnCovPer]), -- io:format(IndexFD, "
", []), -- case LastRow of -- 1 -> 0; -- 0 -> 1 -- end -- end -- end, -- 0, -- lists:sort(Modules) -- ), -- {TotalGood, TotalBad} = lists:foldl( -- fun({_, {Good, Bad, Source}}, {TGood, TBad}) -> -- case Source of none -> {TGood, TBad}; _ -> {TGood + Good, TBad + Bad} end -- end, -- {0, 0}, -- Modules -- ), -- io:format(IndexFD, "

Generated on ~s.

~n", [etap:datetime({date(), time()})]), -- case TotalGood + TotalBad of -- 0 -> ok; -- _ -> -- TotalCovPer = round((TotalGood / (TotalGood + TotalBad)) * 100), -- TotalUnCovPer = round((TotalBad / (TotalGood + TotalBad)) * 100), -- io:format(IndexFD, "
", []), -- io:format(IndexFD, "Total -- -- -- -- --
~p%  -- -- --
--
-- ", [TotalCovPer, TotalCovPer, TotalUnCovPer]), -- io:format(IndexFD, "
", []) -- end, -- io:format(IndexFD, "", []), -- file:close(IndexFD), -- ok. -- --%% @private --file_report(Module) -> -- {ok, Data} = cover:analyse(Module, calls, line), -- Source = find_source(Module), -- {Good, Bad} = collect_coverage(Data, {0, 0}), -- case {Source, Good + Bad} of -- {none, _} -> ok; -- {_, 0} -> ok; -- _ -> -- {ok, SourceFD} = file:open(Source, [read]), -- {ok, WriteFD} = file:open("cover/" ++ atom_to_list(Module) ++ "_report.html", [write]), -- io:format(WriteFD, "~s", [header(Module, Good, Bad)]), -- output_lines(Data, WriteFD, SourceFD, 1), -- io:format(WriteFD, "~s", [footer()]), -- file:close(WriteFD), -- file:close(SourceFD), -- ok -- end, -- {Good, Bad, Source}. -- --%% @private --collect_coverage([], Acc) -> Acc; --collect_coverage([{{_, _}, 0} | Data], {Good, Bad}) -> -- collect_coverage(Data, {Good, Bad + 1}); --collect_coverage([_ | Data], {Good, Bad}) -> -- collect_coverage(Data, {Good + 1, Bad}). -- --%% @private --output_lines(Data, WriteFD, SourceFD, LineNumber) -> -- {Match, NextData} = datas_match(Data, LineNumber), -- case io:get_line(SourceFD, '') of -- eof -> ok; -- Line = "%% @todo" ++ _ -> -- io:format(WriteFD, "~s", [out_line(LineNumber, highlight, Line)]), -- output_lines(NextData, WriteFD, SourceFD, LineNumber + 1); -- Line = "% " ++ _ -> -- io:format(WriteFD, "~s", [out_line(LineNumber, none, Line)]), -- output_lines(NextData, WriteFD, SourceFD, LineNumber + 1); -- Line -> -- case Match of -- {true, CC} -> -- io:format(WriteFD, "~s", [out_line(LineNumber, CC, Line)]), -- output_lines(NextData, WriteFD, SourceFD, LineNumber + 1); -- false -> -- io:format(WriteFD, "~s", [out_line(LineNumber, none, Line)]), -- output_lines(NextData, WriteFD, SourceFD, LineNumber + 1) -- end -- end. -- --%% @private --out_line(Number, none, Line) -> -- PadNu = string:right(integer_to_list(Number), 5, $.), -- io_lib:format("~s ~s", [Number, PadNu, Line]); --out_line(Number, highlight, Line) -> -- PadNu = string:right(integer_to_list(Number), 5, $.), -- io_lib:format("~s ~s", [Number, PadNu, Line]); --out_line(Number, 0, Line) -> -- PadNu = string:right(integer_to_list(Number), 5, $.), -- io_lib:format("~s ~s", [Number, PadNu, Line]); --out_line(Number, _, Line) -> -- PadNu = string:right(integer_to_list(Number), 5, $.), -- io_lib:format("~s ~s", [Number, PadNu, Line]). -- --%% @private --datas_match([], _) -> {false, []}; --datas_match([{{_, Line}, CC} | Datas], LineNumber) when Line == LineNumber -> {{true, CC}, Datas}; --datas_match(Data, _) -> {false, Data}. -- --%% @private --find_source(Module) when is_atom(Module) -> -- Root = filename:rootname(Module), -- Dir = filename:dirname(Root), -- XDir = case os:getenv("SRC") of false -> "src"; X -> X end, -- find_source([ -- filename:join([Dir, Root ++ ".erl"]), -- filename:join([Dir, "..", "src", Root ++ ".erl"]), -- filename:join([Dir, "src", Root ++ ".erl"]), -- filename:join([Dir, "elibs", Root ++ ".erl"]), -- filename:join([Dir, "..", "elibs", Root ++ ".erl"]), -- filename:join([Dir, XDir, Root ++ ".erl"]) -- ]); --find_source([]) -> none; --find_source([Test | Tests]) -> -- case filelib:is_file(Test) of -- true -> Test; -- false -> find_source(Tests) -- end. -- --%% @private --header(Module, Good, Bad) -> -- io:format("Good ~p~n", [Good]), -- io:format("Bad ~p~n", [Bad]), -- CovPer = round((Good / (Good + Bad)) * 100), -- UnCovPer = round((Bad / (Good + Bad)) * 100), -- io:format("CovPer ~p~n", [CovPer]), -- io_lib:format(" -- -- -- ~s - C0 code coverage information -- -- -- -- --

C0 code coverage information

--

Generated on ~s with etap 0.3.4. --

-- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- --
NameTotal linesLines of codeTotal coverageCode coverage
-- ~s -- -- ?? -- -- ?? -- -- ?? -- -- -- -- -- --
~p%  -- -- --
--
--
", [Module, etap:datetime({date(), time()}), atom_to_list(Module) ++ "_report.html", Module, CovPer, CovPer, UnCovPer]).
--
--%% @private
--footer() ->
--    "

Generated using etap 0.3.4.

-- -- -- ". -diff --git a/src/etap/etap_request.erl b/src/etap/etap_request.erl -deleted file mode 100644 -index 9fd23ac..0000000 ---- a/src/etap/etap_request.erl -+++ /dev/null -@@ -1,89 +0,0 @@ --%% Copyright (c) 2008-2009 Nick Gerakines --%% --%% 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. --%% --%% @doc Provides test functionality against a specific web request. Many of --%% the exported methods can be used to build your own more complex tests. ---module(etap_request, [Method, Url, InHeaders, InBody, Status, OutHeaders, OutBody]). -- ---export([status_is/2]). -- ---export([ -- method/0, url/0, status/0, status_code/0, status_line/0, rheaders/0, -- has_rheader/1, rheader/1, rbody/0, header_is/3, body_is/2, -- body_has_string/2 --]). -- --% --- --% Tests -- --%% @doc Assert that response status code is the given status code. --status_is(Code, Desc) -> -- etap:is(status_code(), Code, Desc). -- --header_is(Name, Value, Desc) -> -- etap:is(rheader(Name), Value, Desc). -- --body_is(Value, Desc) -> -- etap:is(rbody(), Value, Desc). -- --body_has_string(String, Desc) when is_list(OutBody), is_list(String) -> -- etap_string:contains_ok(OutBody, String, Desc). -- --% --- --% Accessor functions -- --%% @doc Access a request's method. --method() -> Method. -- --%% @doc Access a request's URL. --url() -> Url. -- --%% @doc Access a request's status. --status() -> Status. -- --%% @doc Access a request's status code. --status_code() -> -- {_, Code, _} = Status, -- Code. -- --%% @doc Access a request's status line. --status_line() -> -- {_, _, Line} = Status, -- Line. -- --%% @doc Access a request's headers. --rheaders() -> OutHeaders. -- --%% @doc Dertermine if a specific request header exists. --has_rheader(Key) -> -- lists:keymember(Key, 1, OutHeaders). -- --%% @doc Return a specific request header. --rheader(Key) -> -- case lists:keysearch(Key, 1, OutHeaders) of -- false -> undefined; -- {value, {Key, Value}} -> Value -- end. -- --%% @doc Access the request's body. --rbody() -> OutBody. -diff --git a/src/etap/etap_string.erl b/src/etap/etap_string.erl -deleted file mode 100644 -index 67aa3d5..0000000 ---- a/src/etap/etap_string.erl -+++ /dev/null -@@ -1,47 +0,0 @@ --%% Copyright (c) 2008-2009 Nick Gerakines --%% --%% 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. --%% --%% @author Nick Gerakines [http://socklabs.com/] --%% @copyright 2008 Nick Gerakines --%% @doc Provide testing functionality for strings. ---module(etap_string). -- ---export([contains_ok/3, is_before/4]). -- --%% @spec contains_ok(string(), string(), string()) -> true | false --%% @doc Assert that a string is contained in another string. --contains_ok(Source, String, Desc) -> -- etap:isnt( -- string:str(Source, String), -- 0, -- Desc -- ). -- --%% @spec is_before(string(), string(), string(), string()) -> true | false --%% @doc Assert that a string comes before another string within a larger body. --is_before(Source, StringA, StringB, Desc) -> -- etap:is_greater( -- string:str(Source, StringB), -- string:str(Source, StringA), -- Desc -- ). -diff --git a/src/etap/etap_web.erl b/src/etap/etap_web.erl -deleted file mode 100644 -index fb7aee1..0000000 ---- a/src/etap/etap_web.erl -+++ /dev/null -@@ -1,65 +0,0 @@ --%% Copyright (c) 2008-2009 Nick Gerakines --%% --%% 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. --%% --%% @author Nick Gerakines [http://socklabs.com/] --%% @copyright 2008 Nick Gerakines --%% @todo Support cookies. --%% @doc Provide testing functionality for web requests. ---module(etap_web). -- ---export([simple_200/2, simple_404/2, build_request/4]). -- --%% @doc Fetch a url and verify that it returned a 200 status. --simple_200(Url, Desc) -> -- Request = build_request(get, Url, [], []), -- Request:status_is(200, Desc). -- --%% @doc Fetch a url and verify that it returned a 404 status. --simple_404(Url, Desc) -> -- Request = build_request(get, Url, [], []), -- Request:status_is(404, Desc). -- --%% @doc Create and return a request structure. --build_request(Method, Url, Headers, Body) -- when Method==options;Method==get;Method==head;Method==delete;Method==trace -> -- try http:request(Method, {Url, Headers}, [{autoredirect, false}], []) of -- {ok, {OutStatus, OutHeaders, OutBody}} -> -- etap_request:new(Method, Url, Headers, Body, OutStatus, OutHeaders, OutBody); -- _ -> error -- catch -- _:_ -> error -- end; -- --%% @doc Create and return a request structure. --build_request(Method, Url, Headers, Body) when Method == post; Method == put -> -- ContentType = case lists:keysearch("Content-Type", 1, Headers) of -- {value, {"Content-Type", X}} -> X; -- _ -> [] -- end, -- try http:request(Method, {Url, Headers, ContentType, Body}, [{autoredirect, false}], []) of -- {ok, {OutStatus, OutHeaders, OutBody}} -> -- etap_request:new(Method, Url, Headers, Body, OutStatus, OutHeaders, OutBody); -- _ -> error -- catch -- _:_ -> error -- end. -diff --git a/test/etap/test_util.erl.in b/test/etap/test_util.erl.in -index 79b0417..c57d7a8 100644 ---- a/test/etap/test_util.erl.in -+++ b/test/etap/test_util.erl.in -@@ -22,7 +22,7 @@ builddir() -> - "@abs_top_builddir@". - - init_code_path() -> -- Paths = ["etap", "couchdb", "ibrowse", "mochiweb"], -+ Paths = ["couchdb", "ibrowse", "mochiweb"], - lists:foreach(fun(Name) -> - code:add_pathz(filename:join([builddir(), "src", Name])) - end, Paths). --- -1.7.1.1 - diff --git a/couchdb0.11.1-0005-Remove-bundled-mochiweb-library.patch b/couchdb0.11.1-0005-Remove-bundled-mochiweb-library.patch deleted file mode 100644 index 5197801..0000000 --- a/couchdb0.11.1-0005-Remove-bundled-mochiweb-library.patch +++ /dev/null @@ -1,7750 +0,0 @@ -From d50c76c16a6e1a2fa55cfdbaa4fc4211d3d8e495 Mon Sep 17 00:00:00 2001 -From: Peter Lemenkov -Date: Wed, 14 Jul 2010 18:01:45 +0400 -Subject: [PATCH 5/9] Remove bundled mochiweb library - ---- - configure | 3 - - configure.ac | 1 - - src/Makefile.am | 2 +- - src/Makefile.in | 2 +- - src/mochiweb/Makefile.am | 80 --- - src/mochiweb/Makefile.in | 515 ------------------ - src/mochiweb/mochifmt.erl | 426 --------------- - src/mochiweb/mochifmt_records.erl | 30 - - src/mochiweb/mochifmt_std.erl | 23 - - src/mochiweb/mochihex.erl | 75 --- - src/mochiweb/mochijson.erl | 528 ------------------ - src/mochiweb/mochijson2.erl | 660 ----------------------- - src/mochiweb/mochinum.erl | 289 ---------- - src/mochiweb/mochiweb.app.in | 32 -- - src/mochiweb/mochiweb.erl | 110 ---- - src/mochiweb/mochiweb_app.erl | 20 - - src/mochiweb/mochiweb_charref.erl | 295 ---------- - src/mochiweb/mochiweb_cookies.erl | 257 --------- - src/mochiweb/mochiweb_echo.erl | 31 -- - src/mochiweb/mochiweb_headers.erl | 251 --------- - src/mochiweb/mochiweb_html.erl | 893 ------------------------------- - src/mochiweb/mochiweb_http.erl | 152 ------ - src/mochiweb/mochiweb_multipart.erl | 530 ------------------ - src/mochiweb/mochiweb_request.erl | 867 ------------------------------ - src/mochiweb/mochiweb_response.erl | 56 -- - src/mochiweb/mochiweb_skel.erl | 73 --- - src/mochiweb/mochiweb_socket_server.erl | 248 --------- - src/mochiweb/mochiweb_sup.erl | 34 -- - src/mochiweb/mochiweb_util.erl | 859 ----------------------------- - src/mochiweb/reloader.erl | 123 ----- - test/etap/test_util.erl.in | 2 +- - 31 files changed, 3 insertions(+), 7464 deletions(-) - delete mode 100644 src/mochiweb/Makefile.am - delete mode 100644 src/mochiweb/Makefile.in - delete mode 100644 src/mochiweb/mochifmt.erl - delete mode 100644 src/mochiweb/mochifmt_records.erl - delete mode 100644 src/mochiweb/mochifmt_std.erl - delete mode 100644 src/mochiweb/mochihex.erl - delete mode 100644 src/mochiweb/mochijson.erl - delete mode 100644 src/mochiweb/mochijson2.erl - delete mode 100644 src/mochiweb/mochinum.erl - delete mode 100644 src/mochiweb/mochiweb.app.in - delete mode 100644 src/mochiweb/mochiweb.erl - delete mode 100644 src/mochiweb/mochiweb_app.erl - delete mode 100644 src/mochiweb/mochiweb_charref.erl - delete mode 100644 src/mochiweb/mochiweb_cookies.erl - delete mode 100644 src/mochiweb/mochiweb_echo.erl - delete mode 100644 src/mochiweb/mochiweb_headers.erl - delete mode 100644 src/mochiweb/mochiweb_html.erl - delete mode 100644 src/mochiweb/mochiweb_http.erl - delete mode 100644 src/mochiweb/mochiweb_multipart.erl - delete mode 100644 src/mochiweb/mochiweb_request.erl - delete mode 100644 src/mochiweb/mochiweb_response.erl - delete mode 100644 src/mochiweb/mochiweb_skel.erl - delete mode 100644 src/mochiweb/mochiweb_socket_server.erl - delete mode 100644 src/mochiweb/mochiweb_sup.erl - delete mode 100644 src/mochiweb/mochiweb_util.erl - delete mode 100644 src/mochiweb/reloader.erl - -diff --git a/configure b/configure -index 68c8db9..b71f364 100755 ---- a/configure -+++ b/configure -@@ -12263,8 +12263,6 @@ ac_config_files="$ac_config_files src/couchdb/priv/Makefile" - - ac_config_files="$ac_config_files src/ibrowse/Makefile" - --ac_config_files="$ac_config_files src/mochiweb/Makefile" -- - ac_config_files="$ac_config_files test/Makefile" - - ac_config_files="$ac_config_files test/bench/Makefile" -@@ -13287,7 +13285,6 @@ do - "src/couchdb/Makefile") CONFIG_FILES="$CONFIG_FILES src/couchdb/Makefile" ;; - "src/couchdb/priv/Makefile") CONFIG_FILES="$CONFIG_FILES src/couchdb/priv/Makefile" ;; - "src/ibrowse/Makefile") CONFIG_FILES="$CONFIG_FILES src/ibrowse/Makefile" ;; -- "src/mochiweb/Makefile") CONFIG_FILES="$CONFIG_FILES src/mochiweb/Makefile" ;; - "test/Makefile") CONFIG_FILES="$CONFIG_FILES test/Makefile" ;; - "test/bench/Makefile") CONFIG_FILES="$CONFIG_FILES test/bench/Makefile" ;; - "test/etap/Makefile") CONFIG_FILES="$CONFIG_FILES test/etap/Makefile" ;; -diff --git a/configure.ac b/configure.ac -index e9375be..b5a19ef 100644 ---- a/configure.ac -+++ b/configure.ac -@@ -388,7 +388,6 @@ AC_CONFIG_FILES([src/couchdb/couch.app.tpl]) - AC_CONFIG_FILES([src/couchdb/Makefile]) - AC_CONFIG_FILES([src/couchdb/priv/Makefile]) - AC_CONFIG_FILES([src/ibrowse/Makefile]) --AC_CONFIG_FILES([src/mochiweb/Makefile]) - AC_CONFIG_FILES([test/Makefile]) - AC_CONFIG_FILES([test/bench/Makefile]) - AC_CONFIG_FILES([test/etap/Makefile]) -diff --git a/src/Makefile.am b/src/Makefile.am -index 19a5d20..5a6646f 100644 ---- a/src/Makefile.am -+++ b/src/Makefile.am -@@ -10,4 +10,4 @@ - ## License for the specific language governing permissions and limitations under - ## the License. - --SUBDIRS = couchdb ibrowse mochiweb -+SUBDIRS = couchdb ibrowse -diff --git a/src/Makefile.in b/src/Makefile.in -index ae1b828..2d11fc8 100644 ---- a/src/Makefile.in -+++ b/src/Makefile.in -@@ -248,7 +248,7 @@ version_minor = @version_minor@ - version_release = @version_release@ - version_revision = @version_revision@ - version_stage = @version_stage@ --SUBDIRS = couchdb ibrowse mochiweb -+SUBDIRS = couchdb ibrowse - all: all-recursive - - .SUFFIXES: -diff --git a/src/mochiweb/Makefile.am b/src/mochiweb/Makefile.am -deleted file mode 100644 -index c191abf..0000000 ---- a/src/mochiweb/Makefile.am -+++ /dev/null -@@ -1,80 +0,0 @@ --## 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. -- --mochiwebebindir = $(localerlanglibdir)/mochiweb-r113/ebin -- --mochiweb_file_collection = \ -- mochifmt.erl \ -- mochifmt_records.erl \ -- mochifmt_std.erl \ -- mochihex.erl \ -- mochijson.erl \ -- mochijson2.erl \ -- mochinum.erl \ -- mochiweb.app.in \ -- mochiweb.erl \ -- mochiweb_app.erl \ -- mochiweb_charref.erl \ -- mochiweb_cookies.erl \ -- mochiweb_echo.erl \ -- mochiweb_headers.erl \ -- mochiweb_html.erl \ -- mochiweb_http.erl \ -- mochiweb_multipart.erl \ -- mochiweb_request.erl \ -- mochiweb_response.erl \ -- mochiweb_skel.erl \ -- mochiweb_socket_server.erl \ -- mochiweb_sup.erl \ -- mochiweb_util.erl \ -- reloader.erl -- --mochiwebebin_make_generated_file_list = \ -- mochifmt.beam \ -- mochifmt_records.beam \ -- mochifmt_std.beam \ -- mochihex.beam \ -- mochijson.beam \ -- mochijson2.beam \ -- mochinum.beam \ -- mochiweb.app \ -- mochiweb.beam \ -- mochiweb_app.beam \ -- mochiweb_charref.beam \ -- mochiweb_cookies.beam \ -- mochiweb_echo.beam \ -- mochiweb_headers.beam \ -- mochiweb_html.beam \ -- mochiweb_http.beam \ -- mochiweb_multipart.beam \ -- mochiweb_request.beam \ -- mochiweb_response.beam \ -- mochiweb_skel.beam \ -- mochiweb_socket_server.beam \ -- mochiweb_sup.beam \ -- mochiweb_util.beam \ -- reloader.beam -- --mochiwebebin_DATA = \ -- $(mochiwebebin_make_generated_file_list) -- --EXTRA_DIST = \ -- $(mochiweb_file_collection) -- --CLEANFILES = \ -- $(mochiwebebin_make_generated_file_list) -- --%.app: %.app.in -- cp $< $@ -- --%.beam: %.erl -- $(ERLC) $(ERLC_FLAGS) $< -diff --git a/src/mochiweb/Makefile.in b/src/mochiweb/Makefile.in -deleted file mode 100644 -index 92f0acc..0000000 ---- a/src/mochiweb/Makefile.in -+++ /dev/null -@@ -1,515 +0,0 @@ --# Makefile.in generated by automake 1.11 from Makefile.am. --# @configure_input@ -- --# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, --# 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation, --# Inc. --# This Makefile.in is free software; the Free Software Foundation --# gives unlimited permission to copy and/or distribute it, --# with or without modifications, as long as this notice is preserved. -- --# This program is distributed in the hope that it will be useful, --# but WITHOUT ANY WARRANTY, to the extent permitted by law; without --# even the implied warranty of MERCHANTABILITY or FITNESS FOR A --# PARTICULAR PURPOSE. -- --@SET_MAKE@ -- --VPATH = @srcdir@ --pkgdatadir = $(datadir)/@PACKAGE@ --pkgincludedir = $(includedir)/@PACKAGE@ --pkglibdir = $(libdir)/@PACKAGE@ --pkglibexecdir = $(libexecdir)/@PACKAGE@ --am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd --install_sh_DATA = $(install_sh) -c -m 644 --install_sh_PROGRAM = $(install_sh) -c --install_sh_SCRIPT = $(install_sh) -c --INSTALL_HEADER = $(INSTALL_DATA) --transform = $(program_transform_name) --NORMAL_INSTALL = : --PRE_INSTALL = : --POST_INSTALL = : --NORMAL_UNINSTALL = : --PRE_UNINSTALL = : --POST_UNINSTALL = : --build_triplet = @build@ --host_triplet = @host@ --subdir = src/mochiweb --DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in --ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 --am__aclocal_m4_deps = $(top_srcdir)/m4/ac_check_curl.m4 \ -- $(top_srcdir)/m4/ac_check_icu.m4 $(top_srcdir)/m4/libtool.m4 \ -- $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \ -- $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \ -- $(top_srcdir)/acinclude.m4 $(top_srcdir)/configure.ac --am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ -- $(ACLOCAL_M4) --mkinstalldirs = $(install_sh) -d --CONFIG_HEADER = $(top_builddir)/config.h --CONFIG_CLEAN_FILES = --CONFIG_CLEAN_VPATH_FILES = --SOURCES = --DIST_SOURCES = --am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; --am__vpath_adj = case $$p in \ -- $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ -- *) f=$$p;; \ -- esac; --am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; --am__install_max = 40 --am__nobase_strip_setup = \ -- srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` --am__nobase_strip = \ -- for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" --am__nobase_list = $(am__nobase_strip_setup); \ -- for p in $$list; do echo "$$p $$p"; done | \ -- sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ -- $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ -- if (++n[$$2] == $(am__install_max)) \ -- { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ -- END { for (dir in files) print dir, files[dir] }' --am__base_list = \ -- sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ -- sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' --am__installdirs = "$(DESTDIR)$(mochiwebebindir)" --DATA = $(mochiwebebin_DATA) --DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) --ACLOCAL = @ACLOCAL@ --AMTAR = @AMTAR@ --AR = @AR@ --AUTOCONF = @AUTOCONF@ --AUTOHEADER = @AUTOHEADER@ --AUTOMAKE = @AUTOMAKE@ --AWK = @AWK@ --CC = @CC@ --CCDEPMODE = @CCDEPMODE@ --CFLAGS = @CFLAGS@ --CPP = @CPP@ --CPPFLAGS = @CPPFLAGS@ --CURL_CFLAGS = @CURL_CFLAGS@ --CURL_CONFIG = @CURL_CONFIG@ --CURL_LDFLAGS = @CURL_LDFLAGS@ --CURL_LIBS = @CURL_LIBS@ --CYGPATH_W = @CYGPATH_W@ --DEFS = @DEFS@ --DEPDIR = @DEPDIR@ --DSYMUTIL = @DSYMUTIL@ --DUMPBIN = @DUMPBIN@ --ECHO_C = @ECHO_C@ --ECHO_N = @ECHO_N@ --ECHO_T = @ECHO_T@ --EGREP = @EGREP@ --ERL = @ERL@ --ERLC = @ERLC@ --ERLC_FLAGS = @ERLC_FLAGS@ --EXEEXT = @EXEEXT@ --FGREP = @FGREP@ --FLAGS = @FLAGS@ --GREP = @GREP@ --HELP2MAN_EXECUTABLE = @HELP2MAN_EXECUTABLE@ --ICU_CFLAGS = @ICU_CFLAGS@ --ICU_CONFIG = @ICU_CONFIG@ --ICU_CXXFLAGS = @ICU_CXXFLAGS@ --ICU_LIBS = @ICU_LIBS@ --ICU_LOCAL_BIN = @ICU_LOCAL_BIN@ --ICU_LOCAL_CFLAGS = @ICU_LOCAL_CFLAGS@ --ICU_LOCAL_LDFLAGS = @ICU_LOCAL_LDFLAGS@ --INNO_COMPILER_EXECUTABLE = @INNO_COMPILER_EXECUTABLE@ --INSTALL = @INSTALL@ --INSTALL_DATA = @INSTALL_DATA@ --INSTALL_PROGRAM = @INSTALL_PROGRAM@ --INSTALL_SCRIPT = @INSTALL_SCRIPT@ --INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ --JSLIB = @JSLIB@ --JS_LIB_BASE = @JS_LIB_BASE@ --JS_LIB_BINARY = @JS_LIB_BINARY@ --JS_LIB_DIR = @JS_LIB_DIR@ --LD = @LD@ --LDFLAGS = @LDFLAGS@ --LIBOBJS = @LIBOBJS@ --LIBS = @LIBS@ --LIBTOOL = @LIBTOOL@ --LIPO = @LIPO@ --LN_S = @LN_S@ --LTLIBOBJS = @LTLIBOBJS@ --MAKEINFO = @MAKEINFO@ --MKDIR_P = @MKDIR_P@ --NM = @NM@ --NMEDIT = @NMEDIT@ --OBJDUMP = @OBJDUMP@ --OBJEXT = @OBJEXT@ --OTOOL = @OTOOL@ --OTOOL64 = @OTOOL64@ --PACKAGE = @PACKAGE@ --PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ --PACKAGE_NAME = @PACKAGE_NAME@ --PACKAGE_STRING = @PACKAGE_STRING@ --PACKAGE_TARNAME = @PACKAGE_TARNAME@ --PACKAGE_URL = @PACKAGE_URL@ --PACKAGE_VERSION = @PACKAGE_VERSION@ --PATH_SEPARATOR = @PATH_SEPARATOR@ --RANLIB = @RANLIB@ --SED = @SED@ --SET_MAKE = @SET_MAKE@ --SHELL = @SHELL@ --STRIP = @STRIP@ --VERSION = @VERSION@ --abs_builddir = @abs_builddir@ --abs_srcdir = @abs_srcdir@ --abs_top_builddir = @abs_top_builddir@ --abs_top_srcdir = @abs_top_srcdir@ --ac_ct_CC = @ac_ct_CC@ --ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ --am__include = @am__include@ --am__leading_dot = @am__leading_dot@ --am__quote = @am__quote@ --am__tar = @am__tar@ --am__untar = @am__untar@ --bindir = @bindir@ --bug_uri = @bug_uri@ --build = @build@ --build_alias = @build_alias@ --build_cpu = @build_cpu@ --build_os = @build_os@ --build_vendor = @build_vendor@ --builddir = @builddir@ --datadir = @datadir@ --datarootdir = @datarootdir@ --docdir = @docdir@ --dvidir = @dvidir@ --exec_prefix = @exec_prefix@ --host = @host@ --host_alias = @host_alias@ --host_cpu = @host_cpu@ --host_os = @host_os@ --host_vendor = @host_vendor@ --htmldir = @htmldir@ --includedir = @includedir@ --infodir = @infodir@ --initdir = @initdir@ --install_sh = @install_sh@ --launchddir = @launchddir@ --libdir = @libdir@ --libexecdir = @libexecdir@ --localconfdir = @localconfdir@ --localdatadir = @localdatadir@ --localdocdir = @localdocdir@ --localedir = @localedir@ --localerlanglibdir = @localerlanglibdir@ --locallibbindir = @locallibbindir@ --locallibdir = @locallibdir@ --localstatedir = @localstatedir@ --localstatelibdir = @localstatelibdir@ --localstatelogdir = @localstatelogdir@ --localstaterundir = @localstaterundir@ --lt_ECHO = @lt_ECHO@ --mandir = @mandir@ --mkdir_p = @mkdir_p@ --msvc_redist_dir = @msvc_redist_dir@ --msvc_redist_name = @msvc_redist_name@ --oldincludedir = @oldincludedir@ --openssl_bin_dir = @openssl_bin_dir@ --package_author_address = @package_author_address@ --package_author_name = @package_author_name@ --package_identifier = @package_identifier@ --package_name = @package_name@ --package_tarname = @package_tarname@ --pdfdir = @pdfdir@ --prefix = @prefix@ --program_transform_name = @program_transform_name@ --psdir = @psdir@ --sbindir = @sbindir@ --sharedstatedir = @sharedstatedir@ --srcdir = @srcdir@ --sysconfdir = @sysconfdir@ --target_alias = @target_alias@ --top_build_prefix = @top_build_prefix@ --top_builddir = @top_builddir@ --top_srcdir = @top_srcdir@ --version = @version@ --version_major = @version_major@ --version_minor = @version_minor@ --version_release = @version_release@ --version_revision = @version_revision@ --version_stage = @version_stage@ --mochiwebebindir = $(localerlanglibdir)/mochiweb-r113/ebin --mochiweb_file_collection = \ -- mochifmt.erl \ -- mochifmt_records.erl \ -- mochifmt_std.erl \ -- mochihex.erl \ -- mochijson.erl \ -- mochijson2.erl \ -- mochinum.erl \ -- mochiweb.app.in \ -- mochiweb.erl \ -- mochiweb_app.erl \ -- mochiweb_charref.erl \ -- mochiweb_cookies.erl \ -- mochiweb_echo.erl \ -- mochiweb_headers.erl \ -- mochiweb_html.erl \ -- mochiweb_http.erl \ -- mochiweb_multipart.erl \ -- mochiweb_request.erl \ -- mochiweb_response.erl \ -- mochiweb_skel.erl \ -- mochiweb_socket_server.erl \ -- mochiweb_sup.erl \ -- mochiweb_util.erl \ -- reloader.erl -- --mochiwebebin_make_generated_file_list = \ -- mochifmt.beam \ -- mochifmt_records.beam \ -- mochifmt_std.beam \ -- mochihex.beam \ -- mochijson.beam \ -- mochijson2.beam \ -- mochinum.beam \ -- mochiweb.app \ -- mochiweb.beam \ -- mochiweb_app.beam \ -- mochiweb_charref.beam \ -- mochiweb_cookies.beam \ -- mochiweb_echo.beam \ -- mochiweb_headers.beam \ -- mochiweb_html.beam \ -- mochiweb_http.beam \ -- mochiweb_multipart.beam \ -- mochiweb_request.beam \ -- mochiweb_response.beam \ -- mochiweb_skel.beam \ -- mochiweb_socket_server.beam \ -- mochiweb_sup.beam \ -- mochiweb_util.beam \ -- reloader.beam -- --mochiwebebin_DATA = \ -- $(mochiwebebin_make_generated_file_list) -- --EXTRA_DIST = \ -- $(mochiweb_file_collection) -- --CLEANFILES = \ -- $(mochiwebebin_make_generated_file_list) -- --all: all-am -- --.SUFFIXES: --$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) -- @for dep in $?; do \ -- case '$(am__configure_deps)' in \ -- *$$dep*) \ -- ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ -- && { if test -f $@; then exit 0; else break; fi; }; \ -- exit 1;; \ -- esac; \ -- done; \ -- echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/mochiweb/Makefile'; \ -- $(am__cd) $(top_srcdir) && \ -- $(AUTOMAKE) --foreign src/mochiweb/Makefile --.PRECIOUS: Makefile --Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status -- @case '$?' in \ -- *config.status*) \ -- cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ -- *) \ -- echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ -- cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ -- esac; -- --$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) -- cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh -- --$(top_srcdir)/configure: $(am__configure_deps) -- cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh --$(ACLOCAL_M4): $(am__aclocal_m4_deps) -- cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh --$(am__aclocal_m4_deps): -- --mostlyclean-libtool: -- -rm -f *.lo -- --clean-libtool: -- -rm -rf .libs _libs --install-mochiwebebinDATA: $(mochiwebebin_DATA) -- @$(NORMAL_INSTALL) -- test -z "$(mochiwebebindir)" || $(MKDIR_P) "$(DESTDIR)$(mochiwebebindir)" -- @list='$(mochiwebebin_DATA)'; test -n "$(mochiwebebindir)" || list=; \ -- for p in $$list; do \ -- if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ -- echo "$$d$$p"; \ -- done | $(am__base_list) | \ -- while read files; do \ -- echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(mochiwebebindir)'"; \ -- $(INSTALL_DATA) $$files "$(DESTDIR)$(mochiwebebindir)" || exit $$?; \ -- done -- --uninstall-mochiwebebinDATA: -- @$(NORMAL_UNINSTALL) -- @list='$(mochiwebebin_DATA)'; test -n "$(mochiwebebindir)" || list=; \ -- files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ -- test -n "$$files" || exit 0; \ -- echo " ( cd '$(DESTDIR)$(mochiwebebindir)' && rm -f" $$files ")"; \ -- cd "$(DESTDIR)$(mochiwebebindir)" && rm -f $$files --tags: TAGS --TAGS: -- --ctags: CTAGS --CTAGS: -- -- --distdir: $(DISTFILES) -- @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ -- topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ -- list='$(DISTFILES)'; \ -- dist_files=`for file in $$list; do echo $$file; done | \ -- sed -e "s|^$$srcdirstrip/||;t" \ -- -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ -- case $$dist_files in \ -- */*) $(MKDIR_P) `echo "$$dist_files" | \ -- sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ -- sort -u` ;; \ -- esac; \ -- for file in $$dist_files; do \ -- if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ -- if test -d $$d/$$file; then \ -- dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ -- if test -d "$(distdir)/$$file"; then \ -- find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ -- fi; \ -- if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ -- cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ -- find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ -- fi; \ -- cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ -- else \ -- test -f "$(distdir)/$$file" \ -- || cp -p $$d/$$file "$(distdir)/$$file" \ -- || exit 1; \ -- fi; \ -- done --check-am: all-am --check: check-am --all-am: Makefile $(DATA) --installdirs: -- for dir in "$(DESTDIR)$(mochiwebebindir)"; do \ -- test -z "$$dir" || $(MKDIR_P) "$$dir"; \ -- done --install: install-am --install-exec: install-exec-am --install-data: install-data-am --uninstall: uninstall-am -- --install-am: all-am -- @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am -- --installcheck: installcheck-am --install-strip: -- $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ -- install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ -- `test -z '$(STRIP)' || \ -- echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install --mostlyclean-generic: -- --clean-generic: -- -test -z "$(CLEANFILES)" || rm -f $(CLEANFILES) -- --distclean-generic: -- -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -- -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) -- --maintainer-clean-generic: -- @echo "This command is intended for maintainers to use" -- @echo "it deletes files that may require special tools to rebuild." --clean: clean-am -- --clean-am: clean-generic clean-libtool mostlyclean-am -- --distclean: distclean-am -- -rm -f Makefile --distclean-am: clean-am distclean-generic -- --dvi: dvi-am -- --dvi-am: -- --html: html-am -- --html-am: -- --info: info-am -- --info-am: -- --install-data-am: install-mochiwebebinDATA -- --install-dvi: install-dvi-am -- --install-dvi-am: -- --install-exec-am: -- --install-html: install-html-am -- --install-html-am: -- --install-info: install-info-am -- --install-info-am: -- --install-man: -- --install-pdf: install-pdf-am -- --install-pdf-am: -- --install-ps: install-ps-am -- --install-ps-am: -- --installcheck-am: -- --maintainer-clean: maintainer-clean-am -- -rm -f Makefile --maintainer-clean-am: distclean-am maintainer-clean-generic -- --mostlyclean: mostlyclean-am -- --mostlyclean-am: mostlyclean-generic mostlyclean-libtool -- --pdf: pdf-am -- --pdf-am: -- --ps: ps-am -- --ps-am: -- --uninstall-am: uninstall-mochiwebebinDATA -- --.MAKE: install-am install-strip -- --.PHONY: all all-am check check-am clean clean-generic clean-libtool \ -- distclean distclean-generic distclean-libtool distdir dvi \ -- dvi-am html html-am info info-am install install-am \ -- install-data install-data-am install-dvi install-dvi-am \ -- install-exec install-exec-am install-html install-html-am \ -- install-info install-info-am install-man \ -- install-mochiwebebinDATA install-pdf install-pdf-am install-ps \ -- install-ps-am install-strip installcheck installcheck-am \ -- installdirs maintainer-clean maintainer-clean-generic \ -- mostlyclean mostlyclean-generic mostlyclean-libtool pdf pdf-am \ -- ps ps-am uninstall uninstall-am uninstall-mochiwebebinDATA -- -- --%.app: %.app.in -- cp $< $@ -- --%.beam: %.erl -- $(ERLC) $(ERLC_FLAGS) $< -- --# Tell versions [3.59,3.63) of GNU make to not export all variables. --# Otherwise a system limit (for SysV at least) may be exceeded. --.NOEXPORT: -diff --git a/src/mochiweb/mochifmt.erl b/src/mochiweb/mochifmt.erl -deleted file mode 100644 -index da0a133..0000000 ---- a/src/mochiweb/mochifmt.erl -+++ /dev/null -@@ -1,426 +0,0 @@ --%% @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/src/mochiweb/mochifmt_records.erl b/src/mochiweb/mochifmt_records.erl -deleted file mode 100644 -index 94c7797..0000000 ---- a/src/mochiweb/mochifmt_records.erl -+++ /dev/null -@@ -1,30 +0,0 @@ --%% @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/src/mochiweb/mochifmt_std.erl b/src/mochiweb/mochifmt_std.erl -deleted file mode 100644 -index 9442016..0000000 ---- a/src/mochiweb/mochifmt_std.erl -+++ /dev/null -@@ -1,23 +0,0 @@ --%% @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/src/mochiweb/mochihex.erl b/src/mochiweb/mochihex.erl -deleted file mode 100644 -index 7fe6899..0000000 ---- a/src/mochiweb/mochihex.erl -+++ /dev/null -@@ -1,75 +0,0 @@ --%% @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/src/mochiweb/mochijson.erl b/src/mochiweb/mochijson.erl -deleted file mode 100644 -index 74695a7..0000000 ---- a/src/mochiweb/mochijson.erl -+++ /dev/null -@@ -1,528 +0,0 @@ --%% @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]) -> -- equiv(V1, V2) andalso equiv_list(L1, L2). -- --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/src/mochiweb/mochijson2.erl b/src/mochiweb/mochijson2.erl -deleted file mode 100644 -index 111c37b..0000000 ---- a/src/mochiweb/mochijson2.erl -+++ /dev/null -@@ -1,660 +0,0 @@ --%% @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) -> -- integer_to_list(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, $\\, _/binary>> -> -- {escape, O}; -- <<_:O/binary, C1, _/binary>> when C1 < 128 -> -- tokenize_string_fast(B, 1 + O); -- <<_:O/binary, C1, C2, _/binary>> when C1 >= 194, C1 =< 223, -- C2 >= 128, C2 =< 191 -> -- tokenize_string_fast(B, 2 + O); -- <<_:O/binary, C1, C2, C3, _/binary>> when C1 >= 224, C1 =< 239, -- C2 >= 128, C2 =< 191, -- C3 >= 128, C3 =< 191 -> -- tokenize_string_fast(B, 3 + O); -- <<_:O/binary, C1, C2, C3, C4, _/binary>> when C1 >= 240, C1 =< 244, -- C2 >= 128, C2 =< 191, -- C3 >= 128, C3 =< 191, -- C4 >= 128, C4 =< 191 -> -- tokenize_string_fast(B, 4 + O); -- _ -> -- throw(invalid_utf8) -- 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, Rest/binary>> -> -- C = erlang:list_to_integer([C3, C2, C1, C0], 16), -- if C > 16#D7FF, C < 16#DC00 -> -- %% coalesce UTF-16 surrogate pair -- <<"\\u", D3, D2, D1, D0, _/binary>> = Rest, -- D = erlang:list_to_integer([D3,D2,D1,D0], 16), -- [CodePoint] = xmerl_ucs:from_utf16be(<>), -- Acc1 = lists:reverse(xmerl_ucs:to_utf8(CodePoint), Acc), -- tokenize_string(B, ?ADV_COL(S, 12), Acc1); -- true -> -- Acc1 = lists:reverse(xmerl_ucs:to_utf8(C), Acc), -- tokenize_string(B, ?ADV_COL(S, 6), Acc1) -- end; -- <<_: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]) -> -- equiv(V1, V2) andalso equiv_list(L1, L2). -- --test_all() -> -- [1199344435545.0, 1] = decode(<<"[1199344435545.0,1]">>), -- <<16#F0,16#9D,16#9C,16#95>> = decode([34,"\\ud835","\\udf15",34]), -- test_encoder_utf8(), -- test_input_validation(), -- 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">>). -- --test_input_validation() -> -- Good = [ -- {16#00A3, <>}, % pound -- {16#20AC, <>}, % euro -- {16#10196, <>} % denarius -- ], -- lists:foreach(fun({CodePoint, UTF8}) -> -- Expect = list_to_binary(xmerl_ucs:to_utf8(CodePoint)), -- Expect = decode(UTF8) -- end, Good), -- -- Bad = [ -- % 2nd, 3rd, or 4th byte of a multi-byte sequence w/o leading byte -- <>, -- % missing continuations, last byte in each should be 80-BF -- <>, -- <>, -- <>, -- % we don't support code points > 10FFFF per RFC 3629 -- <> -- ], -- lists:foreach(fun(X) -> -- ok = try decode(X) catch invalid_utf8 -> ok end -- end, Bad). -diff --git a/src/mochiweb/mochinum.erl b/src/mochiweb/mochinum.erl -deleted file mode 100644 -index 6a86604..0000000 ---- a/src/mochiweb/mochinum.erl -+++ /dev/null -@@ -1,289 +0,0 @@ --%% @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/src/mochiweb/mochiweb.app.in b/src/mochiweb/mochiweb.app.in -deleted file mode 100644 -index b0f9014..0000000 ---- a/src/mochiweb/mochiweb.app.in -+++ /dev/null -@@ -1,32 +0,0 @@ --{application, mochiweb, -- [{description, "MochiMedia Web Server"}, -- {vsn, "113"}, -- {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/src/mochiweb/mochiweb.erl b/src/mochiweb/mochiweb.erl -deleted file mode 100644 -index 0f4d52a..0000000 ---- a/src/mochiweb/mochiweb.erl -+++ /dev/null -@@ -1,110 +0,0 @@ --%% @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/src/mochiweb/mochiweb_app.erl b/src/mochiweb/mochiweb_app.erl -deleted file mode 100644 -index 2b437f6..0000000 ---- a/src/mochiweb/mochiweb_app.erl -+++ /dev/null -@@ -1,20 +0,0 @@ --%% @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/src/mochiweb/mochiweb_charref.erl b/src/mochiweb/mochiweb_charref.erl -deleted file mode 100644 -index d037d2f..0000000 ---- a/src/mochiweb/mochiweb_charref.erl -+++ /dev/null -@@ -1,295 +0,0 @@ --%% @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/src/mochiweb/mochiweb_cookies.erl b/src/mochiweb/mochiweb_cookies.erl -deleted file mode 100644 -index 61711ff..0000000 ---- a/src/mochiweb/mochiweb_cookies.erl -+++ /dev/null -@@ -1,257 +0,0 @@ --%% @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/src/mochiweb/mochiweb_echo.erl b/src/mochiweb/mochiweb_echo.erl -deleted file mode 100644 -index f32d680..0000000 ---- a/src/mochiweb/mochiweb_echo.erl -+++ /dev/null -@@ -1,31 +0,0 @@ --%% @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/src/mochiweb/mochiweb_headers.erl b/src/mochiweb/mochiweb_headers.erl -deleted file mode 100644 -index d90fd67..0000000 ---- a/src/mochiweb/mochiweb_headers.erl -+++ /dev/null -@@ -1,251 +0,0 @@ --%% @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([from_binary/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), -- HB = <<"Content-Length: 47\r\nContent-Type: text/plain\r\n\r\n">>, -- H_HB = ?MODULE:from_binary(HB), -- H_HB = ?MODULE:from_binary(binary_to_list(HB)), -- "47" = ?MODULE:get_value("Content-Length", H_HB), -- "text/plain" = ?MODULE:get_value("Content-Type", H_HB), -- L_H_HB = ?MODULE:to_list(H_HB), -- 2 = length(L_H_HB), -- true = lists:member({'Content-Length', "47"}, L_H_HB), -- true = lists:member({'Content-Type', "text/plain"}, L_H_HB), -- HL = [ <<"Content-Length: 47\r\n">>, <<"Content-Type: text/plain\r\n">> ], -- HL2 = [ "Content-Length: 47\r\n", <<"Content-Type: text/plain\r\n">> ], -- HL3 = [ <<"Content-Length: 47\r\n">>, "Content-Type: text/plain\r\n" ], -- H_HL = ?MODULE:from_binary(HL), -- H_HL = ?MODULE:from_binary(HL2), -- H_HL = ?MODULE:from_binary(HL3), -- "47" = ?MODULE:get_value("Content-Length", H_HL), -- "text/plain" = ?MODULE:get_value("Content-Type", H_HL), -- L_H_HL = ?MODULE:to_list(H_HL), -- 2 = length(L_H_HL), -- true = lists:member({'Content-Length', "47"}, L_H_HL), -- true = lists:member({'Content-Type', "text/plain"}, L_H_HL), -- [] = ?MODULE:to_list(?MODULE:from_binary(<<>>)), -- [] = ?MODULE:to_list(?MODULE:from_binary(<<"">>)), -- [] = ?MODULE:to_list(?MODULE:from_binary(<<"\r\n">>)), -- [] = ?MODULE:to_list(?MODULE:from_binary(<<"\r\n\r\n">>)), -- [] = ?MODULE:to_list(?MODULE:from_binary("")), -- [] = ?MODULE:to_list(?MODULE:from_binary([<<>>])), -- [] = ?MODULE:to_list(?MODULE:from_binary([<<"">>])), -- [] = ?MODULE:to_list(?MODULE:from_binary([<<"\r\n">>])), -- [] = ?MODULE:to_list(?MODULE:from_binary([<<"\r\n\r\n">>])), -- 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_binary(RawHttpHeader()) -> headers() --%% @type RawHttpHeader() -> string() | binary() | [ string() | binary() ] --%% --%% @doc Transforms a raw HTTP header into a mochiweb headers structure. --%% --%% The given raw HTTP header can be one of the following: --%% --%% 1) A string or a binary representing a full HTTP header ending with --%% double CRLF. --%% Examples: --%% "Content-Length: 47\r\nContent-Type: text/plain\r\n\r\n" --%% <<"Content-Length: 47\r\nContent-Type: text/plain\r\n\r\n">> --%% --%% 2) A list of binaries or strings where each element represents a raw --%% HTTP header line ending with a single CRLF. --%% Examples: --%% [ <<"Content-Length: 47\r\n">>, <<"Content-Type: text/plain\r\n">> ] --%% [ "Content-Length: 47\r\n", "Content-Type: text/plain\r\n" ] --%% [ "Content-Length: 47\r\n", <<"Content-Type: text/plain\r\n">> ] --%% --from_binary(RawHttpHeader) when is_binary(RawHttpHeader) -> -- from_binary(RawHttpHeader, []); -- --from_binary(RawHttpHeaderList) -> -- from_binary(list_to_binary([RawHttpHeaderList, "\r\n"])). -- --from_binary(RawHttpHeader, Acc) -> -- case erlang:decode_packet(httph, RawHttpHeader, []) of -- { ok, {http_header, _, H, _, V}, Rest } -> -- from_binary(Rest, [{H, V} | Acc]); -- _ -> -- make(Acc) -- end. -- --%% @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/src/mochiweb/mochiweb_html.erl b/src/mochiweb/mochiweb_html.erl -deleted file mode 100644 -index 77100d5..0000000 ---- a/src/mochiweb/mochiweb_html.erl -+++ /dev/null -@@ -1,893 +0,0 @@ --%% @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/src/mochiweb/mochiweb_http.erl b/src/mochiweb/mochiweb_http.erl -deleted file mode 100644 -index f1821f4..0000000 ---- a/src/mochiweb/mochiweb_http.erl -+++ /dev/null -@@ -1,152 +0,0 @@ --%% @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]). ---export([after_response/2, reentry/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. -- --reentry(Body) -> -- fun (Req) -> -- ?MODULE:after_response(Body, Req) -- 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), -- ?MODULE:after_response(Body, Req); -- {ok, {http_header, _, Name, _, Value}} -> -- headers(Socket, Request, [{Name, Value} | Headers], Body, -- 1 + HeaderCount); -- _Other -> -- gen_tcp:close(Socket), -- exit(normal) -- end. -- --after_response(Body, Req) -> -- Socket = Req:get(socket), -- case Req:should_close() of -- true -> -- gen_tcp:close(Socket), -- exit(normal); -- false -> -- Req:cleanup(), -- ?MODULE:loop(Socket, Body) -- end. -diff --git a/src/mochiweb/mochiweb_multipart.erl b/src/mochiweb/mochiweb_multipart.erl -deleted file mode 100644 -index 0368a9a..0000000 ---- a/src/mochiweb/mochiweb_multipart.erl -+++ /dev/null -@@ -1,530 +0,0 @@ --%% @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 = byte_size(Boundary), -- Chunk = read_chunk(Req, Length), -- Length1 = Length - byte_size(Chunk), -- <<"--", Boundary:BS/binary, "\r\n", Rest/binary>> = Chunk, -- feed_mp(headers, flash_multipart_hack(#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 = <>, -- flash_multipart_hack(State#mp{length=Length - byte_size(Data), -- buffer=Buffer1}). -- --flash_multipart_hack(State=#mp{length=0, buffer=Buffer, boundary=Prefix}) -> -- %% http://code.google.com/p/mochiweb/issues/detail?id=22 -- %% Flash doesn't terminate multipart with \r\n properly so we fix it up here -- PrefixSize = size(Prefix), -- case size(Buffer) - (2 + PrefixSize) of -- Seek when Seek >= 0 -> -- case Buffer of -- <<_:Seek/binary, Prefix:PrefixSize/binary, "--">> -> -- Buffer1 = <>, -- State#mp{buffer=Buffer1}; -- _ -> -- State -- end; -- _ -> -- State -- end; --flash_multipart_hack(State) -> -- State. -- --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}) -> -- Boundary = find_boundary(Prefix, Buffer), -- case Boundary 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_callback({body, Got}, [{body, Expect} | Rest]) -> -- GotSize = size(Got), -- <> = Expect, -- fun (Next) -> test_callback(Next, [{body, Expect1} | Rest]) 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) -> -- ok = gen_tcp:send(Socket, BinContent), -- exit(normal) -- end, -- ClientFun = fun (Socket) -> -- Req = fake_request(Socket, ContentType, -- byte_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) -> -- ok = gen_tcp:send(Socket, BinContent), -- exit(normal) -- end, -- ClientFun = fun (Socket) -> -- Req = fake_request(Socket, ContentType, -- byte_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) -> -- ok = gen_tcp:send(Socket, BinContent), -- exit(normal) -- end, -- ClientFun = fun (Socket) -> -- Req = fake_request(Socket, ContentType, -- byte_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) -> -- ok = gen_tcp:send(Socket, BinContent), -- exit(normal) -- end, -- ClientFun = fun (Socket) -> -- Req = fake_request(Socket, ContentType, -- byte_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_flash_parse() -> -- ContentType = "multipart/form-data; boundary=----------ei4GI3GI3Ij5Ef1ae0KM7Ij5ei4Ij5", -- "----------ei4GI3GI3Ij5Ef1ae0KM7Ij5ei4Ij5" = get_boundary(ContentType), -- BinContent = <<"------------ei4GI3GI3Ij5Ef1ae0KM7Ij5ei4Ij5\r\nContent-Disposition: form-data; name=\"Filename\"\r\n\r\nhello.txt\r\n------------ei4GI3GI3Ij5Ef1ae0KM7Ij5ei4Ij5\r\nContent-Disposition: form-data; name=\"success_action_status\"\r\n\r\n201\r\n------------ei4GI3GI3Ij5Ef1ae0KM7Ij5ei4Ij5\r\nContent-Disposition: form-data; name=\"file\"; filename=\"hello.txt\"\r\nContent-Type: application/octet-stream\r\n\r\nhello\n\r\n------------ei4GI3GI3Ij5Ef1ae0KM7Ij5ei4Ij5\r\nContent-Disposition: form-data; name=\"Upload\"\r\n\r\nSubmit Query\r\n------------ei4GI3GI3Ij5Ef1ae0KM7Ij5ei4Ij5--">>, -- Expect = [{headers, -- [{"content-disposition", -- {"form-data", [{"name", "Filename"}]}}]}, -- {body, <<"hello.txt">>}, -- body_end, -- {headers, -- [{"content-disposition", -- {"form-data", [{"name", "success_action_status"}]}}]}, -- {body, <<"201">>}, -- body_end, -- {headers, -- [{"content-disposition", -- {"form-data", [{"name", "file"}, {"filename", "hello.txt"}]}}, -- {"content-type", {"application/octet-stream", []}}]}, -- {body, <<"hello\n">>}, -- body_end, -- {headers, -- [{"content-disposition", -- {"form-data", [{"name", "Upload"}]}}]}, -- {body, <<"Submit Query">>}, -- body_end, -- eof], -- TestCallback = fun (Next) -> test_callback(Next, Expect) end, -- ServerFun = fun (Socket) -> -- ok = gen_tcp:send(Socket, BinContent), -- exit(normal) -- end, -- ClientFun = fun (Socket) -> -- Req = fake_request(Socket, ContentType, -- byte_size(BinContent)), -- Res = parse_multipart_request(Req, TestCallback), -- {0, <<>>, ok} = Res, -- ok -- end, -- ok = with_socket_server(ServerFun, ClientFun), -- ok. -- --test_flash_parse2() -> -- ContentType = "multipart/form-data; boundary=----------ei4GI3GI3Ij5Ef1ae0KM7Ij5ei4Ij5", -- "----------ei4GI3GI3Ij5Ef1ae0KM7Ij5ei4Ij5" = get_boundary(ContentType), -- Chunk = iolist_to_binary(string:copies("%", 4096)), -- BinContent = <<"------------ei4GI3GI3Ij5Ef1ae0KM7Ij5ei4Ij5\r\nContent-Disposition: form-data; name=\"Filename\"\r\n\r\nhello.txt\r\n------------ei4GI3GI3Ij5Ef1ae0KM7Ij5ei4Ij5\r\nContent-Disposition: form-data; name=\"success_action_status\"\r\n\r\n201\r\n------------ei4GI3GI3Ij5Ef1ae0KM7Ij5ei4Ij5\r\nContent-Disposition: form-data; name=\"file\"; filename=\"hello.txt\"\r\nContent-Type: application/octet-stream\r\n\r\n", Chunk/binary, "\r\n------------ei4GI3GI3Ij5Ef1ae0KM7Ij5ei4Ij5\r\nContent-Disposition: form-data; name=\"Upload\"\r\n\r\nSubmit Query\r\n------------ei4GI3GI3Ij5Ef1ae0KM7Ij5ei4Ij5--">>, -- Expect = [{headers, -- [{"content-disposition", -- {"form-data", [{"name", "Filename"}]}}]}, -- {body, <<"hello.txt">>}, -- body_end, -- {headers, -- [{"content-disposition", -- {"form-data", [{"name", "success_action_status"}]}}]}, -- {body, <<"201">>}, -- body_end, -- {headers, -- [{"content-disposition", -- {"form-data", [{"name", "file"}, {"filename", "hello.txt"}]}}, -- {"content-type", {"application/octet-stream", []}}]}, -- {body, Chunk}, -- body_end, -- {headers, -- [{"content-disposition", -- {"form-data", [{"name", "Upload"}]}}]}, -- {body, <<"Submit Query">>}, -- body_end, -- eof], -- TestCallback = fun (Next) -> test_callback(Next, Expect) end, -- ServerFun = fun (Socket) -> -- ok = gen_tcp:send(Socket, BinContent), -- exit(normal) -- end, -- ClientFun = fun (Socket) -> -- Req = fake_request(Socket, ContentType, -- byte_size(BinContent)), -- Res = parse_multipart_request(Req, TestCallback), -- {0, <<>>, ok} = Res, -- ok -- end, -- ok = with_socket_server(ServerFun, ClientFun), -- ok. -- --test() -> -- test_find_in_binary(), -- test_find_boundary(), -- test_parse(), -- test_parse2(), -- test_parse3(), -- test_parse_form(), -- test_flash_parse(), -- test_flash_parse2(), -- ok. -diff --git a/src/mochiweb/mochiweb_request.erl b/src/mochiweb/mochiweb_request.erl -deleted file mode 100644 -index 5d7af26..0000000 ---- a/src/mochiweb/mochiweb_request.erl -+++ /dev/null -@@ -1,867 +0,0 @@ --%% @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([accepted_encodings/1]). ---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()}]. -- --% 5 minute default idle timeout ---define(IDLE_TIMEOUT, 300000). -- --% 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, 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, Fun, FunState) -> -- Fun({0, <<>>}, FunState); --stream_unchunked_body(Length, Fun, FunState) when Length > 0 -> -- Bin = recv(0), -- BinSize = byte_size(Bin), -- if BinSize > Length -> -- <> = Bin, -- gen_tcp:unrecv(Socket, Extra), -- NewState = Fun({Length, OurBody}, FunState), -- stream_unchunked_body(0, Fun, NewState); -- true -> -- NewState = Fun({BinSize, Bin}, FunState), -- stream_unchunked_body(Length - BinSize, Fun, NewState) -- end. -- -- --%% @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]), -- case filelib:is_dir(FullPath) of -- true -> -- maybe_redirect(RelPath, FullPath, ExtraHeaders); -- false -> -- maybe_serve_file(FullPath, ExtraHeaders) -- end -- end. -- --%% Internal API -- --%% This has the same effect as the DirectoryIndex directive in httpd --directory_index(FullPath) -> -- filename:join([FullPath, "index.html"]). -- --maybe_redirect([], FullPath, ExtraHeaders) -> -- maybe_serve_file(directory_index(FullPath), ExtraHeaders); -- --maybe_redirect(RelPath, FullPath, ExtraHeaders) -> -- case string:right(RelPath, 1) of -- "/" -> -- maybe_serve_file(directory_index(FullPath), ExtraHeaders); -- _ -> -- Host = mochiweb_headers:get_value("host", Headers), -- Location = "http://" ++ Host ++ "/" ++ RelPath ++ "/", -- LocationBin = list_to_binary(Location), -- MoreHeaders = [{"Location", Location}, -- {"Content-Type", "text/html"} | ExtraHeaders], -- Top = <<"" -- "" -- "301 Moved Permanently" -- "" -- "

Moved Permanently

" -- "

The document has moved >, -- Bottom = <<">here.

\n">>, -- Body = <>, -- respond({301, MoreHeaders, Body}) -- end. -- --maybe_serve_file(File, ExtraHeaders) -> -- 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. -- --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. -- --%% @spec accepted_encodings([encoding()]) -> [encoding()] | error() --%% @type encoding() -> string() --%% @type error() -> bad_accept_encoding_value --%% --%% @doc Returns a list of encodings accepted by a request. Encodings that are --%% not supported by the server will not be included in the return list. --%% This list is computed from the "Accept-Encoding" header and --%% its elements are ordered, descendingly, according to their Q values. --%% --%% Section 14.3 of the RFC 2616 (HTTP 1.1) describes the "Accept-Encoding" --%% header and the process of determining which server supported encodings --%% can be used for encoding the body for the request's response. --%% --%% Examples --%% --%% 1) For a missing "Accept-Encoding" header: --%% accepted_encodings(["gzip", "identity"]) -> ["identity"] --%% --%% 2) For an "Accept-Encoding" header with value "gzip, deflate": --%% accepted_encodings(["gzip", "identity"]) -> ["gzip", "identity"] --%% --%% 3) For an "Accept-Encoding" header with value "gzip;q=0.5, deflate": --%% accepted_encodings(["gzip", "deflate", "identity"]) -> --%% ["deflate", "gzip", "identity"] --%% --accepted_encodings(SupportedEncodings) -> -- AcceptEncodingHeader = case get_header_value("Accept-Encoding") of -- undefined -> -- ""; -- Value -> -- Value -- end, -- case mochiweb_util:parse_qvalues(AcceptEncodingHeader) of -- invalid_qvalue_string -> -- bad_accept_encoding_value; -- QList -> -- mochiweb_util:pick_accepted_encodings( -- QList, SupportedEncodings, "identity" -- ) -- 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 = byte_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/src/mochiweb/mochiweb_response.erl b/src/mochiweb/mochiweb_response.erl -deleted file mode 100644 -index 6285c4c..0000000 ---- a/src/mochiweb/mochiweb_response.erl -+++ /dev/null -@@ -1,56 +0,0 @@ --%% @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/src/mochiweb/mochiweb_skel.erl b/src/mochiweb/mochiweb_skel.erl -deleted file mode 100644 -index 36b48be..0000000 ---- a/src/mochiweb/mochiweb_skel.erl -+++ /dev/null -@@ -1,73 +0,0 @@ ---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/src/mochiweb/mochiweb_socket_server.erl b/src/mochiweb/mochiweb_socket_server.erl -deleted file mode 100644 -index 7aafe29..0000000 ---- a/src/mochiweb/mochiweb_socket_server.erl -+++ /dev/null -@@ -1,248 +0,0 @@ --%% @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=128}). -- --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/src/mochiweb/mochiweb_sup.erl b/src/mochiweb/mochiweb_sup.erl -deleted file mode 100644 -index 5cb525b..0000000 ---- a/src/mochiweb/mochiweb_sup.erl -+++ /dev/null -@@ -1,34 +0,0 @@ --%% @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/src/mochiweb/mochiweb_util.erl b/src/mochiweb/mochiweb_util.erl -deleted file mode 100644 -index d8fc89d..0000000 ---- a/src/mochiweb/mochiweb_util.erl -+++ /dev/null -@@ -1,859 +0,0 @@ --%% @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([parse_qvalues/1, pick_accepted_encodings/3]). ---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 tuple_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]). -- --%% @spec parse_qvalues(string()) -> [qvalue()] | error() --%% @type qvalue() -> {element(), q()} --%% @type element() -> string() --%% @type q() -> 0.0 .. 1.0 --%% @type error() -> invalid_qvalue_string --%% --%% @doc Parses a list (given as a string) of elements with Q values associated --%% to them. Elements are separated by commas and each element is separated --%% from its Q value by a semicolon. Q values are optional but when missing --%% the value of an element is considered as 1.0. A Q value is always in the --%% range [0.0, 1.0]. A Q value list is used for example as the value of the --%% HTTP "Accept-Encoding" header. --%% --%% Q values are described in section 2.9 of the RFC 2616 (HTTP 1.1). --%% --%% Example: --%% --%% parse_qvalues("gzip; q=0.5, deflate, identity;q=0.0") -> --%% [{"gzip", 0.5}, {"deflate", 1.0}, {"identity", 0.0}] --%% --parse_qvalues(QValuesStr) -> -- try -- {ok, Re} = re:compile("^\\s*q\\s*=\\s*((?:0|1)(?:\\.\\d{1,3})?)\\s*$"), -- lists:map( -- fun(Pair) -> -- case string:tokens(Pair, ";") of -- [Enc] -> -- {string:strip(Enc), 1.0}; -- [Enc, QStr] -> -- case re:run(QStr, Re, [{capture, [1], list}]) of -- {match, [Q]} -> -- QVal = case Q of -- "0" -> -- 0.0; -- "1" -> -- 1.0; -- Else -> -- list_to_float(Else) -- end, -- case QVal < 0.0 orelse QVal > 1.0 of -- false -> -- {string:strip(Enc), QVal} -- end -- end -- end -- end, -- string:tokens(string:to_lower(QValuesStr), ",") -- ) -- catch -- _Type:_Error -> -- invalid_qvalue_string -- end. -- --%% @spec pick_accepted_encodings(qvalues(), [encoding()], encoding()) -> --%% [encoding()] --%% @type qvalues() -> [ {encoding(), q()} ] --%% @type encoding() -> string() --%% @type q() -> 0.0 .. 1.0 --%% --%% @doc Determines which encodings specified in the given Q values list are --%% valid according to a list of supported encodings and a default encoding. --%% --%% The returned list of encodings is sorted, descendingly, according to the --%% Q values of the given list. The last element of this list is the given --%% default encoding unless this encoding is explicitily or implicitily --%% marked with a Q value of 0.0 in the given Q values list. --%% Note: encodings with the same Q value are kept in the same order as --%% found in the input Q values list. --%% --%% This encoding picking process is described in section 14.3 of the --%% RFC 2616 (HTTP 1.1). --%% --%% Example: --%% --%% pick_accepted_encodings( --%% [{"gzip", 0.5}, {"deflate", 1.0}], --%% ["gzip", "identity"], --%% "identity" --%% ) -> --%% ["gzip", "identity"] --%% --pick_accepted_encodings(AcceptedEncs, SupportedEncs, DefaultEnc) -> -- SortedQList = lists:reverse( -- lists:sort(fun({_, Q1}, {_, Q2}) -> Q1 < Q2 end, AcceptedEncs) -- ), -- {Accepted, Refused} = lists:foldr( -- fun({E, Q}, {A, R}) -> -- case Q > 0.0 of -- true -> -- {[E | A], R}; -- false -> -- {A, [E | R]} -- end -- end, -- {[], []}, -- SortedQList -- ), -- Refused1 = lists:foldr( -- fun(Enc, Acc) -> -- case Enc of -- "*" -> -- lists:subtract(SupportedEncs, Accepted) ++ Acc; -- _ -> -- [Enc | Acc] -- end -- end, -- [], -- Refused -- ), -- Accepted1 = lists:foldr( -- fun(Enc, Acc) -> -- case Enc of -- "*" -> -- lists:subtract(SupportedEncs, Accepted ++ Refused1) ++ Acc; -- _ -> -- [Enc | Acc] -- end -- end, -- [], -- Accepted -- ), -- Accepted2 = case lists:member(DefaultEnc, Accepted1) of -- true -> -- Accepted1; -- false -> -- Accepted1 ++ [DefaultEnc] -- end, -- [E || E <- Accepted2, lists:member(E, SupportedEncs), -- not lists:member(E, Refused1)]. -- --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(), -- test_parse_qvalues(), -- test_pick_accepted_encodings(), -- 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. -- --test_parse_qvalues() -> -- [] = parse_qvalues(""), -- [{"identity", 0.0}] = parse_qvalues("identity;q=0"), -- [{"identity", 0.0}] = parse_qvalues("identity ;q=0"), -- [{"identity", 0.0}] = parse_qvalues(" identity; q =0 "), -- [{"identity", 0.0}] = parse_qvalues("identity ; q = 0"), -- [{"identity", 0.0}] = parse_qvalues("identity ; q= 0.0"), -- [{"gzip", 1.0}, {"deflate", 1.0}, {"identity", 0.0}] = parse_qvalues( -- "gzip,deflate,identity;q=0.0" -- ), -- [{"deflate", 1.0}, {"gzip", 1.0}, {"identity", 0.0}] = parse_qvalues( -- "deflate,gzip,identity;q=0.0" -- ), -- [{"gzip", 1.0}, {"deflate", 1.0}, {"gzip", 1.0}, {"identity", 0.0}] = -- parse_qvalues("gzip,deflate,gzip,identity;q=0"), -- [{"gzip", 1.0}, {"deflate", 1.0}, {"identity", 0.0}] = parse_qvalues( -- "gzip, deflate , identity; q=0.0" -- ), -- [{"gzip", 1.0}, {"deflate", 1.0}, {"identity", 0.0}] = parse_qvalues( -- "gzip; q=1, deflate;q=1.0, identity;q=0.0" -- ), -- [{"gzip", 0.5}, {"deflate", 1.0}, {"identity", 0.0}] = parse_qvalues( -- "gzip; q=0.5, deflate;q=1.0, identity;q=0" -- ), -- [{"gzip", 0.5}, {"deflate", 1.0}, {"identity", 0.0}] = parse_qvalues( -- "gzip; q=0.5, deflate , identity;q=0.0" -- ), -- [{"gzip", 0.5}, {"deflate", 0.8}, {"identity", 0.0}] = parse_qvalues( -- "gzip; q=0.5, deflate;q=0.8, identity;q=0.0" -- ), -- [{"gzip", 0.5}, {"deflate", 1.0}, {"identity", 1.0}] = parse_qvalues( -- "gzip; q=0.5,deflate,identity" -- ), -- [{"gzip", 0.5}, {"deflate", 1.0}, {"identity", 1.0}, {"identity", 1.0}] = -- parse_qvalues("gzip; q=0.5,deflate,identity, identity "), -- invalid_qvalue_string = parse_qvalues("gzip; q=1.1, deflate"), -- invalid_qvalue_string = parse_qvalues("gzip; q=0.5, deflate;q=2"), -- invalid_qvalue_string = parse_qvalues("gzip, deflate;q=AB"), -- invalid_qvalue_string = parse_qvalues("gzip; q=2.1, deflate"), -- ok. -- --test_pick_accepted_encodings() -> -- ["identity"] = pick_accepted_encodings( -- [], -- ["gzip", "identity"], -- "identity" -- ), -- ["gzip", "identity"] = pick_accepted_encodings( -- [{"gzip", 1.0}], -- ["gzip", "identity"], -- "identity" -- ), -- ["identity"] = pick_accepted_encodings( -- [{"gzip", 0.0}], -- ["gzip", "identity"], -- "identity" -- ), -- ["gzip", "identity"] = pick_accepted_encodings( -- [{"gzip", 1.0}, {"deflate", 1.0}], -- ["gzip", "identity"], -- "identity" -- ), -- ["gzip", "identity"] = pick_accepted_encodings( -- [{"gzip", 0.5}, {"deflate", 1.0}], -- ["gzip", "identity"], -- "identity" -- ), -- ["identity"] = pick_accepted_encodings( -- [{"gzip", 0.0}, {"deflate", 0.0}], -- ["gzip", "identity"], -- "identity" -- ), -- ["gzip"] = pick_accepted_encodings( -- [{"gzip", 1.0}, {"deflate", 1.0}, {"identity", 0.0}], -- ["gzip", "identity"], -- "identity" -- ), -- ["gzip", "deflate", "identity"] = pick_accepted_encodings( -- [{"gzip", 1.0}, {"deflate", 1.0}], -- ["gzip", "deflate", "identity"], -- "identity" -- ), -- ["gzip", "deflate"] = pick_accepted_encodings( -- [{"gzip", 1.0}, {"deflate", 1.0}, {"identity", 0.0}], -- ["gzip", "deflate", "identity"], -- "identity" -- ), -- ["deflate", "gzip", "identity"] = pick_accepted_encodings( -- [{"gzip", 0.2}, {"deflate", 1.0}], -- ["gzip", "deflate", "identity"], -- "identity" -- ), -- ["deflate", "deflate", "gzip", "identity"] = pick_accepted_encodings( -- [{"gzip", 0.2}, {"deflate", 1.0}, {"deflate", 1.0}], -- ["gzip", "deflate", "identity"], -- "identity" -- ), -- ["deflate", "gzip", "gzip", "identity"] = pick_accepted_encodings( -- [{"gzip", 0.2}, {"deflate", 1.0}, {"gzip", 1.0}], -- ["gzip", "deflate", "identity"], -- "identity" -- ), -- ["gzip", "deflate", "gzip", "identity"] = pick_accepted_encodings( -- [{"gzip", 0.2}, {"deflate", 0.9}, {"gzip", 1.0}], -- ["gzip", "deflate", "identity"], -- "identity" -- ), -- [] = pick_accepted_encodings( -- [{"*", 0.0}], -- ["gzip", "deflate", "identity"], -- "identity" -- ), -- ["gzip", "deflate", "identity"] = pick_accepted_encodings( -- [{"*", 1.0}], -- ["gzip", "deflate", "identity"], -- "identity" -- ), -- ["gzip", "deflate", "identity"] = pick_accepted_encodings( -- [{"*", 0.6}], -- ["gzip", "deflate", "identity"], -- "identity" -- ), -- ["gzip"] = pick_accepted_encodings( -- [{"gzip", 1.0}, {"*", 0.0}], -- ["gzip", "deflate", "identity"], -- "identity" -- ), -- ["gzip", "deflate"] = pick_accepted_encodings( -- [{"gzip", 1.0}, {"deflate", 0.6}, {"*", 0.0}], -- ["gzip", "deflate", "identity"], -- "identity" -- ), -- ["deflate", "gzip"] = pick_accepted_encodings( -- [{"gzip", 0.5}, {"deflate", 1.0}, {"*", 0.0}], -- ["gzip", "deflate", "identity"], -- "identity" -- ), -- ["gzip", "identity"] = pick_accepted_encodings( -- [{"deflate", 0.0}, {"*", 1.0}], -- ["gzip", "deflate", "identity"], -- "identity" -- ), -- ["gzip", "identity"] = pick_accepted_encodings( -- [{"*", 1.0}, {"deflate", 0.0}], -- ["gzip", "deflate", "identity"], -- "identity" -- ), -- ok. -diff --git a/src/mochiweb/reloader.erl b/src/mochiweb/reloader.erl -deleted file mode 100644 -index 6835f8f..0000000 ---- a/src/mochiweb/reloader.erl -+++ /dev/null -@@ -1,123 +0,0 @@ --%% @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, #file_info{mtime = Mtime}} when Mtime >= From, 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/test/etap/test_util.erl.in b/test/etap/test_util.erl.in -index c57d7a8..948958c 100644 ---- a/test/etap/test_util.erl.in -+++ b/test/etap/test_util.erl.in -@@ -22,7 +22,7 @@ builddir() -> - "@abs_top_builddir@". - - init_code_path() -> -- Paths = ["couchdb", "ibrowse", "mochiweb"], -+ Paths = ["couchdb", "ibrowse"], - lists:foreach(fun(Name) -> - code:add_pathz(filename:join([builddir(), "src", Name])) - end, Paths). --- -1.7.1.1 - diff --git a/couchdb0.11.1-0006-Remove-bundled-ibrowse-library.patch b/couchdb0.11.1-0006-Remove-bundled-ibrowse-library.patch deleted file mode 100644 index 10b3a49..0000000 --- a/couchdb0.11.1-0006-Remove-bundled-ibrowse-library.patch +++ /dev/null @@ -1,4088 +0,0 @@ -From 71f19d084c5656ed2267dcacb7b4904c92103398 Mon Sep 17 00:00:00 2001 -From: Peter Lemenkov -Date: Wed, 14 Jul 2010 18:03:33 +0400 -Subject: [PATCH 6/9] Remove bundled ibrowse library - ---- - configure | 3 - - configure.ac | 1 - - src/Makefile.am | 2 +- - src/Makefile.in | 2 +- - src/ibrowse/Makefile.am | 49 -- - src/ibrowse/Makefile.in | 484 ------------ - src/ibrowse/ibrowse.app.in | 13 - - src/ibrowse/ibrowse.erl | 760 ------------------ - src/ibrowse/ibrowse.hrl | 12 - - src/ibrowse/ibrowse_app.erl | 64 -- - src/ibrowse/ibrowse_http_client.erl | 1476 ----------------------------------- - src/ibrowse/ibrowse_lb.erl | 216 ----- - src/ibrowse/ibrowse_lib.erl | 399 ---------- - src/ibrowse/ibrowse_sup.erl | 65 -- - src/ibrowse/ibrowse_test.erl | 377 --------- - test/etap/test_util.erl.in | 2 +- - 16 files changed, 3 insertions(+), 3922 deletions(-) - delete mode 100644 src/ibrowse/Makefile.am - delete mode 100644 src/ibrowse/Makefile.in - delete mode 100644 src/ibrowse/ibrowse.app.in - delete mode 100644 src/ibrowse/ibrowse.erl - delete mode 100644 src/ibrowse/ibrowse.hrl - delete mode 100644 src/ibrowse/ibrowse_app.erl - delete mode 100644 src/ibrowse/ibrowse_http_client.erl - delete mode 100644 src/ibrowse/ibrowse_lb.erl - delete mode 100644 src/ibrowse/ibrowse_lib.erl - delete mode 100644 src/ibrowse/ibrowse_sup.erl - delete mode 100644 src/ibrowse/ibrowse_test.erl - -diff --git a/configure b/configure -index b71f364..94303b9 100755 ---- a/configure -+++ b/configure -@@ -12261,8 +12261,6 @@ ac_config_files="$ac_config_files src/couchdb/Makefile" - - ac_config_files="$ac_config_files src/couchdb/priv/Makefile" - --ac_config_files="$ac_config_files src/ibrowse/Makefile" -- - ac_config_files="$ac_config_files test/Makefile" - - ac_config_files="$ac_config_files test/bench/Makefile" -@@ -13284,7 +13282,6 @@ do - "src/couchdb/couch.app.tpl") CONFIG_FILES="$CONFIG_FILES src/couchdb/couch.app.tpl" ;; - "src/couchdb/Makefile") CONFIG_FILES="$CONFIG_FILES src/couchdb/Makefile" ;; - "src/couchdb/priv/Makefile") CONFIG_FILES="$CONFIG_FILES src/couchdb/priv/Makefile" ;; -- "src/ibrowse/Makefile") CONFIG_FILES="$CONFIG_FILES src/ibrowse/Makefile" ;; - "test/Makefile") CONFIG_FILES="$CONFIG_FILES test/Makefile" ;; - "test/bench/Makefile") CONFIG_FILES="$CONFIG_FILES test/bench/Makefile" ;; - "test/etap/Makefile") CONFIG_FILES="$CONFIG_FILES test/etap/Makefile" ;; -diff --git a/configure.ac b/configure.ac -index b5a19ef..f867060 100644 ---- a/configure.ac -+++ b/configure.ac -@@ -387,7 +387,6 @@ AC_CONFIG_FILES([src/Makefile]) - AC_CONFIG_FILES([src/couchdb/couch.app.tpl]) - AC_CONFIG_FILES([src/couchdb/Makefile]) - AC_CONFIG_FILES([src/couchdb/priv/Makefile]) --AC_CONFIG_FILES([src/ibrowse/Makefile]) - AC_CONFIG_FILES([test/Makefile]) - AC_CONFIG_FILES([test/bench/Makefile]) - AC_CONFIG_FILES([test/etap/Makefile]) -diff --git a/src/Makefile.am b/src/Makefile.am -index 5a6646f..753b177 100644 ---- a/src/Makefile.am -+++ b/src/Makefile.am -@@ -10,4 +10,4 @@ - ## License for the specific language governing permissions and limitations under - ## the License. - --SUBDIRS = couchdb ibrowse -+SUBDIRS = couchdb -diff --git a/src/Makefile.in b/src/Makefile.in -index 2d11fc8..93c5f43 100644 ---- a/src/Makefile.in -+++ b/src/Makefile.in -@@ -248,7 +248,7 @@ version_minor = @version_minor@ - version_release = @version_release@ - version_revision = @version_revision@ - version_stage = @version_stage@ --SUBDIRS = couchdb ibrowse -+SUBDIRS = couchdb - all: all-recursive - - .SUFFIXES: -diff --git a/src/ibrowse/Makefile.am b/src/ibrowse/Makefile.am -deleted file mode 100644 -index 510f36a..0000000 ---- a/src/ibrowse/Makefile.am -+++ /dev/null -@@ -1,49 +0,0 @@ --## 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. -- --ibrowseebindir = $(localerlanglibdir)/ibrowse-1.5.2/ebin -- --ibrowse_file_collection = \ -- ibrowse.app.in \ -- ibrowse.erl \ -- ibrowse_app.erl \ -- ibrowse_http_client.erl \ -- ibrowse_lb.erl \ -- ibrowse_lib.erl \ -- ibrowse_sup.erl \ -- ibrowse_test.erl -- --ibrowseebin_make_generated_file_list = \ -- ibrowse.app \ -- ibrowse.beam \ -- ibrowse_app.beam \ -- ibrowse_http_client.beam \ -- ibrowse_lb.beam \ -- ibrowse_lib.beam \ -- ibrowse_sup.beam \ -- ibrowse_test.beam -- --ibrowseebin_DATA = \ -- $(ibrowseebin_make_generated_file_list) -- --EXTRA_DIST = \ -- $(ibrowse_file_collection) \ -- ibrowse.hrl -- --CLEANFILES = \ -- $(ibrowseebin_make_generated_file_list) -- --%.app: %.app.in -- cp $< $@ -- --%.beam: %.erl -- $(ERLC) $(ERLC_FLAGS) $< -diff --git a/src/ibrowse/Makefile.in b/src/ibrowse/Makefile.in -deleted file mode 100644 -index 47581bc..0000000 ---- a/src/ibrowse/Makefile.in -+++ /dev/null -@@ -1,484 +0,0 @@ --# Makefile.in generated by automake 1.11 from Makefile.am. --# @configure_input@ -- --# Copyright (C) 1994, 1995, 1996, 1997, 1998, 1999, 2000, 2001, 2002, --# 2003, 2004, 2005, 2006, 2007, 2008, 2009 Free Software Foundation, --# Inc. --# This Makefile.in is free software; the Free Software Foundation --# gives unlimited permission to copy and/or distribute it, --# with or without modifications, as long as this notice is preserved. -- --# This program is distributed in the hope that it will be useful, --# but WITHOUT ANY WARRANTY, to the extent permitted by law; without --# even the implied warranty of MERCHANTABILITY or FITNESS FOR A --# PARTICULAR PURPOSE. -- --@SET_MAKE@ -- --VPATH = @srcdir@ --pkgdatadir = $(datadir)/@PACKAGE@ --pkgincludedir = $(includedir)/@PACKAGE@ --pkglibdir = $(libdir)/@PACKAGE@ --pkglibexecdir = $(libexecdir)/@PACKAGE@ --am__cd = CDPATH="$${ZSH_VERSION+.}$(PATH_SEPARATOR)" && cd --install_sh_DATA = $(install_sh) -c -m 644 --install_sh_PROGRAM = $(install_sh) -c --install_sh_SCRIPT = $(install_sh) -c --INSTALL_HEADER = $(INSTALL_DATA) --transform = $(program_transform_name) --NORMAL_INSTALL = : --PRE_INSTALL = : --POST_INSTALL = : --NORMAL_UNINSTALL = : --PRE_UNINSTALL = : --POST_UNINSTALL = : --build_triplet = @build@ --host_triplet = @host@ --subdir = src/ibrowse --DIST_COMMON = $(srcdir)/Makefile.am $(srcdir)/Makefile.in --ACLOCAL_M4 = $(top_srcdir)/aclocal.m4 --am__aclocal_m4_deps = $(top_srcdir)/m4/ac_check_curl.m4 \ -- $(top_srcdir)/m4/ac_check_icu.m4 $(top_srcdir)/m4/libtool.m4 \ -- $(top_srcdir)/m4/ltoptions.m4 $(top_srcdir)/m4/ltsugar.m4 \ -- $(top_srcdir)/m4/ltversion.m4 $(top_srcdir)/m4/lt~obsolete.m4 \ -- $(top_srcdir)/acinclude.m4 $(top_srcdir)/configure.ac --am__configure_deps = $(am__aclocal_m4_deps) $(CONFIGURE_DEPENDENCIES) \ -- $(ACLOCAL_M4) --mkinstalldirs = $(install_sh) -d --CONFIG_HEADER = $(top_builddir)/config.h --CONFIG_CLEAN_FILES = --CONFIG_CLEAN_VPATH_FILES = --SOURCES = --DIST_SOURCES = --am__vpath_adj_setup = srcdirstrip=`echo "$(srcdir)" | sed 's|.|.|g'`; --am__vpath_adj = case $$p in \ -- $(srcdir)/*) f=`echo "$$p" | sed "s|^$$srcdirstrip/||"`;; \ -- *) f=$$p;; \ -- esac; --am__strip_dir = f=`echo $$p | sed -e 's|^.*/||'`; --am__install_max = 40 --am__nobase_strip_setup = \ -- srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*|]/\\\\&/g'` --am__nobase_strip = \ -- for p in $$list; do echo "$$p"; done | sed -e "s|$$srcdirstrip/||" --am__nobase_list = $(am__nobase_strip_setup); \ -- for p in $$list; do echo "$$p $$p"; done | \ -- sed "s| $$srcdirstrip/| |;"' / .*\//!s/ .*/ ./; s,\( .*\)/[^/]*$$,\1,' | \ -- $(AWK) 'BEGIN { files["."] = "" } { files[$$2] = files[$$2] " " $$1; \ -- if (++n[$$2] == $(am__install_max)) \ -- { print $$2, files[$$2]; n[$$2] = 0; files[$$2] = "" } } \ -- END { for (dir in files) print dir, files[dir] }' --am__base_list = \ -- sed '$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;$$!N;s/\n/ /g' | \ -- sed '$$!N;$$!N;$$!N;$$!N;s/\n/ /g' --am__installdirs = "$(DESTDIR)$(ibrowseebindir)" --DATA = $(ibrowseebin_DATA) --DISTFILES = $(DIST_COMMON) $(DIST_SOURCES) $(TEXINFOS) $(EXTRA_DIST) --ACLOCAL = @ACLOCAL@ --AMTAR = @AMTAR@ --AR = @AR@ --AUTOCONF = @AUTOCONF@ --AUTOHEADER = @AUTOHEADER@ --AUTOMAKE = @AUTOMAKE@ --AWK = @AWK@ --CC = @CC@ --CCDEPMODE = @CCDEPMODE@ --CFLAGS = @CFLAGS@ --CPP = @CPP@ --CPPFLAGS = @CPPFLAGS@ --CURL_CFLAGS = @CURL_CFLAGS@ --CURL_CONFIG = @CURL_CONFIG@ --CURL_LDFLAGS = @CURL_LDFLAGS@ --CURL_LIBS = @CURL_LIBS@ --CYGPATH_W = @CYGPATH_W@ --DEFS = @DEFS@ --DEPDIR = @DEPDIR@ --DSYMUTIL = @DSYMUTIL@ --DUMPBIN = @DUMPBIN@ --ECHO_C = @ECHO_C@ --ECHO_N = @ECHO_N@ --ECHO_T = @ECHO_T@ --EGREP = @EGREP@ --ERL = @ERL@ --ERLC = @ERLC@ --ERLC_FLAGS = @ERLC_FLAGS@ --EXEEXT = @EXEEXT@ --FGREP = @FGREP@ --FLAGS = @FLAGS@ --GREP = @GREP@ --HELP2MAN_EXECUTABLE = @HELP2MAN_EXECUTABLE@ --ICU_CFLAGS = @ICU_CFLAGS@ --ICU_CONFIG = @ICU_CONFIG@ --ICU_CXXFLAGS = @ICU_CXXFLAGS@ --ICU_LIBS = @ICU_LIBS@ --ICU_LOCAL_BIN = @ICU_LOCAL_BIN@ --ICU_LOCAL_CFLAGS = @ICU_LOCAL_CFLAGS@ --ICU_LOCAL_LDFLAGS = @ICU_LOCAL_LDFLAGS@ --INNO_COMPILER_EXECUTABLE = @INNO_COMPILER_EXECUTABLE@ --INSTALL = @INSTALL@ --INSTALL_DATA = @INSTALL_DATA@ --INSTALL_PROGRAM = @INSTALL_PROGRAM@ --INSTALL_SCRIPT = @INSTALL_SCRIPT@ --INSTALL_STRIP_PROGRAM = @INSTALL_STRIP_PROGRAM@ --JSLIB = @JSLIB@ --JS_LIB_BASE = @JS_LIB_BASE@ --JS_LIB_BINARY = @JS_LIB_BINARY@ --JS_LIB_DIR = @JS_LIB_DIR@ --LD = @LD@ --LDFLAGS = @LDFLAGS@ --LIBOBJS = @LIBOBJS@ --LIBS = @LIBS@ --LIBTOOL = @LIBTOOL@ --LIPO = @LIPO@ --LN_S = @LN_S@ --LTLIBOBJS = @LTLIBOBJS@ --MAKEINFO = @MAKEINFO@ --MKDIR_P = @MKDIR_P@ --NM = @NM@ --NMEDIT = @NMEDIT@ --OBJDUMP = @OBJDUMP@ --OBJEXT = @OBJEXT@ --OTOOL = @OTOOL@ --OTOOL64 = @OTOOL64@ --PACKAGE = @PACKAGE@ --PACKAGE_BUGREPORT = @PACKAGE_BUGREPORT@ --PACKAGE_NAME = @PACKAGE_NAME@ --PACKAGE_STRING = @PACKAGE_STRING@ --PACKAGE_TARNAME = @PACKAGE_TARNAME@ --PACKAGE_URL = @PACKAGE_URL@ --PACKAGE_VERSION = @PACKAGE_VERSION@ --PATH_SEPARATOR = @PATH_SEPARATOR@ --RANLIB = @RANLIB@ --SED = @SED@ --SET_MAKE = @SET_MAKE@ --SHELL = @SHELL@ --STRIP = @STRIP@ --VERSION = @VERSION@ --abs_builddir = @abs_builddir@ --abs_srcdir = @abs_srcdir@ --abs_top_builddir = @abs_top_builddir@ --abs_top_srcdir = @abs_top_srcdir@ --ac_ct_CC = @ac_ct_CC@ --ac_ct_DUMPBIN = @ac_ct_DUMPBIN@ --am__include = @am__include@ --am__leading_dot = @am__leading_dot@ --am__quote = @am__quote@ --am__tar = @am__tar@ --am__untar = @am__untar@ --bindir = @bindir@ --bug_uri = @bug_uri@ --build = @build@ --build_alias = @build_alias@ --build_cpu = @build_cpu@ --build_os = @build_os@ --build_vendor = @build_vendor@ --builddir = @builddir@ --datadir = @datadir@ --datarootdir = @datarootdir@ --docdir = @docdir@ --dvidir = @dvidir@ --exec_prefix = @exec_prefix@ --host = @host@ --host_alias = @host_alias@ --host_cpu = @host_cpu@ --host_os = @host_os@ --host_vendor = @host_vendor@ --htmldir = @htmldir@ --includedir = @includedir@ --infodir = @infodir@ --initdir = @initdir@ --install_sh = @install_sh@ --launchddir = @launchddir@ --libdir = @libdir@ --libexecdir = @libexecdir@ --localconfdir = @localconfdir@ --localdatadir = @localdatadir@ --localdocdir = @localdocdir@ --localedir = @localedir@ --localerlanglibdir = @localerlanglibdir@ --locallibbindir = @locallibbindir@ --locallibdir = @locallibdir@ --localstatedir = @localstatedir@ --localstatelibdir = @localstatelibdir@ --localstatelogdir = @localstatelogdir@ --localstaterundir = @localstaterundir@ --lt_ECHO = @lt_ECHO@ --mandir = @mandir@ --mkdir_p = @mkdir_p@ --msvc_redist_dir = @msvc_redist_dir@ --msvc_redist_name = @msvc_redist_name@ --oldincludedir = @oldincludedir@ --openssl_bin_dir = @openssl_bin_dir@ --package_author_address = @package_author_address@ --package_author_name = @package_author_name@ --package_identifier = @package_identifier@ --package_name = @package_name@ --package_tarname = @package_tarname@ --pdfdir = @pdfdir@ --prefix = @prefix@ --program_transform_name = @program_transform_name@ --psdir = @psdir@ --sbindir = @sbindir@ --sharedstatedir = @sharedstatedir@ --srcdir = @srcdir@ --sysconfdir = @sysconfdir@ --target_alias = @target_alias@ --top_build_prefix = @top_build_prefix@ --top_builddir = @top_builddir@ --top_srcdir = @top_srcdir@ --version = @version@ --version_major = @version_major@ --version_minor = @version_minor@ --version_release = @version_release@ --version_revision = @version_revision@ --version_stage = @version_stage@ --ibrowseebindir = $(localerlanglibdir)/ibrowse-1.5.2/ebin --ibrowse_file_collection = \ -- ibrowse.app.in \ -- ibrowse.erl \ -- ibrowse_app.erl \ -- ibrowse_http_client.erl \ -- ibrowse_lb.erl \ -- ibrowse_lib.erl \ -- ibrowse_sup.erl \ -- ibrowse_test.erl -- --ibrowseebin_make_generated_file_list = \ -- ibrowse.app \ -- ibrowse.beam \ -- ibrowse_app.beam \ -- ibrowse_http_client.beam \ -- ibrowse_lb.beam \ -- ibrowse_lib.beam \ -- ibrowse_sup.beam \ -- ibrowse_test.beam -- --ibrowseebin_DATA = \ -- $(ibrowseebin_make_generated_file_list) -- --EXTRA_DIST = \ -- $(ibrowse_file_collection) \ -- ibrowse.hrl -- --CLEANFILES = \ -- $(ibrowseebin_make_generated_file_list) -- --all: all-am -- --.SUFFIXES: --$(srcdir)/Makefile.in: $(srcdir)/Makefile.am $(am__configure_deps) -- @for dep in $?; do \ -- case '$(am__configure_deps)' in \ -- *$$dep*) \ -- ( cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh ) \ -- && { if test -f $@; then exit 0; else break; fi; }; \ -- exit 1;; \ -- esac; \ -- done; \ -- echo ' cd $(top_srcdir) && $(AUTOMAKE) --foreign src/ibrowse/Makefile'; \ -- $(am__cd) $(top_srcdir) && \ -- $(AUTOMAKE) --foreign src/ibrowse/Makefile --.PRECIOUS: Makefile --Makefile: $(srcdir)/Makefile.in $(top_builddir)/config.status -- @case '$?' in \ -- *config.status*) \ -- cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh;; \ -- *) \ -- echo ' cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe)'; \ -- cd $(top_builddir) && $(SHELL) ./config.status $(subdir)/$@ $(am__depfiles_maybe);; \ -- esac; -- --$(top_builddir)/config.status: $(top_srcdir)/configure $(CONFIG_STATUS_DEPENDENCIES) -- cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh -- --$(top_srcdir)/configure: $(am__configure_deps) -- cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh --$(ACLOCAL_M4): $(am__aclocal_m4_deps) -- cd $(top_builddir) && $(MAKE) $(AM_MAKEFLAGS) am--refresh --$(am__aclocal_m4_deps): -- --mostlyclean-libtool: -- -rm -f *.lo -- --clean-libtool: -- -rm -rf .libs _libs --install-ibrowseebinDATA: $(ibrowseebin_DATA) -- @$(NORMAL_INSTALL) -- test -z "$(ibrowseebindir)" || $(MKDIR_P) "$(DESTDIR)$(ibrowseebindir)" -- @list='$(ibrowseebin_DATA)'; test -n "$(ibrowseebindir)" || list=; \ -- for p in $$list; do \ -- if test -f "$$p"; then d=; else d="$(srcdir)/"; fi; \ -- echo "$$d$$p"; \ -- done | $(am__base_list) | \ -- while read files; do \ -- echo " $(INSTALL_DATA) $$files '$(DESTDIR)$(ibrowseebindir)'"; \ -- $(INSTALL_DATA) $$files "$(DESTDIR)$(ibrowseebindir)" || exit $$?; \ -- done -- --uninstall-ibrowseebinDATA: -- @$(NORMAL_UNINSTALL) -- @list='$(ibrowseebin_DATA)'; test -n "$(ibrowseebindir)" || list=; \ -- files=`for p in $$list; do echo $$p; done | sed -e 's|^.*/||'`; \ -- test -n "$$files" || exit 0; \ -- echo " ( cd '$(DESTDIR)$(ibrowseebindir)' && rm -f" $$files ")"; \ -- cd "$(DESTDIR)$(ibrowseebindir)" && rm -f $$files --tags: TAGS --TAGS: -- --ctags: CTAGS --CTAGS: -- -- --distdir: $(DISTFILES) -- @srcdirstrip=`echo "$(srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ -- topsrcdirstrip=`echo "$(top_srcdir)" | sed 's/[].[^$$\\*]/\\\\&/g'`; \ -- list='$(DISTFILES)'; \ -- dist_files=`for file in $$list; do echo $$file; done | \ -- sed -e "s|^$$srcdirstrip/||;t" \ -- -e "s|^$$topsrcdirstrip/|$(top_builddir)/|;t"`; \ -- case $$dist_files in \ -- */*) $(MKDIR_P) `echo "$$dist_files" | \ -- sed '/\//!d;s|^|$(distdir)/|;s,/[^/]*$$,,' | \ -- sort -u` ;; \ -- esac; \ -- for file in $$dist_files; do \ -- if test -f $$file || test -d $$file; then d=.; else d=$(srcdir); fi; \ -- if test -d $$d/$$file; then \ -- dir=`echo "/$$file" | sed -e 's,/[^/]*$$,,'`; \ -- if test -d "$(distdir)/$$file"; then \ -- find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ -- fi; \ -- if test -d $(srcdir)/$$file && test $$d != $(srcdir); then \ -- cp -fpR $(srcdir)/$$file "$(distdir)$$dir" || exit 1; \ -- find "$(distdir)/$$file" -type d ! -perm -700 -exec chmod u+rwx {} \;; \ -- fi; \ -- cp -fpR $$d/$$file "$(distdir)$$dir" || exit 1; \ -- else \ -- test -f "$(distdir)/$$file" \ -- || cp -p $$d/$$file "$(distdir)/$$file" \ -- || exit 1; \ -- fi; \ -- done --check-am: all-am --check: check-am --all-am: Makefile $(DATA) --installdirs: -- for dir in "$(DESTDIR)$(ibrowseebindir)"; do \ -- test -z "$$dir" || $(MKDIR_P) "$$dir"; \ -- done --install: install-am --install-exec: install-exec-am --install-data: install-data-am --uninstall: uninstall-am -- --install-am: all-am -- @$(MAKE) $(AM_MAKEFLAGS) install-exec-am install-data-am -- --installcheck: installcheck-am --install-strip: -- $(MAKE) $(AM_MAKEFLAGS) INSTALL_PROGRAM="$(INSTALL_STRIP_PROGRAM)" \ -- install_sh_PROGRAM="$(INSTALL_STRIP_PROGRAM)" INSTALL_STRIP_FLAG=-s \ -- `test -z '$(STRIP)' || \ -- echo "INSTALL_PROGRAM_ENV=STRIPPROG='$(STRIP)'"` install --mostlyclean-generic: -- --clean-generic: -- -test -z "$(CLEANFILES)" || rm -f $(CLEANFILES) -- --distclean-generic: -- -test -z "$(CONFIG_CLEAN_FILES)" || rm -f $(CONFIG_CLEAN_FILES) -- -test . = "$(srcdir)" || test -z "$(CONFIG_CLEAN_VPATH_FILES)" || rm -f $(CONFIG_CLEAN_VPATH_FILES) -- --maintainer-clean-generic: -- @echo "This command is intended for maintainers to use" -- @echo "it deletes files that may require special tools to rebuild." --clean: clean-am -- --clean-am: clean-generic clean-libtool mostlyclean-am -- --distclean: distclean-am -- -rm -f Makefile --distclean-am: clean-am distclean-generic -- --dvi: dvi-am -- --dvi-am: -- --html: html-am -- --html-am: -- --info: info-am -- --info-am: -- --install-data-am: install-ibrowseebinDATA -- --install-dvi: install-dvi-am -- --install-dvi-am: -- --install-exec-am: -- --install-html: install-html-am -- --install-html-am: -- --install-info: install-info-am -- --install-info-am: -- --install-man: -- --install-pdf: install-pdf-am -- --install-pdf-am: -- --install-ps: install-ps-am -- --install-ps-am: -- --installcheck-am: -- --maintainer-clean: maintainer-clean-am -- -rm -f Makefile --maintainer-clean-am: distclean-am maintainer-clean-generic -- --mostlyclean: mostlyclean-am -- --mostlyclean-am: mostlyclean-generic mostlyclean-libtool -- --pdf: pdf-am -- --pdf-am: -- --ps: ps-am -- --ps-am: -- --uninstall-am: uninstall-ibrowseebinDATA -- --.MAKE: install-am install-strip -- --.PHONY: all all-am check check-am clean clean-generic clean-libtool \ -- distclean distclean-generic distclean-libtool distdir dvi \ -- dvi-am html html-am info info-am install install-am \ -- install-data install-data-am install-dvi install-dvi-am \ -- install-exec install-exec-am install-html install-html-am \ -- install-ibrowseebinDATA install-info install-info-am \ -- install-man install-pdf install-pdf-am install-ps \ -- install-ps-am install-strip installcheck installcheck-am \ -- installdirs maintainer-clean maintainer-clean-generic \ -- mostlyclean mostlyclean-generic mostlyclean-libtool pdf pdf-am \ -- ps ps-am uninstall uninstall-am uninstall-ibrowseebinDATA -- -- --%.app: %.app.in -- cp $< $@ -- --%.beam: %.erl -- $(ERLC) $(ERLC_FLAGS) $< -- --# Tell versions [3.59,3.63) of GNU make to not export all variables. --# Otherwise a system limit (for SysV at least) may be exceeded. --.NOEXPORT: -diff --git a/src/ibrowse/ibrowse.app.in b/src/ibrowse/ibrowse.app.in -deleted file mode 100644 -index 4f43dd9..0000000 ---- a/src/ibrowse/ibrowse.app.in -+++ /dev/null -@@ -1,13 +0,0 @@ --{application, ibrowse, -- [{description, "HTTP client application"}, -- {vsn, "1.5.1"}, -- {modules, [ ibrowse, -- ibrowse_http_client, -- ibrowse_app, -- ibrowse_sup, -- ibrowse_lib, -- ibrowse_lb ]}, -- {registered, []}, -- {applications, [kernel,stdlib,sasl]}, -- {env, []}, -- {mod, {ibrowse_app, []}}]}. -diff --git a/src/ibrowse/ibrowse.erl b/src/ibrowse/ibrowse.erl -deleted file mode 100644 -index 1913ef5..0000000 ---- a/src/ibrowse/ibrowse.erl -+++ /dev/null -@@ -1,760 +0,0 @@ --%%%------------------------------------------------------------------- --%%% File : ibrowse.erl --%%% Author : Chandrashekhar Mullaparthi --%%% Description : Load balancer process for HTTP client connections. --%%% --%%% Created : 11 Oct 2003 by Chandrashekhar Mullaparthi --%%%------------------------------------------------------------------- --%% @author Chandrashekhar Mullaparthi --%% @copyright 2005-2009 Chandrashekhar Mullaparthi --%% @version 1.5.2 --%% @doc The ibrowse application implements an HTTP 1.1 client. This --%% module implements the API of the HTTP client. There is one named --%% process called 'ibrowse' which assists in load balancing and maintaining configuration. There is one load balancing process per unique webserver. There is --%% one process to handle one TCP connection to a webserver --%% (implemented in the module ibrowse_http_client). Multiple connections to a --%% webserver are setup based on the settings for each webserver. The --%% ibrowse process also determines which connection to pipeline a --%% certain request on. The functions to call are send_req/3, --%% send_req/4, send_req/5, send_req/6. --%% --%%

Here are a few sample invocations.

--%% --%% --%% ibrowse:send_req("http://intranet/messenger/", [], get). --%%

--%% --%% ibrowse:send_req("http://www.google.com/", [], get, [], --%% [{proxy_user, "XXXXX"}, --%% {proxy_password, "XXXXX"}, --%% {proxy_host, "proxy"}, --%% {proxy_port, 8080}], 1000). --%%

--%% --%%ibrowse:send_req("http://www.erlang.org/download/otp_src_R10B-3.tar.gz", [], get, [], --%% [{proxy_user, "XXXXX"}, --%% {proxy_password, "XXXXX"}, --%% {proxy_host, "proxy"}, --%% {proxy_port, 8080}, --%% {save_response_to_file, true}], 1000). --%%

--%% --%% ibrowse:send_req("http://www.erlang.org", [], head). --%% --%%

--%% ibrowse:send_req("http://www.sun.com", [], options). --%% --%%

--%% ibrowse:send_req("http://www.bbc.co.uk", [], trace). --%% --%%

--%% ibrowse:send_req("http://www.google.com", [], get, [], --%% [{stream_to, self()}]). --%%
--%% --%%

A driver exists which implements URL encoding in C, but the --%% speed achieved using only erlang has been good enough, so the --%% driver isn't actually used.

-- ---module(ibrowse). ---vsn('$Id: ibrowse.erl,v 1.8 2009/07/01 22:43:19 chandrusf Exp $ '). -- ---behaviour(gen_server). --%%-------------------------------------------------------------------- --%% Include files --%%-------------------------------------------------------------------- -- --%%-------------------------------------------------------------------- --%% External exports ---export([start_link/0, start/0, stop/0]). -- --%% gen_server callbacks ---export([init/1, handle_call/3, handle_cast/2, handle_info/2, -- terminate/2, code_change/3]). -- --%% API interface ---export([ -- rescan_config/0, -- rescan_config/1, -- get_config_value/1, -- get_config_value/2, -- spawn_worker_process/2, -- spawn_link_worker_process/2, -- stop_worker_process/1, -- send_req/3, -- send_req/4, -- send_req/5, -- send_req/6, -- send_req_direct/4, -- send_req_direct/5, -- send_req_direct/6, -- send_req_direct/7, -- stream_next/1, -- set_max_sessions/3, -- set_max_pipeline_size/3, -- set_dest/3, -- trace_on/0, -- trace_off/0, -- trace_on/2, -- trace_off/2, -- all_trace_off/0, -- show_dest_status/0, -- show_dest_status/2 -- ]). -- ---ifdef(debug). ---compile(export_all). ---endif. -- ---import(ibrowse_lib, [ -- parse_url/1, -- get_value/3, -- do_trace/2 -- ]). -- ---record(state, {trace = false}). -- ---include("ibrowse.hrl"). ---include_lib("stdlib/include/ms_transform.hrl"). -- ---define(DEF_MAX_SESSIONS,10). ---define(DEF_MAX_PIPELINE_SIZE,10). -- --%%==================================================================== --%% External functions --%%==================================================================== --%%-------------------------------------------------------------------- --%% Function: start_link/0 --%% Description: Starts the server --%%-------------------------------------------------------------------- --%% @doc Starts the ibrowse process linked to the calling process. Usually invoked by the supervisor ibrowse_sup --%% @spec start_link() -> {ok, pid()} --start_link() -> -- gen_server:start_link({local, ?MODULE}, ?MODULE, [], []). -- --%% @doc Starts the ibrowse process without linking. Useful when testing using the shell --start() -> -- gen_server:start({local, ?MODULE}, ?MODULE, [], [{debug, []}]). -- --%% @doc Stop the ibrowse process. Useful when testing using the shell. --stop() -> -- catch gen_server:call(ibrowse, stop). -- --%% @doc This is the basic function to send a HTTP request. --%% The Status return value indicates the HTTP status code returned by the webserver --%% @spec send_req(Url::string(), Headers::headerList(), Method::method()) -> response() --%% headerList() = [{header(), value()}] --%% header() = atom() | string() --%% value() = term() --%% method() = get | post | head | options | put | delete | trace | mkcol | propfind | proppatch | lock | unlock | move | copy --%% Status = string() --%% ResponseHeaders = [respHeader()] --%% respHeader() = {headerName(), headerValue()} --%% headerName() = string() --%% headerValue() = string() --%% response() = {ok, Status, ResponseHeaders, ResponseBody} | {ibrowse_req_id, req_id() } | {error, Reason} --%% req_id() = term() --%% ResponseBody = string() | {file, Filename} --%% Reason = term() --send_req(Url, Headers, Method) -> -- send_req(Url, Headers, Method, [], []). -- --%% @doc Same as send_req/3. --%% If a list is specified for the body it has to be a flat list. The body can also be a fun/0 or a fun/1.
--%% If fun/0, the connection handling process will repeatdely call the fun until it returns an error or eof.
Fun() = {ok, Data} | eof

--%% If fun/1, the connection handling process will repeatedly call the fun with the supplied state until it returns an error or eof.
Fun(State) = {ok, Data} | {ok, Data, NewState} | eof
--%% @spec send_req(Url, Headers, Method::method(), Body::body()) -> response() --%% body() = [] | string() | binary() | fun_arity_0() | {fun_arity_1(), initial_state()} --%% initial_state() = term() --send_req(Url, Headers, Method, Body) -> -- send_req(Url, Headers, Method, Body, []). -- --%% @doc Same as send_req/4. --%% For a description of SSL Options, look in the ssl manpage. If the --%% HTTP Version to use is not specified, the default is 1.1. --%%
--%%

The host_header option is useful in the case where ibrowse is --%% connecting to a component such as stunnel which then sets up a --%% secure connection to a webserver. In this case, the URL supplied to --%% ibrowse must have the stunnel host/port details, but that won't --%% make sense to the destination webserver. This option can then be --%% used to specify what should go in the Host header in --%% the request.

--%%
    --%%
  • The stream_to option can be used to have the HTTP --%% response streamed to a process as messages as data arrives on the --%% socket. If the calling process wishes to control the rate at which --%% data is received from the server, the option {stream_to, --%% {process(), once}} can be specified. The calling process --%% will have to invoke ibrowse:stream_next(Request_id) to --%% receive the next packet.
  • --%% --%%
  • When both the options save_response_to_file and stream_to --%% are specified, the former takes precedence.
  • --%% --%%
  • For the save_response_to_file option, the response body is saved to --%% file only if the status code is in the 200-299 range. If not, the response body is returned --%% as a string.
  • --%%
  • Whenever an error occurs in the processing of a request, ibrowse will return as much --%% information as it has, such as HTTP Status Code and HTTP Headers. When this happens, the response --%% is of the form {error, {Reason, {stat_code, StatusCode}, HTTP_headers}}
  • --%% --%%
  • The inactivity_timeout option is useful when --%% dealing with large response bodies and/or slow links. In these --%% cases, it might be hard to estimate how long a request will take to --%% complete. In such cases, the client might want to timeout if no --%% data has been received on the link for a certain time interval.
  • --%% --%%
  • --%% The connect_timeout option is to specify how long the --%% client process should wait for connection establishment. This is --%% useful in scenarios where connections to servers are usually setup --%% very fast, but responses might take much longer compared to --%% connection setup. In such cases, it is better for the calling --%% process to timeout faster if there is a problem (DNS lookup --%% delays/failures, network routing issues, etc). The total timeout --%% value specified for the request will enforced. To illustrate using --%% an example: --%% --%% ibrowse:send_req("http://www.example.com/cgi-bin/request", [], get, [], [{connect_timeout, 100}], 1000). --%% --%% In the above invocation, if the connection isn't established within --%% 100 milliseconds, the request will fail with --%% {error, conn_failed}.
    --%% If connection setup succeeds, the total time allowed for the --%% request to complete will be 1000 milliseconds minus the time taken --%% for connection setup. --%%
  • --%%
--%% --%% @spec send_req(Url::string(), Headers::headerList(), Method::method(), Body::body(), Options::optionList()) -> response() --%% optionList() = [option()] --%% option() = {max_sessions, integer()} | --%% {response_format,response_format()}| --%% {stream_chunk_size, integer()} | --%% {max_pipeline_size, integer()} | --%% {trace, boolean()} | --%% {is_ssl, boolean()} | --%% {ssl_options, [SSLOpt]} | --%% {pool_name, atom()} | --%% {proxy_host, string()} | --%% {proxy_port, integer()} | --%% {proxy_user, string()} | --%% {proxy_password, string()} | --%% {use_absolute_uri, boolean()} | --%% {basic_auth, {username(), password()}} | --%% {cookie, string()} | --%% {content_length, integer()} | --%% {content_type, string()} | --%% {save_response_to_file, srtf()} | --%% {stream_to, stream_to()} | --%% {http_vsn, {MajorVsn, MinorVsn}} | --%% {host_header, string()} | --%% {inactivity_timeout, integer()} | --%% {connect_timeout, integer()} | --%% {transfer_encoding, {chunked, ChunkSize}} --%% --%% stream_to() = process() | {process(), once} --%% process() = pid() | atom() --%% username() = string() --%% password() = string() --%% SSLOpt = term() --%% ChunkSize = integer() --%% srtf() = boolean() | filename() --%% filename() = string() --%% response_format() = list | binary --send_req(Url, Headers, Method, Body, Options) -> -- send_req(Url, Headers, Method, Body, Options, 30000). -- --%% @doc Same as send_req/5. --%% All timeout values are in milliseconds. --%% @spec send_req(Url, Headers::headerList(), Method::method(), Body::body(), Options::optionList(), Timeout) -> response() --%% Timeout = integer() | infinity --send_req(Url, Headers, Method, Body, Options, Timeout) -> -- case catch parse_url(Url) of -- #url{host = Host, -- port = Port, -- protocol = Protocol} = Parsed_url -> -- Lb_pid = case ets:lookup(ibrowse_lb, {Host, Port}) of -- [] -> -- get_lb_pid(Parsed_url); -- [#lb_pid{pid = Lb_pid_1}] -> -- Lb_pid_1 -- end, -- Max_sessions = get_max_sessions(Host, Port, Options), -- Max_pipeline_size = get_max_pipeline_size(Host, Port, Options), -- Options_1 = merge_options(Host, Port, Options), -- {SSLOptions, IsSSL} = -- case (Protocol == https) orelse -- get_value(is_ssl, Options_1, false) of -- false -> {[], false}; -- true -> {get_value(ssl_options, Options_1, []), true} -- end, -- case ibrowse_lb:spawn_connection(Lb_pid, Parsed_url, -- Max_sessions, -- Max_pipeline_size, -- {SSLOptions, IsSSL}) of -- {ok, Conn_Pid} -> -- do_send_req(Conn_Pid, Parsed_url, Headers, -- Method, Body, Options_1, Timeout); -- Err -> -- Err -- end; -- Err -> -- {error, {url_parsing_failed, Err}} -- end. -- --merge_options(Host, Port, Options) -> -- Config_options = get_config_value({options, Host, Port}, []), -- lists:foldl( -- fun({Key, Val}, Acc) -> -- case lists:keysearch(Key, 1, Options) of -- false -> -- [{Key, Val} | Acc]; -- _ -> -- Acc -- end -- end, Options, Config_options). -- --get_lb_pid(Url) -> -- gen_server:call(?MODULE, {get_lb_pid, Url}). -- --get_max_sessions(Host, Port, Options) -> -- get_value(max_sessions, Options, -- get_config_value({max_sessions, Host, Port}, ?DEF_MAX_SESSIONS)). -- --get_max_pipeline_size(Host, Port, Options) -> -- get_value(max_pipeline_size, Options, -- get_config_value({max_pipeline_size, Host, Port}, ?DEF_MAX_PIPELINE_SIZE)). -- --%% @doc Deprecated. Use set_max_sessions/3 and set_max_pipeline_size/3 --%% for achieving the same effect. --set_dest(Host, Port, [{max_sessions, Max} | T]) -> -- set_max_sessions(Host, Port, Max), -- set_dest(Host, Port, T); --set_dest(Host, Port, [{max_pipeline_size, Max} | T]) -> -- set_max_pipeline_size(Host, Port, Max), -- set_dest(Host, Port, T); --set_dest(Host, Port, [{trace, Bool} | T]) when Bool == true; Bool == false -> -- ibrowse ! {trace, true, Host, Port}, -- set_dest(Host, Port, T); --set_dest(_Host, _Port, [H | _]) -> -- exit({invalid_option, H}); --set_dest(_, _, []) -> -- ok. -- --%% @doc Set the maximum number of connections allowed to a specific Host:Port. --%% @spec set_max_sessions(Host::string(), Port::integer(), Max::integer()) -> ok --set_max_sessions(Host, Port, Max) when is_integer(Max), Max > 0 -> -- gen_server:call(?MODULE, {set_config_value, {max_sessions, Host, Port}, Max}). -- --%% @doc Set the maximum pipeline size for each connection to a specific Host:Port. --%% @spec set_max_pipeline_size(Host::string(), Port::integer(), Max::integer()) -> ok --set_max_pipeline_size(Host, Port, Max) when is_integer(Max), Max > 0 -> -- gen_server:call(?MODULE, {set_config_value, {max_pipeline_size, Host, Port}, Max}). -- --do_send_req(Conn_Pid, Parsed_url, Headers, Method, Body, Options, Timeout) -> -- case catch ibrowse_http_client:send_req(Conn_Pid, Parsed_url, -- Headers, Method, ensure_bin(Body), -- Options, Timeout) of -- {'EXIT', {timeout, _}} -> -- {error, req_timedout}; -- {'EXIT', Reason} -> -- {error, {'EXIT', Reason}}; -- {ok, St_code, Headers, Body} = Ret when is_binary(Body) -> -- case get_value(response_format, Options, list) of -- list -> -- {ok, St_code, Headers, binary_to_list(Body)}; -- binary -> -- Ret -- end; -- Ret -> -- Ret -- end. -- --ensure_bin(L) when is_list(L) -> list_to_binary(L); --ensure_bin(B) when is_binary(B) -> B; --ensure_bin(Fun) when is_function(Fun) -> Fun; --ensure_bin({Fun}) when is_function(Fun) -> Fun; --ensure_bin({Fun, _} = Body) when is_function(Fun) -> Body. -- --%% @doc Creates a HTTP client process to the specified Host:Port which --%% is not part of the load balancing pool. This is useful in cases --%% where some requests to a webserver might take a long time whereas --%% some might take a very short time. To avoid getting these quick --%% requests stuck in the pipeline behind time consuming requests, use --%% this function to get a handle to a connection process.
--%% Note: Calling this function only creates a worker process. No connection --%% is setup. The connection attempt is made only when the first --%% request is sent via any of the send_req_direct/4,5,6,7 functions.
--%% Note: It is the responsibility of the calling process to control --%% pipeline size on such connections. --%% --%% @spec spawn_worker_process(Host::string(), Port::integer()) -> {ok, pid()} --spawn_worker_process(Host, Port) -> -- ibrowse_http_client:start({Host, Port}). -- --%% @doc Same as spawn_worker_process/2 except the the calling process --%% is linked to the worker process which is spawned. --spawn_link_worker_process(Host, Port) -> -- ibrowse_http_client:start_link({Host, Port}). -- --%% @doc Terminate a worker process spawned using --%% spawn_worker_process/2 or spawn_link_worker_process/2. Requests in --%% progress will get the error response
{error, closing_on_request}
--%% @spec stop_worker_process(Conn_pid::pid()) -> ok --stop_worker_process(Conn_pid) -> -- ibrowse_http_client:stop(Conn_pid). -- --%% @doc Same as send_req/3 except that the first argument is the PID --%% returned by spawn_worker_process/2 or spawn_link_worker_process/2 --send_req_direct(Conn_pid, Url, Headers, Method) -> -- send_req_direct(Conn_pid, Url, Headers, Method, [], []). -- --%% @doc Same as send_req/4 except that the first argument is the PID --%% returned by spawn_worker_process/2 or spawn_link_worker_process/2 --send_req_direct(Conn_pid, Url, Headers, Method, Body) -> -- send_req_direct(Conn_pid, Url, Headers, Method, Body, []). -- --%% @doc Same as send_req/5 except that the first argument is the PID --%% returned by spawn_worker_process/2 or spawn_link_worker_process/2 --send_req_direct(Conn_pid, Url, Headers, Method, Body, Options) -> -- send_req_direct(Conn_pid, Url, Headers, Method, Body, Options, 30000). -- --%% @doc Same as send_req/6 except that the first argument is the PID --%% returned by spawn_worker_process/2 or spawn_link_worker_process/2 --send_req_direct(Conn_pid, Url, Headers, Method, Body, Options, Timeout) -> -- case catch parse_url(Url) of -- #url{host = Host, -- port = Port} = Parsed_url -> -- Options_1 = merge_options(Host, Port, Options), -- case do_send_req(Conn_pid, Parsed_url, Headers, Method, Body, Options_1, Timeout) of -- {error, {'EXIT', {noproc, _}}} -> -- {error, worker_is_dead}; -- Ret -> -- Ret -- end; -- Err -> -- {error, {url_parsing_failed, Err}} -- end. -- --%% @doc Tell ibrowse to stream the next chunk of data to the --%% caller. Should be used in conjunction with the --%% stream_to option --%% @spec stream_next(Req_id :: req_id()) -> ok | {error, unknown_req_id} --stream_next(Req_id) -> -- case ets:lookup(ibrowse_stream, {req_id_pid, Req_id}) of -- [] -> -- {error, unknown_req_id}; -- [{_, Pid}] -> -- catch Pid ! {stream_next, Req_id}, -- ok -- end. -- --%% @doc Turn tracing on for the ibrowse process --trace_on() -> -- ibrowse ! {trace, true}. --%% @doc Turn tracing off for the ibrowse process --trace_off() -> -- ibrowse ! {trace, false}. -- --%% @doc Turn tracing on for all connections to the specified HTTP --%% server. Host is whatever is specified as the domain name in the URL --%% @spec trace_on(Host, Port) -> ok --%% Host = string() --%% Port = integer() --trace_on(Host, Port) -> -- ibrowse ! {trace, true, Host, Port}, -- ok. -- --%% @doc Turn tracing OFF for all connections to the specified HTTP --%% server. --%% @spec trace_off(Host, Port) -> ok --trace_off(Host, Port) -> -- ibrowse ! {trace, false, Host, Port}, -- ok. -- --%% @doc Turn Off ALL tracing --%% @spec all_trace_off() -> ok --all_trace_off() -> -- ibrowse ! all_trace_off, -- ok. -- --show_dest_status() -> -- Dests = lists:filter(fun({lb_pid, {Host, Port}, _}) when is_list(Host), -- is_integer(Port) -> -- true; -- (_) -> -- false -- end, ets:tab2list(ibrowse_lb)), -- All_ets = ets:all(), -- io:format("~-40.40s | ~-5.5s | ~-10.10s | ~s~n", -- ["Server:port", "ETS", "Num conns", "LB Pid"]), -- io:format("~80.80.=s~n", [""]), -- lists:foreach(fun({lb_pid, {Host, Port}, Lb_pid}) -> -- case lists:dropwhile( -- fun(Tid) -> -- ets:info(Tid, owner) /= Lb_pid -- end, All_ets) of -- [] -> -- io:format("~40.40s | ~-5.5s | ~-5.5s | ~s~n", -- [Host ++ ":" ++ integer_to_list(Port), -- "", -- "", -- io_lib:format("~p", [Lb_pid])] -- ); -- [Tid | _] -> -- catch ( -- begin -- Size = ets:info(Tid, size), -- io:format("~40.40s | ~-5.5s | ~-5.5s | ~s~n", -- [Host ++ ":" ++ integer_to_list(Port), -- integer_to_list(Tid), -- integer_to_list(Size), -- io_lib:format("~p", [Lb_pid])] -- ) -- end -- ) -- end -- end, Dests). -- --%% @doc Shows some internal information about load balancing to a --%% specified Host:Port. Info about workers spawned using --%% spawn_worker_process/2 or spawn_link_worker_process/2 is not --%% included. --show_dest_status(Host, Port) -> -- case ets:lookup(ibrowse_lb, {Host, Port}) of -- [] -> -- no_active_processes; -- [#lb_pid{pid = Lb_pid}] -> -- io:format("Load Balancer Pid : ~p~n", [Lb_pid]), -- io:format("LB process msg q size : ~p~n", [(catch process_info(Lb_pid, message_queue_len))]), -- case lists:dropwhile( -- fun(Tid) -> -- ets:info(Tid, owner) /= Lb_pid -- end, ets:all()) of -- [] -> -- io:format("Couldn't locate ETS table for ~p~n", [Lb_pid]); -- [Tid | _] -> -- First = ets:first(Tid), -- Last = ets:last(Tid), -- Size = ets:info(Tid, size), -- io:format("LB ETS table id : ~p~n", [Tid]), -- io:format("Num Connections : ~p~n", [Size]), -- case Size of -- 0 -> -- ok; -- _ -> -- {First_p_sz, _} = First, -- {Last_p_sz, _} = Last, -- io:format("Smallest pipeline : ~1000.p~n", [First_p_sz]), -- io:format("Largest pipeline : ~1000.p~n", [Last_p_sz]) -- end -- end -- end. -- --%% @doc Clear current configuration for ibrowse and load from the file --%% ibrowse.conf in the IBROWSE_EBIN/../priv directory. Current --%% configuration is cleared only if the ibrowse.conf file is readable --%% using file:consult/1 --rescan_config() -> -- gen_server:call(?MODULE, rescan_config). -- --%% Clear current configuration for ibrowse and load from the specified --%% file. Current configuration is cleared only if the specified --%% file is readable using file:consult/1 --rescan_config(File) when is_list(File) -> -- gen_server:call(?MODULE, {rescan_config, File}). -- --%%==================================================================== --%% Server functions --%%==================================================================== -- --%%-------------------------------------------------------------------- --%% Function: init/1 --%% Description: Initiates the server --%% Returns: {ok, State} | --%% {ok, State, Timeout} | --%% ignore | --%% {stop, Reason} --%%-------------------------------------------------------------------- --init(_) -> -- process_flag(trap_exit, true), -- State = #state{}, -- put(my_trace_flag, State#state.trace), -- put(ibrowse_trace_token, "ibrowse"), -- ets:new(ibrowse_lb, [named_table, public, {keypos, 2}]), -- ets:new(ibrowse_conf, [named_table, protected, {keypos, 2}]), -- ets:new(ibrowse_stream, [named_table, public]), -- import_config(), -- {ok, #state{}}. -- --import_config() -> -- case code:priv_dir(ibrowse) of -- {error, _} = Err -> -- Err; -- PrivDir -> -- Filename = filename:join(PrivDir, "ibrowse.conf"), -- import_config(Filename) -- end. -- --import_config(Filename) -> -- case file:consult(Filename) of -- {ok, Terms} -> -- ets:delete_all_objects(ibrowse_conf), -- Fun = fun({dest, Host, Port, MaxSess, MaxPipe, Options}) -- when is_list(Host), is_integer(Port), -- is_integer(MaxSess), MaxSess > 0, -- is_integer(MaxPipe), MaxPipe > 0, is_list(Options) -> -- I = [{{max_sessions, Host, Port}, MaxSess}, -- {{max_pipeline_size, Host, Port}, MaxPipe}, -- {{options, Host, Port}, Options}], -- lists:foreach( -- fun({X, Y}) -> -- ets:insert(ibrowse_conf, -- #ibrowse_conf{key = X, -- value = Y}) -- end, I); -- ({K, V}) -> -- ets:insert(ibrowse_conf, -- #ibrowse_conf{key = K, -- value = V}); -- (X) -> -- io:format("Skipping unrecognised term: ~p~n", [X]) -- end, -- lists:foreach(Fun, Terms); -- Err -> -- Err -- end. -- --%% @doc Internal export --get_config_value(Key) -> -- [#ibrowse_conf{value = V}] = ets:lookup(ibrowse_conf, Key), -- V. -- --%% @doc Internal export --get_config_value(Key, DefVal) -> -- case ets:lookup(ibrowse_conf, Key) of -- [] -> -- DefVal; -- [#ibrowse_conf{value = V}] -> -- V -- end. -- --set_config_value(Key, Val) -> -- ets:insert(ibrowse_conf, #ibrowse_conf{key = Key, value = Val}). --%%-------------------------------------------------------------------- --%% Function: handle_call/3 --%% Description: Handling call messages --%% Returns: {reply, Reply, State} | --%% {reply, Reply, State, Timeout} | --%% {noreply, State} | --%% {noreply, State, Timeout} | --%% {stop, Reason, Reply, State} | (terminate/2 is called) --%% {stop, Reason, State} (terminate/2 is called) --%%-------------------------------------------------------------------- --handle_call({get_lb_pid, #url{host = Host, port = Port} = Url}, _From, State) -> -- Pid = do_get_connection(Url, ets:lookup(ibrowse_lb, {Host, Port})), -- {reply, Pid, State}; -- --handle_call(stop, _From, State) -> -- do_trace("IBROWSE shutting down~n", []), -- {stop, normal, ok, State}; -- --handle_call({set_config_value, Key, Val}, _From, State) -> -- set_config_value(Key, Val), -- {reply, ok, State}; -- --handle_call(rescan_config, _From, State) -> -- Ret = (catch import_config()), -- {reply, Ret, State}; -- --handle_call({rescan_config, File}, _From, State) -> -- Ret = (catch import_config(File)), -- {reply, Ret, State}; -- --handle_call(Request, _From, State) -> -- Reply = {unknown_request, Request}, -- {reply, Reply, State}. -- --%%-------------------------------------------------------------------- --%% Function: handle_cast/2 --%% Description: Handling cast messages --%% Returns: {noreply, State} | --%% {noreply, State, Timeout} | --%% {stop, Reason, State} (terminate/2 is called) --%%-------------------------------------------------------------------- -- --handle_cast(_Msg, State) -> -- {noreply, State}. -- --%%-------------------------------------------------------------------- --%% Function: handle_info/2 --%% Description: Handling all non call/cast messages --%% Returns: {noreply, State} | --%% {noreply, State, Timeout} | --%% {stop, Reason, State} (terminate/2 is called) --%%-------------------------------------------------------------------- --handle_info(all_trace_off, State) -> -- Mspec = [{{ibrowse_conf,{trace,'$1','$2'},true},[],[{{'$1','$2'}}]}], -- Trace_on_dests = ets:select(ibrowse_conf, Mspec), -- Fun = fun(#lb_pid{host_port = {H, P}, pid = Pid}, _) -> -- case lists:member({H, P}, Trace_on_dests) of -- false -> -- ok; -- true -> -- catch Pid ! {trace, false} -- end; -- (_, Acc) -> -- Acc -- end, -- ets:foldl(Fun, undefined, ibrowse_lb), -- ets:select_delete(ibrowse_conf, [{{ibrowse_conf,{trace,'$1','$2'},true},[],['true']}]), -- {noreply, State}; -- --handle_info({trace, Bool}, State) -> -- put(my_trace_flag, Bool), -- {noreply, State}; -- --handle_info({trace, Bool, Host, Port}, State) -> -- Fun = fun(#lb_pid{host_port = {H, P}, pid = Pid}, _) -- when H == Host, -- P == Port -> -- catch Pid ! {trace, Bool}; -- (_, Acc) -> -- Acc -- end, -- ets:foldl(Fun, undefined, ibrowse_lb), -- ets:insert(ibrowse_conf, #ibrowse_conf{key = {trace, Host, Port}, -- value = Bool}), -- {noreply, State}; -- --handle_info(_Info, State) -> -- {noreply, State}. -- --%%-------------------------------------------------------------------- --%% Function: terminate/2 --%% Description: Shutdown the server --%% Returns: any (ignored by gen_server) --%%-------------------------------------------------------------------- --terminate(_Reason, _State) -> -- ok. -- --%%-------------------------------------------------------------------- --%% Func: code_change/3 --%% Purpose: Convert process state when code is changed --%% Returns: {ok, NewState} --%%-------------------------------------------------------------------- --code_change(_OldVsn, State, _Extra) -> -- {ok, State}. -- --%%-------------------------------------------------------------------- --%%% Internal functions --%%-------------------------------------------------------------------- --do_get_connection(#url{host = Host, port = Port}, []) -> -- {ok, Pid} = ibrowse_lb:start_link([Host, Port]), -- ets:insert(ibrowse_lb, #lb_pid{host_port = {Host, Port}, pid = Pid}), -- Pid; --do_get_connection(_Url, [#lb_pid{pid = Pid}]) -> -- Pid. -diff --git a/src/ibrowse/ibrowse.hrl b/src/ibrowse/ibrowse.hrl -deleted file mode 100644 -index ebf3bb3..0000000 ---- a/src/ibrowse/ibrowse.hrl -+++ /dev/null -@@ -1,12 +0,0 @@ ---ifndef(IBROWSE_HRL). ---define(IBROWSE_HRL, "ibrowse.hrl"). -- ---record(url, {abspath, host, port, username, password, path, protocol}). -- ---record(lb_pid, {host_port, pid}). -- ---record(client_conn, {key, cur_pipeline_size = 0, reqs_served = 0}). -- ---record(ibrowse_conf, {key, value}). -- ---endif. -diff --git a/src/ibrowse/ibrowse_app.erl b/src/ibrowse/ibrowse_app.erl -deleted file mode 100644 -index 8c83e8f..0000000 ---- a/src/ibrowse/ibrowse_app.erl -+++ /dev/null -@@ -1,64 +0,0 @@ --%%%------------------------------------------------------------------- --%%% File : ibrowse_app.erl --%%% Author : Chandrashekhar Mullaparthi --%%% Description : --%%% --%%% Created : 15 Oct 2003 by Chandrashekhar Mullaparthi --%%%------------------------------------------------------------------- ---module(ibrowse_app). ---vsn('$Id: ibrowse_app.erl,v 1.1 2005/05/05 22:28:28 chandrusf Exp $ '). -- ---behaviour(application). --%%-------------------------------------------------------------------- --%% Include files --%%-------------------------------------------------------------------- -- --%%-------------------------------------------------------------------- --%% External exports --%%-------------------------------------------------------------------- ---export([ -- start/2, -- stop/1 -- ]). -- --%%-------------------------------------------------------------------- --%% Internal exports --%%-------------------------------------------------------------------- ---export([ -- ]). -- --%%-------------------------------------------------------------------- --%% Macros --%%-------------------------------------------------------------------- -- --%%-------------------------------------------------------------------- --%% Records --%%-------------------------------------------------------------------- -- --%%==================================================================== --%% External functions --%%==================================================================== --%%-------------------------------------------------------------------- --%% Func: start/2 --%% Returns: {ok, Pid} | --%% {ok, Pid, State} | --%% {error, Reason} --%%-------------------------------------------------------------------- --start(_Type, _StartArgs) -> -- case ibrowse_sup:start_link() of -- {ok, Pid} -> -- {ok, Pid}; -- Error -> -- Error -- end. -- --%%-------------------------------------------------------------------- --%% Func: stop/1 --%% Returns: any --%%-------------------------------------------------------------------- --stop(_State) -> -- ok. -- --%%==================================================================== --%% Internal functions --%%==================================================================== -diff --git a/src/ibrowse/ibrowse_http_client.erl b/src/ibrowse/ibrowse_http_client.erl -deleted file mode 100644 -index a767b84..0000000 ---- a/src/ibrowse/ibrowse_http_client.erl -+++ /dev/null -@@ -1,1476 +0,0 @@ --%%%------------------------------------------------------------------- --%%% File : ibrowse_http_client.erl --%%% Author : Chandrashekhar Mullaparthi --%%% Description : The name says it all --%%% --%%% Created : 11 Oct 2003 by Chandrashekhar Mullaparthi --%%%------------------------------------------------------------------- ---module(ibrowse_http_client). ---vsn('$Id: ibrowse_http_client.erl,v 1.19 2009/07/01 22:43:19 chandrusf Exp $ '). -- ---behaviour(gen_server). --%%-------------------------------------------------------------------- --%% Include files --%%-------------------------------------------------------------------- -- --%%-------------------------------------------------------------------- --%% External exports ---export([ -- start_link/1, -- start/1, -- stop/1, -- send_req/7 -- ]). -- ---ifdef(debug). ---compile(export_all). ---endif. -- --%% gen_server callbacks ---export([ -- init/1, -- handle_call/3, -- handle_cast/2, -- handle_info/2, -- terminate/2, -- code_change/3 -- ]). -- ---include("ibrowse.hrl"). -- ---record(state, {host, port, -- use_proxy = false, proxy_auth_digest, -- ssl_options = [], is_ssl = false, socket, -- reqs=queue:new(), cur_req, status=idle, http_status_code, -- reply_buffer = <<>>, rep_buf_size=0, streamed_size = 0, -- recvd_headers=[], -- is_closing, send_timer, content_length, -- deleted_crlf = false, transfer_encoding, -- chunk_size, chunk_size_buffer = <<>>, recvd_chunk_size, -- lb_ets_tid, cur_pipeline_size = 0, prev_req_id -- }). -- ---record(request, {url, method, options, from, -- stream_to, caller_controls_socket = false, -- req_id, -- stream_chunk_size, -- save_response_to_file = false, -- tmp_file_name, tmp_file_fd, -- response_format}). -- ---import(ibrowse_lib, [ -- get_value/2, -- get_value/3, -- do_trace/2 -- ]). -- ---define(DEFAULT_STREAM_CHUNK_SIZE, 1024*1024). -- --%%==================================================================== --%% External functions --%%==================================================================== --%%-------------------------------------------------------------------- --%% Function: start_link/0 --%% Description: Starts the server --%%-------------------------------------------------------------------- --start(Args) -> -- gen_server:start(?MODULE, Args, []). -- --start_link(Args) -> -- gen_server:start_link(?MODULE, Args, []). -- --stop(Conn_pid) -> -- gen_server:call(Conn_pid, stop). -- --send_req(Conn_Pid, Url, Headers, Method, Body, Options, Timeout) -> -- gen_server:call( -- Conn_Pid, -- {send_req, {Url, Headers, Method, Body, Options, Timeout}}, Timeout). -- --%%==================================================================== --%% Server functions --%%==================================================================== -- --%%-------------------------------------------------------------------- --%% Function: init/1 --%% Description: Initiates the server --%% Returns: {ok, State} | --%% {ok, State, Timeout} | --%% ignore | --%% {stop, Reason} --%%-------------------------------------------------------------------- --init({Lb_Tid, #url{host = Host, port = Port}, {SSLOptions, Is_ssl}}) -> -- State = #state{host = Host, -- port = Port, -- ssl_options = SSLOptions, -- is_ssl = Is_ssl, -- lb_ets_tid = Lb_Tid}, -- put(ibrowse_trace_token, [Host, $:, integer_to_list(Port)]), -- put(my_trace_flag, ibrowse_lib:get_trace_status(Host, Port)), -- {ok, State}; --init({Host, Port}) -> -- State = #state{host = Host, -- port = Port}, -- put(ibrowse_trace_token, [Host, $:, integer_to_list(Port)]), -- put(my_trace_flag, ibrowse_lib:get_trace_status(Host, Port)), -- {ok, State}; --init(#url{host=Host, port=Port, protocol=Protocol}) -> -- State = #state{ -- host = Host, -- port = Port, -- is_ssl = (Protocol == https), -- ssl_options = [{ssl_imp, new}] -- }, -- put(ibrowse_trace_token, [Host, $:, integer_to_list(Port)]), -- put(my_trace_flag, ibrowse_lib:get_trace_status(Host, Port)), -- {ok, State}. -- --%%-------------------------------------------------------------------- --%% Function: handle_call/3 --%% Description: Handling call messages --%% Returns: {reply, Reply, State} | --%% {reply, Reply, State, Timeout} | --%% {noreply, State} | --%% {noreply, State, Timeout} | --%% {stop, Reason, Reply, State} | (terminate/2 is called) --%% {stop, Reason, State} (terminate/2 is called) --%%-------------------------------------------------------------------- --%% Received a request when the remote server has already sent us a --%% Connection: Close header --handle_call({send_req, _}, _From, #state{is_closing = true} = State) -> -- {reply, {error, connection_closing}, State}; -- --handle_call({send_req, {Url, Headers, Method, Body, Options, Timeout}}, -- From, State) -> -- send_req_1(From, Url, Headers, Method, Body, Options, Timeout, State); -- --handle_call(stop, _From, State) -> -- do_close(State), -- do_error_reply(State, closing_on_request), -- {stop, normal, ok, State#state{socket=undefined}}; -- --handle_call(Request, _From, State) -> -- Reply = {unknown_request, Request}, -- {reply, Reply, State}. -- --%%-------------------------------------------------------------------- --%% Function: handle_cast/2 --%% Description: Handling cast messages --%% Returns: {noreply, State} | --%% {noreply, State, Timeout} | --%% {stop, Reason, State} (terminate/2 is called) --%%-------------------------------------------------------------------- --handle_cast(_Msg, State) -> -- {noreply, State}. -- --%%-------------------------------------------------------------------- --%% Function: handle_info/2 --%% Description: Handling all non call/cast messages --%% Returns: {noreply, State} | --%% {noreply, State, Timeout} | --%% {stop, Reason, State} (terminate/2 is called) --%%-------------------------------------------------------------------- --handle_info({tcp, _Sock, Data}, #state{status = Status} = State) -> -- do_trace("Data recvd in state: ~p. Size: ~p. ~p~n~n", [Status, size(Data), Data]), -- handle_sock_data(Data, State); --handle_info({ssl, _Sock, Data}, State) -> -- handle_sock_data(Data, State); -- --handle_info({stream_next, Req_id}, #state{socket = Socket, -- is_ssl = Is_ssl, -- cur_req = #request{req_id = Req_id}} = State) -> -- do_setopts(Socket, [{active, once}], Is_ssl), -- {noreply, State}; -- --handle_info({stream_next, _Req_id}, State) -> -- {noreply, State}; -- --handle_info({tcp_closed, _Sock}, State) -> -- do_trace("TCP connection closed by peer!~n", []), -- handle_sock_closed(State), -- {stop, normal, State}; --handle_info({ssl_closed, _Sock}, State) -> -- do_trace("SSL connection closed by peer!~n", []), -- handle_sock_closed(State), -- {stop, normal, State}; -- --handle_info({tcp_error, _Sock}, State) -> -- io:format("Error on connection to ~1000.p:~1000.p~n", [State#state.host, State#state.port]), -- handle_sock_closed(State), -- {stop, normal, State}; --handle_info({ssl_error, _Sock}, State) -> -- io:format("Error on SSL connection to ~1000.p:~1000.p~n", [State#state.host, State#state.port]), -- handle_sock_closed(State), -- {stop, normal, State}; -- --handle_info({req_timedout, From}, State) -> -- case lists:keysearch(From, #request.from, queue:to_list(State#state.reqs)) of -- false -> -- {noreply, State}; -- {value, _} -> -- shutting_down(State), -- do_error_reply(State, req_timedout), -- {stop, normal, State} -- end; -- --handle_info(timeout, State) -> -- shutting_down(State), -- do_error_reply(State, req_timedout), -- {stop, normal, State}; -- --handle_info({trace, Bool}, State) -> -- put(my_trace_flag, Bool), -- {noreply, State}; -- --handle_info(Info, State) -> -- io:format("Unknown message recvd for ~1000.p:~1000.p -> ~p~n", -- [State#state.host, State#state.port, Info]), -- io:format("Recvd unknown message ~p when in state: ~p~n", [Info, State]), -- {noreply, State}. -- --%%-------------------------------------------------------------------- --%% Function: terminate/2 --%% Description: Shutdown the server --%% Returns: any (ignored by gen_server) --%%-------------------------------------------------------------------- --terminate(_Reason, State) -> -- do_close(State). -- --%%-------------------------------------------------------------------- --%% Func: code_change/3 --%% Purpose: Convert process state when code is changed --%% Returns: {ok, NewState} --%%-------------------------------------------------------------------- --code_change(_OldVsn, State, _Extra) -> -- {ok, State}. -- --%%-------------------------------------------------------------------- --%%% Internal functions --%%-------------------------------------------------------------------- -- --%%-------------------------------------------------------------------- --%% Handles data recvd on the socket --%%-------------------------------------------------------------------- --handle_sock_data(Data, #state{status=idle}=State) -> -- do_trace("Data recvd on socket in state idle!. ~1000.p~n", [Data]), -- shutting_down(State), -- do_error_reply(State, data_in_status_idle), -- do_close(State), -- {stop, normal, State}; -- --handle_sock_data(Data, #state{status = get_header}=State) -> -- case parse_response(Data, State) of -- {error, _Reason} -> -- shutting_down(State), -- {stop, normal, State}; -- stop -> -- shutting_down(State), -- {stop, normal, State}; -- State_1 -> -- active_once(State_1), -- {noreply, State_1, get_inac_timeout(State_1)} -- end; -- --handle_sock_data(Data, #state{status = get_body, -- content_length = CL, -- http_status_code = StatCode, -- recvd_headers = Headers, -- chunk_size = CSz} = State) -> -- case (CL == undefined) and (CSz == undefined) of -- true -> -- case accumulate_response(Data, State) of -- {error, Reason} -> -- shutting_down(State), -- fail_pipelined_requests(State, -- {error, {Reason, {stat_code, StatCode}, Headers}}), -- {stop, normal, State}; -- State_1 -> -- active_once(State_1), -- {noreply, State_1, get_inac_timeout(State_1)} -- end; -- _ -> -- case parse_11_response(Data, State) of -- {error, Reason} -> -- shutting_down(State), -- fail_pipelined_requests(State, -- {error, {Reason, {stat_code, StatCode}, Headers}}), -- {stop, normal, State}; -- stop -> -- shutting_down(State), -- {stop, normal, State}; -- State_1 -> -- active_once(State_1), -- {noreply, State_1, get_inac_timeout(State_1)} -- end -- end. -- --accumulate_response(Data, -- #state{ -- cur_req = #request{save_response_to_file = true, -- tmp_file_fd = undefined} = CurReq, -- http_status_code=[$2 | _]}=State) -> -- TmpFilename = make_tmp_filename(), -- case file:open(TmpFilename, [write, delayed_write, raw]) of -- {ok, Fd} -> -- accumulate_response(Data, State#state{ -- cur_req = CurReq#request{ -- tmp_file_fd = Fd, -- tmp_file_name = TmpFilename}}); -- {error, Reason} -> -- {error, {file_open_error, Reason}} -- end; --accumulate_response(Data, #state{cur_req = #request{save_response_to_file = true, -- tmp_file_fd = Fd}, -- transfer_encoding=chunked, -- reply_buffer = Reply_buf, -- http_status_code=[$2 | _] -- } = State) -> -- case file:write(Fd, [Reply_buf, Data]) of -- ok -> -- State#state{reply_buffer = <<>>}; -- {error, Reason} -> -- {error, {file_write_error, Reason}} -- end; --accumulate_response(Data, #state{cur_req = #request{save_response_to_file = true, -- tmp_file_fd = Fd}, -- reply_buffer = RepBuf, -- http_status_code=[$2 | _] -- } = State) -> -- case file:write(Fd, [RepBuf, Data]) of -- ok -> -- State#state{reply_buffer = <<>>}; -- {error, Reason} -> -- {error, {file_write_error, Reason}} -- end; --accumulate_response(<<>>, State) -> -- State; --accumulate_response(Data, #state{reply_buffer = RepBuf, -- rep_buf_size = RepBufSize, -- streamed_size = Streamed_size, -- cur_req = CurReq}=State) -> -- #request{stream_to=StreamTo, req_id=ReqId, -- stream_chunk_size = Stream_chunk_size, -- response_format = Response_format, -- caller_controls_socket = Caller_controls_socket} = CurReq, -- RepBuf_1 = list_to_binary([RepBuf, Data]), -- New_data_size = RepBufSize - Streamed_size, -- case StreamTo of -- undefined -> -- State#state{reply_buffer = RepBuf_1}; -- _ when Caller_controls_socket == true -> -- do_interim_reply(StreamTo, Response_format, ReqId, RepBuf_1), -- State#state{reply_buffer = <<>>, -- streamed_size = Streamed_size + size(RepBuf_1)}; -- _ when New_data_size >= Stream_chunk_size -> -- {Stream_chunk, Rem_data} = split_binary(RepBuf_1, Stream_chunk_size), -- do_interim_reply(StreamTo, Response_format, ReqId, Stream_chunk), -- accumulate_response( -- Rem_data, -- State#state{ -- reply_buffer = <<>>, -- streamed_size = Streamed_size + Stream_chunk_size}); -- _ -> -- State#state{reply_buffer = RepBuf_1} -- end. -- --make_tmp_filename() -> -- DownloadDir = ibrowse:get_config_value(download_dir, filename:absname("./")), -- {A,B,C} = now(), -- filename:join([DownloadDir, -- "ibrowse_tmp_file_"++ -- integer_to_list(A) ++ -- integer_to_list(B) ++ -- integer_to_list(C)]). -- -- --%%-------------------------------------------------------------------- --%% Handles the case when the server closes the socket --%%-------------------------------------------------------------------- --handle_sock_closed(#state{status=get_header}=State) -> -- shutting_down(State), -- do_error_reply(State, connection_closed); -- --handle_sock_closed(#state{cur_req=undefined} = State) -> -- shutting_down(State); -- --%% We check for IsClosing because this the server could have sent a --%% Connection-Close header and has closed the socket to indicate end --%% of response. There maybe requests pipelined which need a response. --handle_sock_closed(#state{reply_buffer = Buf, reqs = Reqs, http_status_code = SC, -- is_closing = IsClosing, -- cur_req = #request{tmp_file_name=TmpFilename, -- tmp_file_fd=Fd} = CurReq, -- status = get_body, recvd_headers = Headers}=State) -> -- #request{from=From, stream_to=StreamTo, req_id=ReqId, -- response_format = Resp_format} = CurReq, -- case IsClosing of -- true -> -- {_, Reqs_1} = queue:out(Reqs), -- case TmpFilename of -- undefined -> -- do_reply(State, From, StreamTo, ReqId, Resp_format, -- {ok, SC, Headers, Buf}); -- _ -> -- file:close(Fd), -- do_reply(State, From, StreamTo, ReqId, Resp_format, -- {ok, SC, Headers, {file, TmpFilename}}) -- end, -- do_error_reply(State#state{reqs = Reqs_1}, connection_closed), -- State; -- _ -> -- do_error_reply(State, connection_closed), -- State -- end. -- --do_connect(Host, Port, _Options, #state{is_ssl=true, ssl_options=SSLOptions}, Timeout) -> -- ssl:connect(Host, Port, -- [binary, {nodelay, true}, {active, false} | SSLOptions], -- Timeout); --do_connect(Host, Port, _Options, _State, Timeout) -> -- gen_tcp:connect(Host, Port, -- [binary, {nodelay, true}, {active, false}], -- Timeout). -- --do_send(Req, #state{socket = Sock, is_ssl = true}) -> ssl:send(Sock, Req); --do_send(Req, #state{socket = Sock, is_ssl = false}) -> gen_tcp:send(Sock, Req). -- --%% @spec do_send_body(Sock::socket_descriptor(), Source::source_descriptor(), IsSSL::boolean()) -> ok | error() --%% source_descriptor() = fun_arity_0 | --%% {fun_arity_0} | --%% {fun_arity_1, term()} --%% error() = term() --do_send_body(Source, State) when is_function(Source) -> -- do_send_body({Source}, State); --do_send_body({Source}, State) when is_function(Source) -> -- do_send_body1(Source, Source(), State); --do_send_body({Source, Source_state}, State) when is_function(Source) -> -- do_send_body1(Source, Source(Source_state), State); --do_send_body(Body, State) -> -- do_send(Body, State). -- --do_send_body1(Source, Resp, State) -> -- case Resp of -- {ok, Data} -> -- do_send(Data, State), -- do_send_body({Source}, State); -- {ok, Data, New_source_state} -> -- do_send(Data, State), -- do_send_body({Source, New_source_state}, State); -- eof -> -- ok; -- Err -> -- Err -- end. -- --do_close(#state{socket = undefined}) -> ok; --do_close(#state{socket = Sock, is_ssl = true}) -> ssl:close(Sock); --do_close(#state{socket = Sock, is_ssl = false}) -> gen_tcp:close(Sock). -- --active_once(#state{cur_req = #request{caller_controls_socket = true}}) -> -- ok; --active_once(#state{socket = Socket, is_ssl = Is_ssl}) -> -- do_setopts(Socket, [{active, once}], Is_ssl). -- --do_setopts(Sock, Opts, true) -> ssl:setopts(Sock, Opts); --do_setopts(Sock, Opts, false) -> inet:setopts(Sock, Opts). -- --check_ssl_options(Options, State) -> -- case get_value(is_ssl, Options, false) of -- false -> -- State; -- true -> -- State#state{is_ssl=true, ssl_options=get_value(ssl_options, Options)} -- end. -- --send_req_1(From, -- #url{host = Host, -- port = Port} = Url, -- Headers, Method, Body, Options, Timeout, -- #state{socket = undefined} = State) -> -- {Host_1, Port_1, State_1} = -- case get_value(proxy_host, Options, false) of -- false -> -- {Host, Port, State}; -- PHost -> -- ProxyUser = get_value(proxy_user, Options, []), -- ProxyPassword = get_value(proxy_password, Options, []), -- Digest = http_auth_digest(ProxyUser, ProxyPassword), -- {PHost, get_value(proxy_port, Options, 80), -- State#state{use_proxy = true, -- proxy_auth_digest = Digest}} -- end, -- State_2 = check_ssl_options(Options, State_1), -- do_trace("Connecting...~n", []), -- Start_ts = now(), -- Conn_timeout = get_value(connect_timeout, Options, Timeout), -- case do_connect(Host_1, Port_1, Options, State_2, Conn_timeout) of -- {ok, Sock} -> -- do_trace("Connected!~n", []), -- End_ts = now(), -- Timeout_1 = case Timeout of -- infinity -> -- infinity; -- _ -> -- Timeout - trunc(round(timer:now_diff(End_ts, Start_ts) / 1000)) -- end, -- State_3 = State_2#state{socket = Sock}, -- send_req_1(From, Url, Headers, Method, Body, Options, Timeout_1, State_3); -- Err -> -- shutting_down(State_2), -- do_trace("Error connecting. Reason: ~1000.p~n", [Err]), -- gen_server:reply(From, {error, conn_failed}), -- {stop, normal, State_2} -- end; --send_req_1(From, -- #url{abspath = AbsPath, -- host = Host, -- port = Port, -- path = RelPath} = Url, -- Headers, Method, Body, Options, Timeout, -- #state{status = Status} = State) -> -- ReqId = make_req_id(), -- Resp_format = get_value(response_format, Options, list), -- {StreamTo, Caller_controls_socket} = -- case get_value(stream_to, Options, undefined) of -- {Caller, once} when is_pid(Caller) or -- is_atom(Caller) -> -- Async_pid_rec = {{req_id_pid, ReqId}, self()}, -- true = ets:insert(ibrowse_stream, Async_pid_rec), -- {Caller, true}; -- undefined -> -- {undefined, false}; -- Caller when is_pid(Caller) or -- is_atom(Caller) -> -- {Caller, false}; -- Stream_to_inv -> -- exit({invalid_option, {stream_to, Stream_to_inv}}) -- end, -- SaveResponseToFile = get_value(save_response_to_file, Options, false), -- NewReq = #request{url = Url, -- method = Method, -- stream_to = StreamTo, -- caller_controls_socket = Caller_controls_socket, -- options = Options, -- req_id = ReqId, -- save_response_to_file = SaveResponseToFile, -- stream_chunk_size = get_stream_chunk_size(Options), -- response_format = Resp_format, -- from = From}, -- State_1 = State#state{reqs=queue:in(NewReq, State#state.reqs)}, -- Headers_1 = add_auth_headers(Url, Options, Headers, State), -- HostHeaderValue = case lists:keysearch(host_header, 1, Options) of -- false -> -- case Port of -- 80 -> Host; -- _ -> [Host, ":", integer_to_list(Port)] -- end; -- {value, {_, Host_h_val}} -> -- Host_h_val -- end, -- {Req, Body_1} = make_request(Method, -- [{"Host", HostHeaderValue} | Headers_1], -- AbsPath, RelPath, Body, Options, State#state.use_proxy), -- case get(my_trace_flag) of -- true -> -- %%Avoid the binary operations if trace is not on... -- NReq = binary_to_list(list_to_binary(Req)), -- do_trace("Sending request: ~n" -- "--- Request Begin ---~n~s~n" -- "--- Request End ---~n", [NReq]); -- _ -> ok -- end, -- case do_send(Req, State) of -- ok -> -- case do_send_body(Body_1, State) of -- ok -> -- State_2 = inc_pipeline_counter(State_1), -- active_once(State_1), -- Ref = case Timeout of -- infinity -> -- undefined; -- _ -> -- erlang:send_after(Timeout, self(), {req_timedout, From}) -- end, -- State_3 = case Status of -- idle -> -- State_2#state{status = get_header, -- cur_req = NewReq, -- send_timer = Ref}; -- _ -> -- State_2#state{send_timer = Ref} -- end, -- case StreamTo of -- undefined -> -- ok; -- _ -> -- gen_server:reply(From, {ibrowse_req_id, ReqId}) -- end, -- {noreply, State_3, get_inac_timeout(State_3)}; -- Err -> -- shutting_down(State_1), -- do_trace("Send failed... Reason: ~p~n", [Err]), -- gen_server:reply(From, {error, send_failed}), -- {stop, normal, State_1} -- end; -- Err -> -- shutting_down(State_1), -- do_trace("Send failed... Reason: ~p~n", [Err]), -- gen_server:reply(From, {error, send_failed}), -- {stop, normal, State_1} -- end. -- --add_auth_headers(#url{username = User, -- password = UPw}, -- Options, -- Headers, -- #state{use_proxy = UseProxy, -- proxy_auth_digest = ProxyAuthDigest}) -> -- Headers_1 = case User of -- undefined -> -- case get_value(basic_auth, Options, undefined) of -- undefined -> -- Headers; -- {U,P} -> -- [{"Authorization", ["Basic ", http_auth_digest(U, P)]} | Headers] -- end; -- _ -> -- [{"Authorization", ["Basic ", http_auth_digest(User, UPw)]} | Headers] -- end, -- case UseProxy of -- false -> -- Headers_1; -- true when ProxyAuthDigest == [] -> -- Headers_1; -- true -> -- [{"Proxy-Authorization", ["Basic ", ProxyAuthDigest]} | Headers_1] -- end. -- --http_auth_digest([], []) -> -- []; --http_auth_digest(Username, Password) -> -- encode_base64(Username ++ [$: | Password]). -- --encode_base64([]) -> -- []; --encode_base64([A]) -> -- [e(A bsr 2), e((A band 3) bsl 4), $=, $=]; --encode_base64([A,B]) -> -- [e(A bsr 2), e(((A band 3) bsl 4) bor (B bsr 4)), e((B band 15) bsl 2), $=]; --encode_base64([A,B,C|Ls]) -> -- encode_base64_do(A,B,C, Ls). --encode_base64_do(A,B,C, Rest) -> -- BB = (A bsl 16) bor (B bsl 8) bor C, -- [e(BB bsr 18), e((BB bsr 12) band 63), -- e((BB bsr 6) band 63), e(BB band 63)|encode_base64(Rest)]. -- --e(X) when X >= 0, X < 26 -> X+65; --e(X) when X>25, X<52 -> X+71; --e(X) when X>51, X<62 -> X-4; --e(62) -> $+; --e(63) -> $/; --e(X) -> exit({bad_encode_base64_token, X}). -- --make_request(Method, Headers, AbsPath, RelPath, Body, Options, UseProxy) -> -- HttpVsn = http_vsn_string(get_value(http_vsn, Options, {1,1})), -- Headers_1 = -- case get_value(content_length, Headers, false) of -- false when (Body == []) or -- (Body == <<>>) or -- is_tuple(Body) or -- is_function(Body) -> -- Headers; -- false when is_binary(Body) -> -- [{"content-length", integer_to_list(size(Body))} | Headers]; -- false -> -- [{"content-length", integer_to_list(length(Body))} | Headers]; -- _ -> -- Headers -- end, -- {Headers_2, Body_1} = -- case get_value(transfer_encoding, Options, false) of -- false -> -- {Headers_1, Body}; -- {chunked, ChunkSize} -> -- {[{X, Y} || {X, Y} <- Headers_1, -- X /= "Content-Length", -- X /= "content-length", -- X /= content_length] ++ -- [{"Transfer-Encoding", "chunked"}], -- chunk_request_body(Body, ChunkSize)} -- end, -- Headers_3 = cons_headers(Headers_2), -- Uri = case get_value(use_absolute_uri, Options, false) or UseProxy of -- true -> -- AbsPath; -- false -> -- RelPath -- end, -- {[method(Method), " ", Uri, " ", HttpVsn, crnl(), Headers_3, crnl()], Body_1}. -- --http_vsn_string({0,9}) -> "HTTP/0.9"; --http_vsn_string({1,0}) -> "HTTP/1.0"; --http_vsn_string({1,1}) -> "HTTP/1.1". -- --cons_headers(Headers) -> -- cons_headers(Headers, []). --cons_headers([], Acc) -> -- encode_headers(Acc); --cons_headers([{basic_auth, {U,P}} | T], Acc) -> -- cons_headers(T, [{"Authorization", -- ["Basic ", ibrowse_lib:encode_base64(U++":"++P)]} | Acc]); --cons_headers([{cookie, Cookie} | T], Acc) -> -- cons_headers(T, [{"Cookie", Cookie} | Acc]); --cons_headers([{content_length, L} | T], Acc) -> -- cons_headers(T, [{"Content-Length", L} | Acc]); --cons_headers([{content_type, L} | T], Acc) -> -- cons_headers(T, [{"Content-Type", L} | Acc]); --cons_headers([H | T], Acc) -> -- cons_headers(T, [H | Acc]). -- --encode_headers(L) -> -- encode_headers(L, []). --encode_headers([{http_vsn, _Val} | T], Acc) -> -- encode_headers(T, Acc); --encode_headers([{Name,Val} | T], Acc) when is_list(Name) -> -- encode_headers(T, [[Name, ": ", fmt_val(Val), crnl()] | Acc]); --encode_headers([{Name,Val} | T], Acc) when is_atom(Name) -> -- encode_headers(T, [[atom_to_list(Name), ": ", fmt_val(Val), crnl()] | Acc]); --encode_headers([], Acc) -> -- lists:reverse(Acc). -- --chunk_request_body(Body, ChunkSize) -> -- chunk_request_body(Body, ChunkSize, []). -- --chunk_request_body(Body, _ChunkSize, Acc) when Body == <<>>; Body == [] -> -- LastChunk = "0\r\n", -- lists:reverse(["\r\n", LastChunk | Acc]); --chunk_request_body(Body, ChunkSize, Acc) when is_binary(Body), -- size(Body) >= ChunkSize -> -- <> = Body, -- Chunk = [ibrowse_lib:dec2hex(4, ChunkSize),"\r\n", -- ChunkBody, "\r\n"], -- chunk_request_body(Rest, ChunkSize, [Chunk | Acc]); --chunk_request_body(Body, _ChunkSize, Acc) when is_binary(Body) -> -- BodySize = size(Body), -- Chunk = [ibrowse_lib:dec2hex(4, BodySize),"\r\n", -- Body, "\r\n"], -- LastChunk = "0\r\n", -- lists:reverse(["\r\n", LastChunk, Chunk | Acc]); --chunk_request_body(Body, ChunkSize, Acc) when is_list(Body), -- length(Body) >= ChunkSize -> -- {ChunkBody, Rest} = split_list_at(Body, ChunkSize), -- Chunk = [ibrowse_lib:dec2hex(4, ChunkSize),"\r\n", -- ChunkBody, "\r\n"], -- chunk_request_body(Rest, ChunkSize, [Chunk | Acc]); --chunk_request_body(Body, _ChunkSize, Acc) when is_list(Body) -> -- BodySize = length(Body), -- Chunk = [ibrowse_lib:dec2hex(4, BodySize),"\r\n", -- Body, "\r\n"], -- LastChunk = "0\r\n", -- lists:reverse(["\r\n", LastChunk, Chunk | Acc]). -- -- --parse_response(_Data, #state{cur_req = undefined}=State) -> -- State#state{status = idle}; --parse_response(Data, #state{reply_buffer = Acc, reqs = Reqs, -- cur_req = CurReq} = State) -> -- #request{from=From, stream_to=StreamTo, req_id=ReqId, -- method=Method, response_format = Resp_format} = CurReq, -- MaxHeaderSize = ibrowse:get_config_value(max_headers_size, infinity), -- case scan_header(Acc, Data) of -- {yes, Headers, Data_1} -> -- do_trace("Recvd Header Data -> ~s~n----~n", [Headers]), -- do_trace("Recvd headers~n--- Headers Begin ---~n~s~n--- Headers End ---~n~n", [Headers]), -- {HttpVsn, StatCode, Headers_1} = parse_headers(Headers), -- do_trace("HttpVsn: ~p StatusCode: ~p Headers_1 -> ~1000.p~n", [HttpVsn, StatCode, Headers_1]), -- LCHeaders = [{to_lower(X), Y} || {X,Y} <- Headers_1], -- ConnClose = to_lower(get_value("connection", LCHeaders, "false")), -- IsClosing = is_connection_closing(HttpVsn, ConnClose), -- case IsClosing of -- true -> -- shutting_down(State); -- false -> -- ok -- end, -- State_1 = State#state{recvd_headers=Headers_1, status=get_body, -- reply_buffer = <<>>, -- http_status_code=StatCode, is_closing=IsClosing}, -- put(conn_close, ConnClose), -- TransferEncoding = to_lower(get_value("transfer-encoding", LCHeaders, "false")), -- case get_value("content-length", LCHeaders, undefined) of -- _ when Method == head -> -- {_, Reqs_1} = queue:out(Reqs), -- send_async_headers(ReqId, StreamTo, StatCode, Headers_1), -- State_1_1 = do_reply(State_1, From, StreamTo, ReqId, Resp_format, -- {ok, StatCode, Headers_1, []}), -- cancel_timer(State_1_1#state.send_timer, {eat_message, {req_timedout, From}}), -- State_2 = reset_state(State_1_1), -- State_3 = set_cur_request(State_2#state{reqs = Reqs_1}), -- parse_response(Data_1, State_3); -- _ when hd(StatCode) == $1 -> -- %% No message body is expected. Server may send -- %% one or more 1XX responses before a proper -- %% response. -- send_async_headers(ReqId, StreamTo, StatCode, Headers_1), -- do_trace("Recvd a status code of ~p. Ignoring and waiting for a proper response~n", [StatCode]), -- parse_response(Data_1, State_1#state{recvd_headers = [], -- status = get_header}); -- _ when StatCode == "204"; -- StatCode == "304" -> -- %% No message body is expected for these Status Codes. -- %% RFC2616 - Sec 4.4 -- {_, Reqs_1} = queue:out(Reqs), -- send_async_headers(ReqId, StreamTo, StatCode, Headers_1), -- State_1_1 = do_reply(State_1, From, StreamTo, ReqId, Resp_format, -- {ok, StatCode, Headers_1, []}), -- cancel_timer(State_1_1#state.send_timer, {eat_message, {req_timedout, From}}), -- State_2 = reset_state(State_1_1), -- State_3 = set_cur_request(State_2#state{reqs = Reqs_1}), -- parse_response(Data_1, State_3); -- _ when TransferEncoding == "chunked" -> -- do_trace("Chunked encoding detected...~n",[]), -- send_async_headers(ReqId, StreamTo, StatCode, Headers_1), -- case parse_11_response(Data_1, State_1#state{transfer_encoding=chunked, -- chunk_size=chunk_start, -- reply_buffer = <<>>}) of -- {error, Reason} -> -- fail_pipelined_requests(State_1, -- {error, {Reason, -- {stat_code, StatCode}, Headers_1}}), -- {error, Reason}; -- State_2 -> -- State_2 -- end; -- undefined when HttpVsn == "HTTP/1.0"; -- ConnClose == "close" -> -- send_async_headers(ReqId, StreamTo, StatCode, Headers_1), -- State_1#state{reply_buffer = Data_1}; -- undefined -> -- fail_pipelined_requests(State_1, -- {error, {content_length_undefined, -- {stat_code, StatCode}, Headers}}), -- {error, content_length_undefined}; -- V -> -- case catch list_to_integer(V) of -- V_1 when is_integer(V_1), V_1 >= 0 -> -- send_async_headers(ReqId, StreamTo, StatCode, Headers_1), -- do_trace("Recvd Content-Length of ~p~n", [V_1]), -- State_2 = State_1#state{rep_buf_size=0, -- reply_buffer = <<>>, -- content_length=V_1}, -- case parse_11_response(Data_1, State_2) of -- {error, Reason} -> -- fail_pipelined_requests(State_1, -- {error, {Reason, -- {stat_code, StatCode}, Headers_1}}), -- {error, Reason}; -- State_3 -> -- State_3 -- end; -- _ -> -- fail_pipelined_requests(State_1, -- {error, {content_length_undefined, -- {stat_code, StatCode}, Headers}}), -- {error, content_length_undefined} -- end -- end; -- {no, Acc_1} when MaxHeaderSize == infinity -> -- State#state{reply_buffer = Acc_1}; -- {no, Acc_1} when size(Acc_1) < MaxHeaderSize -> -- State#state{reply_buffer = Acc_1}; -- {no, _Acc_1} -> -- fail_pipelined_requests(State, {error, max_headers_size_exceeded}), -- {error, max_headers_size_exceeded} -- end. -- --is_connection_closing("HTTP/0.9", _) -> true; --is_connection_closing(_, "close") -> true; --is_connection_closing("HTTP/1.0", "false") -> true; --is_connection_closing(_, _) -> false. -- --%% This clause determines the chunk size when given data from the beginning of the chunk --parse_11_response(DataRecvd, -- #state{transfer_encoding = chunked, -- chunk_size = chunk_start, -- chunk_size_buffer = Chunk_sz_buf -- } = State) -> -- case scan_crlf(Chunk_sz_buf, DataRecvd) of -- {yes, ChunkHeader, Data_1} -> -- case parse_chunk_header(ChunkHeader) of -- {error, Reason} -> -- {error, Reason}; -- ChunkSize -> -- %% -- %% Do we have to preserve the chunk encoding when -- %% streaming? NO. This should be transparent to the client -- %% process. Chunked encoding was only introduced to make -- %% it efficient for the server. -- %% -- RemLen = size(Data_1), -- do_trace("Determined chunk size: ~p. Already recvd: ~p~n", [ChunkSize, RemLen]), -- parse_11_response(Data_1, State#state{chunk_size_buffer = <<>>, -- deleted_crlf = true, -- recvd_chunk_size = 0, -- chunk_size = ChunkSize}) -- end; -- {no, Data_1} -> -- State#state{chunk_size_buffer = Data_1} -- end; -- --%% This clause is to remove the CRLF between two chunks --%% --parse_11_response(DataRecvd, -- #state{transfer_encoding = chunked, -- chunk_size = tbd, -- chunk_size_buffer = Buf}=State) -> -- case scan_crlf(Buf, DataRecvd) of -- {yes, _, NextChunk} -> -- State_1 = State#state{chunk_size = chunk_start, -- chunk_size_buffer = <<>>, -- deleted_crlf = true}, -- parse_11_response(NextChunk, State_1); -- {no, Data_1} -> -- State#state{chunk_size_buffer = Data_1} -- end; -- --%% This clause deals with the end of a chunked transfer. ibrowse does --%% not support Trailers in the Chunked Transfer encoding. Any trailer --%% received is silently discarded. --parse_11_response(DataRecvd, -- #state{transfer_encoding = chunked, chunk_size = 0, -- cur_req = CurReq, -- deleted_crlf = DelCrlf, -- chunk_size_buffer = Trailer, reqs = Reqs}=State) -> -- do_trace("Detected end of chunked transfer...~n", []), -- DataRecvd_1 = case DelCrlf of -- false -> -- DataRecvd; -- true -> -- <<$\r, $\n, DataRecvd/binary>> -- end, -- case scan_header(Trailer, DataRecvd_1) of -- {yes, _TEHeaders, Rem} -> -- {_, Reqs_1} = queue:out(Reqs), -- State_1 = handle_response(CurReq, State#state{reqs = Reqs_1}), -- parse_response(Rem, reset_state(State_1)); -- {no, Rem} -> -- State#state{chunk_size_buffer = Rem, deleted_crlf = false} -- end; -- --%% This clause extracts a chunk, given the size. --parse_11_response(DataRecvd, -- #state{transfer_encoding = chunked, -- chunk_size = CSz, -- recvd_chunk_size = Recvd_csz, -- rep_buf_size = RepBufSz} = State) -> -- NeedBytes = CSz - Recvd_csz, -- DataLen = size(DataRecvd), -- do_trace("Recvd more data: size: ~p. NeedBytes: ~p~n", [DataLen, NeedBytes]), -- case DataLen >= NeedBytes of -- true -> -- {RemChunk, RemData} = split_binary(DataRecvd, NeedBytes), -- do_trace("Recvd another chunk...~n", []), -- do_trace("RemData -> ~p~n", [RemData]), -- case accumulate_response(RemChunk, State) of -- {error, Reason} -> -- do_trace("Error accumulating response --> ~p~n", [Reason]), -- {error, Reason}; -- #state{} = State_1 -> -- State_2 = State_1#state{chunk_size=tbd}, -- parse_11_response(RemData, State_2) -- end; -- false -> -- accumulate_response(DataRecvd, -- State#state{rep_buf_size = RepBufSz + DataLen, -- recvd_chunk_size = Recvd_csz + DataLen}) -- end; -- --%% This clause to extract the body when Content-Length is specified --parse_11_response(DataRecvd, -- #state{content_length=CL, rep_buf_size=RepBufSz, -- reqs=Reqs}=State) -> -- NeedBytes = CL - RepBufSz, -- DataLen = size(DataRecvd), -- case DataLen >= NeedBytes of -- true -> -- {RemBody, Rem} = split_binary(DataRecvd, NeedBytes), -- {_, Reqs_1} = queue:out(Reqs), -- State_1 = accumulate_response(RemBody, State), -- State_2 = handle_response(State_1#state.cur_req, State_1#state{reqs=Reqs_1}), -- State_3 = reset_state(State_2), -- parse_response(Rem, State_3); -- false -> -- accumulate_response(DataRecvd, State#state{rep_buf_size = (RepBufSz+DataLen)}) -- end. -- --handle_response(#request{from=From, stream_to=StreamTo, req_id=ReqId, -- response_format = Resp_format, -- save_response_to_file = SaveResponseToFile, -- tmp_file_name = TmpFilename, -- tmp_file_fd = Fd -- }, -- #state{http_status_code = SCode, -- send_timer = ReqTimer, -- reply_buffer = RepBuf, -- recvd_headers = RespHeaders}=State) when SaveResponseToFile /= false -> -- Body = RepBuf, -- State_1 = set_cur_request(State), -- file:close(Fd), -- ResponseBody = case TmpFilename of -- undefined -> -- Body; -- _ -> -- {file, TmpFilename} -- end, -- State_2 = do_reply(State_1, From, StreamTo, ReqId, Resp_format, -- {ok, SCode, RespHeaders, ResponseBody}), -- cancel_timer(ReqTimer, {eat_message, {req_timedout, From}}), -- State_2; --handle_response(#request{from=From, stream_to=StreamTo, req_id=ReqId, -- response_format = Resp_format}, -- #state{http_status_code=SCode, recvd_headers=RespHeaders, -- reply_buffer = RepBuf, -- send_timer=ReqTimer}=State) -> -- Body = RepBuf, --%% State_1 = set_cur_request(State), -- State_1 = case get(conn_close) of -- "close" -> -- do_reply(State, From, StreamTo, ReqId, Resp_format, -- {ok, SCode, RespHeaders, Body}), -- exit(normal); -- _ -> -- State_1_1 = do_reply(State, From, StreamTo, ReqId, Resp_format, -- {ok, SCode, RespHeaders, Body}), -- cancel_timer(ReqTimer, {eat_message, {req_timedout, From}}), -- State_1_1 -- end, -- set_cur_request(State_1). -- --reset_state(State) -> -- State#state{status = get_header, -- rep_buf_size = 0, -- streamed_size = 0, -- content_length = undefined, -- reply_buffer = <<>>, -- chunk_size_buffer = <<>>, -- recvd_headers = [], -- deleted_crlf = false, -- http_status_code = undefined, -- chunk_size = undefined, -- transfer_encoding = undefined}. -- --set_cur_request(#state{reqs = Reqs} = State) -> -- case queue:to_list(Reqs) of -- [] -> -- State#state{cur_req = undefined}; -- [NextReq | _] -> -- State#state{cur_req = NextReq} -- end. -- --parse_headers(Headers) -> -- case scan_crlf(Headers) of -- {yes, StatusLine, T} -> -- parse_headers(StatusLine, T); -- {no, StatusLine} -> -- parse_headers(StatusLine, <<>>) -- end. -- --parse_headers(StatusLine, Headers) -> -- Headers_1 = parse_headers_1(Headers), -- case parse_status_line(StatusLine) of -- {ok, HttpVsn, StatCode, _Msg} -> -- put(http_prot_vsn, HttpVsn), -- {HttpVsn, StatCode, Headers_1}; -- _ -> %% A HTTP 0.9 response? -- put(http_prot_vsn, "HTTP/0.9"), -- {"HTTP/0.9", undefined, Headers} -- end. -- --% From RFC 2616 --% --% HTTP/1.1 header field values can be folded onto multiple lines if --% the continuation line begins with a space or horizontal tab. All --% linear white space, including folding, has the same semantics as --% SP. A recipient MAY replace any linear white space with a single --% SP before interpreting the field value or forwarding the message --% downstream. -- parse_headers_1(B) when is_binary(B) -> -- parse_headers_1(binary_to_list(B)); -- parse_headers_1(String) -> -- parse_headers_1(String, [], []). -- --parse_headers_1([$\n, H |T], [$\r | L], Acc) when H == 32; -- H == $\t -> -- parse_headers_1(lists:dropwhile(fun(X) -> -- is_whitespace(X) -- end, T), [32 | L], Acc); --parse_headers_1([$\n|T], [$\r | L], Acc) -> -- case parse_header(lists:reverse(L)) of -- invalid -> -- parse_headers_1(T, [], Acc); -- NewHeader -> -- parse_headers_1(T, [], [NewHeader | Acc]) -- end; --parse_headers_1([H|T], L, Acc) -> -- parse_headers_1(T, [H|L], Acc); --parse_headers_1([], [], Acc) -> -- lists:reverse(Acc); --parse_headers_1([], L, Acc) -> -- Acc_1 = case parse_header(lists:reverse(L)) of -- invalid -> -- Acc; -- NewHeader -> -- [NewHeader | Acc] -- end, -- lists:reverse(Acc_1). -- --parse_status_line(Line) when is_binary(Line) -> -- parse_status_line(binary_to_list(Line)); --parse_status_line(Line) -> -- parse_status_line(Line, get_prot_vsn, [], []). --parse_status_line([32 | T], get_prot_vsn, ProtVsn, StatCode) -> -- parse_status_line(T, get_status_code, ProtVsn, StatCode); --parse_status_line([32 | T], get_status_code, ProtVsn, StatCode) -> -- {ok, lists:reverse(ProtVsn), lists:reverse(StatCode), T}; --parse_status_line([H | T], get_prot_vsn, ProtVsn, StatCode) -> -- parse_status_line(T, get_prot_vsn, [H|ProtVsn], StatCode); --parse_status_line([H | T], get_status_code, ProtVsn, StatCode) -> -- parse_status_line(T, get_status_code, ProtVsn, [H | StatCode]); --parse_status_line([], _, _, _) -> -- http_09. -- --parse_header(B) when is_binary(B) -> -- parse_header(binary_to_list(B)); --parse_header(L) -> -- parse_header(L, []). --parse_header([$: | V], Acc) -> -- {lists:reverse(Acc), string:strip(V)}; --parse_header([H | T], Acc) -> -- parse_header(T, [H | Acc]); --parse_header([], _) -> -- invalid. -- --scan_header(Bin) -> -- case get_crlf_crlf_pos(Bin, 0) of -- {yes, Pos} -> -- {Headers, <<_:4/binary, Body/binary>>} = split_binary(Bin, Pos), -- {yes, Headers, Body}; -- no -> -- {no, Bin} -- end. -- --scan_header(Bin1, Bin2) when size(Bin1) < 4 -> -- scan_header(<>); --scan_header(Bin1, <<>>) -> -- scan_header(Bin1); --scan_header(Bin1, Bin2) -> -- Bin1_already_scanned_size = size(Bin1) - 4, -- <> = Bin1, -- Bin_to_scan = <>, -- case get_crlf_crlf_pos(Bin_to_scan, 0) of -- {yes, Pos} -> -- {Headers_suffix, <<_:4/binary, Body/binary>>} = split_binary(Bin_to_scan, Pos), -- {yes, <>, Body}; -- no -> -- {no, <>} -- end. -- --get_crlf_crlf_pos(<<$\r, $\n, $\r, $\n, _/binary>>, Pos) -> {yes, Pos}; --get_crlf_crlf_pos(<<_, Rest/binary>>, Pos) -> get_crlf_crlf_pos(Rest, Pos + 1); --get_crlf_crlf_pos(<<>>, _) -> no. -- --scan_crlf(Bin) -> -- case get_crlf_pos(Bin) of -- {yes, Pos} -> -- {Prefix, <<_, _, Suffix/binary>>} = split_binary(Bin, Pos), -- {yes, Prefix, Suffix}; -- no -> -- {no, Bin} -- end. -- --scan_crlf(<<>>, Bin2) -> -- scan_crlf(Bin2); --scan_crlf(Bin1, Bin2) when size(Bin1) < 2 -> -- scan_crlf(<>); --scan_crlf(Bin1, Bin2) -> -- scan_crlf_1(size(Bin1) - 2, Bin1, Bin2). -- --scan_crlf_1(Bin1_head_size, Bin1, Bin2) -> -- <> = Bin1, -- Bin3 = <>, -- case get_crlf_pos(Bin3) of -- {yes, Pos} -> -- {Prefix, <<_, _, Suffix/binary>>} = split_binary(Bin3, Pos), -- {yes, list_to_binary([Bin1_head, Prefix]), Suffix}; -- no -> -- {no, list_to_binary([Bin1, Bin2])} -- end. -- --get_crlf_pos(Bin) -> -- get_crlf_pos(Bin, 0). -- --get_crlf_pos(<<$\r, $\n, _/binary>>, Pos) -> {yes, Pos}; --get_crlf_pos(<<_, Rest/binary>>, Pos) -> get_crlf_pos(Rest, Pos + 1); --get_crlf_pos(<<>>, _) -> no. -- --%% scan_crlf(<<$\n, T/binary>>, [$\r | L]) -> {yes, lists:reverse(L), T}; --%% scan_crlf(<>, L) -> scan_crlf(T, [H|L]); --%% scan_crlf(<<>>, L) -> {no, L}; --%% scan_crlf([$\n|T], [$\r | L]) -> {yes, lists:reverse(L), T}; --%% scan_crlf([H|T], L) -> scan_crlf(T, [H|L]); --%% scan_crlf([], L) -> {no, L}. -- --fmt_val(L) when is_list(L) -> L; --fmt_val(I) when is_integer(I) -> integer_to_list(I); --fmt_val(A) when is_atom(A) -> atom_to_list(A); --fmt_val(Term) -> io_lib:format("~p", [Term]). -- --crnl() -> "\r\n". -- --method(get) -> "GET"; --method(post) -> "POST"; --method(head) -> "HEAD"; --method(options) -> "OPTIONS"; --method(put) -> "PUT"; --method(delete) -> "DELETE"; --method(trace) -> "TRACE"; --method(mkcol) -> "MKCOL"; --method(propfind) -> "PROPFIND"; --method(proppatch) -> "PROPPATCH"; --method(lock) -> "LOCK"; --method(unlock) -> "UNLOCK"; --method(move) -> "MOVE"; --method(copy) -> "COPY". -- --%% From RFC 2616 --%% --% The chunked encoding modifies the body of a message in order to --% transfer it as a series of chunks, each with its own size indicator, --% followed by an OPTIONAL trailer containing entity-header --% fields. This allows dynamically produced content to be transferred --% along with the information necessary for the recipient to verify --% that it has received the full message. --% Chunked-Body = *chunk --% last-chunk --% trailer --% CRLF --% chunk = chunk-size [ chunk-extension ] CRLF --% chunk-data CRLF --% chunk-size = 1*HEX --% last-chunk = 1*("0") [ chunk-extension ] CRLF --% chunk-extension= *( ";" chunk-ext-name [ "=" chunk-ext-val ] ) --% chunk-ext-name = token --% chunk-ext-val = token | quoted-string --% chunk-data = chunk-size(OCTET) --% trailer = *(entity-header CRLF) --% The chunk-size field is a string of hex digits indicating the size --% of the chunk. The chunked encoding is ended by any chunk whose size --% is zero, followed by the trailer, which is terminated by an empty --% line. --%% --%% The parsing implemented here discards all chunk extensions. It also --%% strips trailing spaces from the chunk size fields as Apache 1.3.27 was --%% sending them. --parse_chunk_header([]) -> -- throw({error, invalid_chunk_size}); --parse_chunk_header(ChunkHeader) -> -- parse_chunk_header(ChunkHeader, []). -- --parse_chunk_header(<<$;, _/binary>>, Acc) -> -- hexlist_to_integer(lists:reverse(Acc)); --parse_chunk_header(<>, Acc) -> -- case is_whitespace(H) of -- true -> -- parse_chunk_header(T, Acc); -- false -> -- parse_chunk_header(T, [H | Acc]) -- end; --parse_chunk_header(<<>>, Acc) -> -- hexlist_to_integer(lists:reverse(Acc)). -- --is_whitespace($\s) -> true; --is_whitespace($\r) -> true; --is_whitespace($\n) -> true; --is_whitespace($\t) -> true; --is_whitespace(_) -> false. -- -- --send_async_headers(_ReqId, undefined, _StatCode, _Headers) -> -- ok; --send_async_headers(ReqId, StreamTo, StatCode, Headers) -> -- catch StreamTo ! {ibrowse_async_headers, ReqId, StatCode, Headers}. -- --format_response_data(Resp_format, Body) -> -- case Resp_format of -- list when is_list(Body) -> -- flatten(Body); -- list when is_binary(Body) -> -- binary_to_list(Body); -- binary when is_list(Body) -> -- list_to_binary(Body); -- _ -> -- %% This is to cater for sending messages such as -- %% {chunk_start, _}, chunk_end etc -- Body -- end. -- --do_reply(State, From, undefined, _, Resp_format, {ok, St_code, Headers, Body}) -> -- Msg_1 = {ok, St_code, Headers, format_response_data(Resp_format, Body)}, -- gen_server:reply(From, Msg_1), -- dec_pipeline_counter(State); --do_reply(State, From, undefined, _, _, Msg) -> -- gen_server:reply(From, Msg), -- dec_pipeline_counter(State); --do_reply(#state{prev_req_id = Prev_req_id} = State, -- _From, StreamTo, ReqId, Resp_format, {ok, _, _, Body}) -> -- State_1 = dec_pipeline_counter(State), -- case Body of -- [] -> -- ok; -- _ -> -- Body_1 = format_response_data(Resp_format, Body), -- catch StreamTo ! {ibrowse_async_response, ReqId, Body_1} -- end, -- catch StreamTo ! {ibrowse_async_response_end, ReqId}, -- %% We don't want to delete the Req-id to Pid mapping straightaway -- %% as the client may send a stream_next message just while we are -- %% sending back this ibrowse_async_response_end message. If we -- %% deleted this mapping straightaway, the caller will see a -- %% {error, unknown_req_id} when it calls ibrowse:stream_next/1. To -- %% get around this, we store the req id, and clear it after the -- %% next request. If there are wierd combinations of stream, -- %% stream_once and sync requests on the same connection, it will -- %% take a while for the req_id-pid mapping to get cleared, but it -- %% should do no harm. -- ets:delete(ibrowse_stream, {req_id_pid, Prev_req_id}), -- State_1#state{prev_req_id = ReqId}; --do_reply(State, _From, StreamTo, ReqId, Resp_format, Msg) -> -- State_1 = dec_pipeline_counter(State), -- Msg_1 = format_response_data(Resp_format, Msg), -- catch StreamTo ! {ibrowse_async_response, ReqId, Msg_1}, -- State_1. -- --do_interim_reply(undefined, _, _ReqId, _Msg) -> -- ok; --do_interim_reply(StreamTo, Response_format, ReqId, Msg) -> -- Msg_1 = format_response_data(Response_format, Msg), -- catch StreamTo ! {ibrowse_async_response, ReqId, Msg_1}. -- --do_error_reply(#state{reqs = Reqs} = State, Err) -> -- ReqList = queue:to_list(Reqs), -- lists:foreach(fun(#request{from=From, stream_to=StreamTo, req_id=ReqId, -- response_format = Resp_format}) -> -- ets:delete(ibrowse_stream, {req_id_pid, ReqId}), -- do_reply(State, From, StreamTo, ReqId, Resp_format, {error, Err}) -- end, ReqList). -- --fail_pipelined_requests(#state{reqs = Reqs, cur_req = CurReq} = State, Reply) -> -- {_, Reqs_1} = queue:out(Reqs), -- #request{from=From, stream_to=StreamTo, req_id=ReqId, -- response_format = Resp_format} = CurReq, -- do_reply(State, From, StreamTo, ReqId, Resp_format, Reply), -- do_error_reply(State#state{reqs = Reqs_1}, previous_request_failed). -- --split_list_at(List, N) -> -- split_list_at(List, N, []). --split_list_at([], _, Acc) -> -- {lists:reverse(Acc), []}; --split_list_at(List2, 0, List1) -> -- {lists:reverse(List1), List2}; --split_list_at([H | List2], N, List1) -> -- split_list_at(List2, N-1, [H | List1]). -- --hexlist_to_integer(List) -> -- hexlist_to_integer(lists:reverse(List), 1, 0). --hexlist_to_integer([H | T], Multiplier, Acc) -> -- hexlist_to_integer(T, Multiplier*16, Multiplier*to_ascii(H) + Acc); --hexlist_to_integer([], _, Acc) -> -- Acc. -- --to_ascii($A) -> 10; --to_ascii($a) -> 10; --to_ascii($B) -> 11; --to_ascii($b) -> 11; --to_ascii($C) -> 12; --to_ascii($c) -> 12; --to_ascii($D) -> 13; --to_ascii($d) -> 13; --to_ascii($E) -> 14; --to_ascii($e) -> 14; --to_ascii($F) -> 15; --to_ascii($f) -> 15; --to_ascii($1) -> 1; --to_ascii($2) -> 2; --to_ascii($3) -> 3; --to_ascii($4) -> 4; --to_ascii($5) -> 5; --to_ascii($6) -> 6; --to_ascii($7) -> 7; --to_ascii($8) -> 8; --to_ascii($9) -> 9; --to_ascii($0) -> 0. -- --cancel_timer(undefined) -> ok; --cancel_timer(Ref) -> erlang:cancel_timer(Ref). -- --cancel_timer(Ref, {eat_message, Msg}) -> -- cancel_timer(Ref), -- receive -- Msg -> -- ok -- after 0 -> -- ok -- end. -- --make_req_id() -> -- now(). -- --to_lower(Str) -> -- to_lower(Str, []). --to_lower([H|T], Acc) when H >= $A, H =< $Z -> -- to_lower(T, [H+32|Acc]); --to_lower([H|T], Acc) -> -- to_lower(T, [H|Acc]); --to_lower([], Acc) -> -- lists:reverse(Acc). -- --shutting_down(#state{lb_ets_tid = undefined}) -> -- ok; --shutting_down(#state{lb_ets_tid = Tid, -- cur_pipeline_size = Sz}) -> -- catch ets:delete(Tid, {Sz, self()}). -- --inc_pipeline_counter(#state{is_closing = true} = State) -> -- State; --inc_pipeline_counter(#state{cur_pipeline_size = Pipe_sz} = State) -> -- State#state{cur_pipeline_size = Pipe_sz + 1}. -- --dec_pipeline_counter(#state{is_closing = true} = State) -> -- State; --dec_pipeline_counter(#state{lb_ets_tid = undefined} = State) -> -- State; --dec_pipeline_counter(#state{cur_pipeline_size = Pipe_sz, -- lb_ets_tid = Tid} = State) -> -- ets:delete(Tid, {Pipe_sz, self()}), -- ets:insert(Tid, {{Pipe_sz - 1, self()}, []}), -- State#state{cur_pipeline_size = Pipe_sz - 1}. -- --flatten([H | _] = L) when is_integer(H) -> -- L; --flatten([H | _] = L) when is_list(H) -> -- lists:flatten(L); --flatten([]) -> -- []. -- --get_stream_chunk_size(Options) -> -- case lists:keysearch(stream_chunk_size, 1, Options) of -- {value, {_, V}} when V > 0 -> -- V; -- _ -> -- ?DEFAULT_STREAM_CHUNK_SIZE -- end. -- --get_inac_timeout(#state{cur_req = #request{options = Opts}}) -> -- get_value(inactivity_timeout, Opts, infinity); --get_inac_timeout(#state{cur_req = undefined}) -> -- infinity. -diff --git a/src/ibrowse/ibrowse_lb.erl b/src/ibrowse/ibrowse_lb.erl -deleted file mode 100644 -index 834054a..0000000 ---- a/src/ibrowse/ibrowse_lb.erl -+++ /dev/null -@@ -1,216 +0,0 @@ --%%%------------------------------------------------------------------- --%%% File : ibrowse_lb.erl --%%% Author : chandru --%%% Description : --%%% --%%% Created : 6 Mar 2008 by chandru --%%%------------------------------------------------------------------- ---module(ibrowse_lb). -- ---vsn('$Id: ibrowse_lb.erl,v 1.2 2009/07/01 22:43:19 chandrusf Exp $ '). ---author(chandru). ---behaviour(gen_server). --%%-------------------------------------------------------------------- --%% Include files --%%-------------------------------------------------------------------- -- --%%-------------------------------------------------------------------- --%% External exports ---export([ -- start_link/1, -- spawn_connection/5 -- ]). -- --%% gen_server callbacks ---export([ -- init/1, -- handle_call/3, -- handle_cast/2, -- handle_info/2, -- terminate/2, -- code_change/3 -- ]). -- ---record(state, {parent_pid, -- ets_tid, -- host, -- port, -- max_sessions, -- max_pipeline_size, -- num_cur_sessions = 0}). -- ---include("ibrowse.hrl"). -- --%%==================================================================== --%% External functions --%%==================================================================== --%%-------------------------------------------------------------------- --%% Function: start_link/0 --%% Description: Starts the server --%%-------------------------------------------------------------------- --start_link(Args) -> -- gen_server:start_link(?MODULE, Args, []). -- --%%==================================================================== --%% Server functions --%%==================================================================== -- --%%-------------------------------------------------------------------- --%% Function: init/1 --%% Description: Initiates the server --%% Returns: {ok, State} | --%% {ok, State, Timeout} | --%% ignore | --%% {stop, Reason} --%%-------------------------------------------------------------------- --init([Host, Port]) -> -- process_flag(trap_exit, true), -- Max_sessions = ibrowse:get_config_value({max_sessions, Host, Port}, 10), -- Max_pipe_sz = ibrowse:get_config_value({max_pipeline_size, Host, Port}, 10), -- put(my_trace_flag, ibrowse_lib:get_trace_status(Host, Port)), -- put(ibrowse_trace_token, ["LB: ", Host, $:, integer_to_list(Port)]), -- Tid = ets:new(ibrowse_lb, [public, ordered_set]), -- {ok, #state{parent_pid = whereis(ibrowse), -- host = Host, -- port = Port, -- ets_tid = Tid, -- max_pipeline_size = Max_pipe_sz, -- max_sessions = Max_sessions}}. -- --spawn_connection(Lb_pid, Url, -- Max_sessions, -- Max_pipeline_size, -- SSL_options) -- when is_pid(Lb_pid), -- is_record(Url, url), -- is_integer(Max_pipeline_size), -- is_integer(Max_sessions) -> -- gen_server:call(Lb_pid, -- {spawn_connection, Url, Max_sessions, Max_pipeline_size, SSL_options}). --%%-------------------------------------------------------------------- --%% Function: handle_call/3 --%% Description: Handling call messages --%% Returns: {reply, Reply, State} | --%% {reply, Reply, State, Timeout} | --%% {noreply, State} | --%% {noreply, State, Timeout} | --%% {stop, Reason, Reply, State} | (terminate/2 is called) --%% {stop, Reason, State} (terminate/2 is called) --%%-------------------------------------------------------------------- --% handle_call({spawn_connection, _Url, Max_sess, Max_pipe, _}, _From, --% #state{max_sessions = Max_sess, --% ets_tid = Tid, --% max_pipeline_size = Max_pipe_sz, --% num_cur_sessions = Num} = State) --% when Num >= Max -> --% Reply = find_best_connection(Tid), --% {reply, sorry_dude_reuse, State}; -- --%% Update max_sessions in #state with supplied value --handle_call({spawn_connection, _Url, Max_sess, Max_pipe, _}, _From, -- #state{num_cur_sessions = Num} = State) -- when Num >= Max_sess -> -- State_1 = maybe_create_ets(State), -- Reply = find_best_connection(State_1#state.ets_tid, Max_pipe), -- {reply, Reply, State_1#state{max_sessions = Max_sess}}; -- --handle_call({spawn_connection, Url, _Max_sess, _Max_pipe, SSL_options}, _From, -- #state{num_cur_sessions = Cur} = State) -> -- State_1 = maybe_create_ets(State), -- Tid = State_1#state.ets_tid, -- {ok, Pid} = ibrowse_http_client:start_link({Tid, Url, SSL_options}), -- ets:insert(Tid, {{1, Pid}, []}), -- {reply, {ok, Pid}, State_1#state{num_cur_sessions = Cur + 1}}; -- --handle_call(Request, _From, State) -> -- Reply = {unknown_request, Request}, -- {reply, Reply, State}. -- --%%-------------------------------------------------------------------- --%% Function: handle_cast/2 --%% Description: Handling cast messages --%% Returns: {noreply, State} | --%% {noreply, State, Timeout} | --%% {stop, Reason, State} (terminate/2 is called) --%%-------------------------------------------------------------------- --handle_cast(_Msg, State) -> -- {noreply, State}. -- --%%-------------------------------------------------------------------- --%% Function: handle_info/2 --%% Description: Handling all non call/cast messages --%% Returns: {noreply, State} | --%% {noreply, State, Timeout} | --%% {stop, Reason, State} (terminate/2 is called) --%%-------------------------------------------------------------------- --handle_info({'EXIT', Parent, _Reason}, #state{parent_pid = Parent} = State) -> -- {stop, normal, State}; -- --handle_info({'EXIT', _Pid, _Reason}, #state{ets_tid = undefined} = State) -> -- {noreply, State}; -- --handle_info({'EXIT', Pid, _Reason}, -- #state{num_cur_sessions = Cur, -- ets_tid = Tid} = State) -> -- ets:match_delete(Tid, {{'_', Pid}, '_'}), -- Cur_1 = Cur - 1, -- State_1 = case Cur_1 of -- 0 -> -- ets:delete(Tid), -- State#state{ets_tid = undefined}; -- _ -> -- State -- end, -- {noreply, State_1#state{num_cur_sessions = Cur_1}}; -- --handle_info({trace, Bool}, #state{ets_tid = undefined} = State) -> -- put(my_trace_flag, Bool), -- {noreply, State}; -- --handle_info({trace, Bool}, #state{ets_tid = Tid} = State) -> -- ets:foldl(fun({{_, Pid}, _}, Acc) when is_pid(Pid) -> -- catch Pid ! {trace, Bool}, -- Acc; -- (_, Acc) -> -- Acc -- end, undefined, Tid), -- put(my_trace_flag, Bool), -- {noreply, State}; -- --handle_info(_Info, State) -> -- {noreply, State}. -- --%%-------------------------------------------------------------------- --%% Function: terminate/2 --%% Description: Shutdown the server --%% Returns: any (ignored by gen_server) --%%-------------------------------------------------------------------- --terminate(_Reason, _State) -> -- ok. -- --%%-------------------------------------------------------------------- --%% Func: code_change/3 --%% Purpose: Convert process state when code is changed --%% Returns: {ok, NewState} --%%-------------------------------------------------------------------- --code_change(_OldVsn, State, _Extra) -> -- {ok, State}. -- --%%-------------------------------------------------------------------- --%%% Internal functions --%%-------------------------------------------------------------------- --find_best_connection(Tid, Max_pipe) -> -- case ets:first(Tid) of -- {Cur_sz, Pid} when Cur_sz < Max_pipe -> -- ets:delete(Tid, {Cur_sz, Pid}), -- ets:insert(Tid, {{Cur_sz + 1, Pid}, []}), -- {ok, Pid}; -- _ -> -- {error, retry_later} -- end. -- --maybe_create_ets(#state{ets_tid = undefined} = State) -> -- Tid = ets:new(ibrowse_lb, [public, ordered_set]), -- State#state{ets_tid = Tid}; --maybe_create_ets(State) -> -- State. -diff --git a/src/ibrowse/ibrowse_lib.erl b/src/ibrowse/ibrowse_lib.erl -deleted file mode 100644 -index 6c7b154..0000000 ---- a/src/ibrowse/ibrowse_lib.erl -+++ /dev/null -@@ -1,399 +0,0 @@ --%%% File : ibrowse_lib.erl --%%% Author : Chandrashekhar Mullaparthi --%%% Description : --%%% Created : 27 Feb 2004 by Chandrashekhar Mullaparthi --%% @doc Module with a few useful functions -- ---module(ibrowse_lib). ---vsn('$Id: ibrowse_lib.erl,v 1.6 2008/03/27 01:35:50 chandrusf Exp $ '). ---author('chandru'). ---ifdef(debug). ---compile(export_all). ---endif. -- ---include("ibrowse.hrl"). -- ---export([ -- get_trace_status/2, -- do_trace/2, -- do_trace/3, -- url_encode/1, -- decode_rfc822_date/1, -- status_code/1, -- dec2hex/2, -- drv_ue/1, -- drv_ue/2, -- encode_base64/1, -- decode_base64/1, -- get_value/2, -- get_value/3, -- parse_url/1, -- printable_date/0 -- ]). -- --get_trace_status(Host, Port) -> -- ibrowse:get_config_value({trace, Host, Port}, false). -- --drv_ue(Str) -> -- [{port, Port}| _] = ets:lookup(ibrowse_table, port), -- drv_ue(Str, Port). --drv_ue(Str, Port) -> -- case erlang:port_control(Port, 1, Str) of -- [] -> -- Str; -- Res -> -- Res -- end. -- --%% @doc URL-encodes a string based on RFC 1738. Returns a flat list. --%% @spec url_encode(Str) -> UrlEncodedStr --%% Str = string() --%% UrlEncodedStr = string() --url_encode(Str) when is_list(Str) -> -- url_encode_char(lists:reverse(Str), []). -- --url_encode_char([X | T], Acc) when X >= $0, X =< $9 -> -- url_encode_char(T, [X | Acc]); --url_encode_char([X | T], Acc) when X >= $a, X =< $z -> -- url_encode_char(T, [X | Acc]); --url_encode_char([X | T], Acc) when X >= $A, X =< $Z -> -- url_encode_char(T, [X | Acc]); --url_encode_char([X | T], Acc) when X == $-; X == $_; X == $. -> -- url_encode_char(T, [X | Acc]); --url_encode_char([32 | T], Acc) -> -- url_encode_char(T, [$+ | Acc]); --url_encode_char([X | T], Acc) -> -- url_encode_char(T, [$%, d2h(X bsr 4), d2h(X band 16#0f) | Acc]); --url_encode_char([], Acc) -> -- Acc. -- --d2h(N) when N<10 -> N+$0; --d2h(N) -> N+$a-10. -- --decode_rfc822_date(String) when is_list(String) -> -- case catch decode_rfc822_date_1(string:tokens(String, ", \t\r\n")) of -- {'EXIT', _} -> -- {error, invalid_date}; -- Res -> -- Res -- end. -- --% TODO: Have to handle the Zone --decode_rfc822_date_1([_,DayInt,Month,Year, Time,Zone]) -> -- decode_rfc822_date_1([DayInt,Month,Year, Time,Zone]); --decode_rfc822_date_1([Day,Month,Year, Time,_Zone]) -> -- DayI = list_to_integer(Day), -- MonthI = month_int(Month), -- YearI = list_to_integer(Year), -- TimeTup = case string:tokens(Time, ":") of -- [H,M] -> -- {list_to_integer(H), -- list_to_integer(M), -- 0}; -- [H,M,S] -> -- {list_to_integer(H), -- list_to_integer(M), -- list_to_integer(S)} -- end, -- {{YearI,MonthI,DayI}, TimeTup}. -- --month_int("Jan") -> 1; --month_int("Feb") -> 2; --month_int("Mar") -> 3; --month_int("Apr") -> 4; --month_int("May") -> 5; --month_int("Jun") -> 6; --month_int("Jul") -> 7; --month_int("Aug") -> 8; --month_int("Sep") -> 9; --month_int("Oct") -> 10; --month_int("Nov") -> 11; --month_int("Dec") -> 12. -- --%% @doc Given a status code, returns an atom describing the status code. --%% @spec status_code(StatusCode::status_code()) -> StatusDescription --%% status_code() = string() | integer() --%% StatusDescription = atom() --status_code(100) -> continue; --status_code(101) -> switching_protocols; --status_code(102) -> processing; --status_code(200) -> ok; --status_code(201) -> created; --status_code(202) -> accepted; --status_code(203) -> non_authoritative_information; --status_code(204) -> no_content; --status_code(205) -> reset_content; --status_code(206) -> partial_content; --status_code(207) -> multi_status; --status_code(300) -> multiple_choices; --status_code(301) -> moved_permanently; --status_code(302) -> found; --status_code(303) -> see_other; --status_code(304) -> not_modified; --status_code(305) -> use_proxy; --status_code(306) -> unused; --status_code(307) -> temporary_redirect; --status_code(400) -> bad_request; --status_code(401) -> unauthorized; --status_code(402) -> payment_required; --status_code(403) -> forbidden; --status_code(404) -> not_found; --status_code(405) -> method_not_allowed; --status_code(406) -> not_acceptable; --status_code(407) -> proxy_authentication_required; --status_code(408) -> request_timeout; --status_code(409) -> conflict; --status_code(410) -> gone; --status_code(411) -> length_required; --status_code(412) -> precondition_failed; --status_code(413) -> request_entity_too_large; --status_code(414) -> request_uri_too_long; --status_code(415) -> unsupported_media_type; --status_code(416) -> requested_range_not_satisfiable; --status_code(417) -> expectation_failed; --status_code(422) -> unprocessable_entity; --status_code(423) -> locked; --status_code(424) -> failed_dependency; --status_code(500) -> internal_server_error; --status_code(501) -> not_implemented; --status_code(502) -> bad_gateway; --status_code(503) -> service_unavailable; --status_code(504) -> gateway_timeout; --status_code(505) -> http_version_not_supported; --status_code(507) -> insufficient_storage; --status_code(X) when is_list(X) -> status_code(list_to_integer(X)); --status_code(_) -> unknown_status_code. -- --%% @doc dec2hex taken from gtk.erl in std dist --%% M = integer() -- number of hex digits required --%% N = integer() -- the number to represent as hex --%% @spec dec2hex(M::integer(), N::integer()) -> string() --dec2hex(M,N) -> dec2hex(M,N,[]). -- --dec2hex(0,_N,Ack) -> Ack; --dec2hex(M,N,Ack) -> dec2hex(M-1,N bsr 4,[d2h(N band 15)|Ack]). -- --%% @doc Implements the base64 encoding algorithm. The output data type matches in the input data type. --%% @spec encode_base64(In) -> Out --%% In = string() | binary() --%% Out = string() | binary() --encode_base64(List) when is_list(List) -> -- encode_base64_1(list_to_binary(List)); --encode_base64(Bin) when is_binary(Bin) -> -- List = encode_base64_1(Bin), -- list_to_binary(List). -- --encode_base64_1(<>) -> -- [int_to_b64(A), int_to_b64(B), -- int_to_b64(C), int_to_b64(D) | encode_base64_1(Rest)]; --encode_base64_1(<>) -> -- [int_to_b64(A), int_to_b64(B), int_to_b64(C bsl 2), $=]; --encode_base64_1(<>) -> -- [int_to_b64(A), int_to_b64(B bsl 4), $=, $=]; --encode_base64_1(<<>>) -> -- []. -- --%% @doc Implements the base64 decoding algorithm. The output data type matches in the input data type. --%% @spec decode_base64(In) -> Out | exit({error, invalid_input}) --%% In = string() | binary() --%% Out = string() | binary() --decode_base64(List) when is_list(List) -> -- decode_base64_1(List, []); --decode_base64(Bin) when is_binary(Bin) -> -- List = decode_base64_1(binary_to_list(Bin), []), -- list_to_binary(List). -- --decode_base64_1([H | T], Acc) when ((H == $\t) or -- (H == 32) or -- (H == $\r) or -- (H == $\n)) -> -- decode_base64_1(T, Acc); -- --decode_base64_1([$=, $=], Acc) -> -- lists:reverse(Acc); --decode_base64_1([$=, _ | _], _Acc) -> -- exit({error, invalid_input}); -- --decode_base64_1([A1, B1, $=, $=], Acc) -> -- A = b64_to_int(A1), -- B = b64_to_int(B1), -- Oct1 = (A bsl 2) bor (B bsr 4), -- decode_base64_1([], [Oct1 | Acc]); --decode_base64_1([A1, B1, C1, $=], Acc) -> -- A = b64_to_int(A1), -- B = b64_to_int(B1), -- C = b64_to_int(C1), -- Oct1 = (A bsl 2) bor (B bsr 4), -- Oct2 = ((B band 16#f) bsl 6) bor (C bsr 2), -- decode_base64_1([], [Oct2, Oct1 | Acc]); --decode_base64_1([A1, B1, C1, D1 | T], Acc) -> -- A = b64_to_int(A1), -- B = b64_to_int(B1), -- C = b64_to_int(C1), -- D = b64_to_int(D1), -- Oct1 = (A bsl 2) bor (B bsr 4), -- Oct2 = ((B band 16#f) bsl 4) bor (C bsr 2), -- Oct3 = ((C band 2#11) bsl 6) bor D, -- decode_base64_1(T, [Oct3, Oct2, Oct1 | Acc]); --decode_base64_1([], Acc) -> -- lists:reverse(Acc). -- --%% Taken from httpd_util.erl --int_to_b64(X) when X >= 0, X =< 25 -> X + $A; --int_to_b64(X) when X >= 26, X =< 51 -> X - 26 + $a; --int_to_b64(X) when X >= 52, X =< 61 -> X - 52 + $0; --int_to_b64(62) -> $+; --int_to_b64(63) -> $/. -- --%% Taken from httpd_util.erl --b64_to_int(X) when X >= $A, X =< $Z -> X - $A; --b64_to_int(X) when X >= $a, X =< $z -> X - $a + 26; --b64_to_int(X) when X >= $0, X =< $9 -> X - $0 + 52; --b64_to_int($+) -> 62; --b64_to_int($/) -> 63. -- --get_value(Tag, TVL, DefVal) -> -- case lists:keysearch(Tag, 1, TVL) of -- false -> -- DefVal; -- {value, {_, Val}} -> -- Val -- end. -- --get_value(Tag, TVL) -> -- {value, {_, V}} = lists:keysearch(Tag,1,TVL), -- V. -- --parse_url(Url) -> -- parse_url(Url, get_protocol, #url{abspath=Url}, []). -- --parse_url([$:, $/, $/ | _], get_protocol, Url, []) -> -- {invalid_uri_1, Url}; --parse_url([$:, $/, $/ | T], get_protocol, Url, TmpAcc) -> -- Prot = list_to_atom(lists:reverse(TmpAcc)), -- parse_url(T, get_username, -- Url#url{protocol = Prot}, -- []); --parse_url([$/ | T], get_username, Url, TmpAcc) -> -- %% No username/password. No port number -- Url#url{host = lists:reverse(TmpAcc), -- port = default_port(Url#url.protocol), -- path = [$/ | T]}; --parse_url([$: | T], get_username, Url, TmpAcc) -> -- %% It is possible that no username/password has been -- %% specified. But we'll continue with the assumption that there is -- %% a username/password. If we encounter a '@' later on, there is a -- %% username/password indeed. If we encounter a '/', it was -- %% actually the hostname -- parse_url(T, get_password, -- Url#url{username = lists:reverse(TmpAcc)}, -- []); --parse_url([$@ | T], get_username, Url, TmpAcc) -> -- parse_url(T, get_host, -- Url#url{username = lists:reverse(TmpAcc), -- password = ""}, -- []); --parse_url([$@ | T], get_password, Url, TmpAcc) -> -- parse_url(T, get_host, -- Url#url{password = lists:reverse(TmpAcc)}, -- []); --parse_url([$/ | T], get_password, Url, TmpAcc) -> -- %% Ok, what we thought was the username/password was the hostname -- %% and portnumber -- #url{username=User} = Url, -- Port = list_to_integer(lists:reverse(TmpAcc)), -- Url#url{host = User, -- port = Port, -- username = undefined, -- password = undefined, -- path = [$/ | T]}; --parse_url([$: | T], get_host, #url{} = Url, TmpAcc) -> -- parse_url(T, get_port, -- Url#url{host = lists:reverse(TmpAcc)}, -- []); --parse_url([$/ | T], get_host, #url{protocol=Prot} = Url, TmpAcc) -> -- Url#url{host = lists:reverse(TmpAcc), -- port = default_port(Prot), -- path = [$/ | T]}; --parse_url([$/ | T], get_port, #url{protocol=Prot} = Url, TmpAcc) -> -- Port = case TmpAcc of -- [] -> -- default_port(Prot); -- _ -> -- list_to_integer(lists:reverse(TmpAcc)) -- end, -- Url#url{port = Port, path = [$/ | T]}; --parse_url([H | T], State, Url, TmpAcc) -> -- parse_url(T, State, Url, [H | TmpAcc]); --parse_url([], get_host, Url, TmpAcc) when TmpAcc /= [] -> -- Url#url{host = lists:reverse(TmpAcc), -- port = default_port(Url#url.protocol), -- path = "/"}; --parse_url([], get_username, Url, TmpAcc) when TmpAcc /= [] -> -- Url#url{host = lists:reverse(TmpAcc), -- port = default_port(Url#url.protocol), -- path = "/"}; --parse_url([], get_port, #url{protocol=Prot} = Url, TmpAcc) -> -- Port = case TmpAcc of -- [] -> -- default_port(Prot); -- _ -> -- list_to_integer(lists:reverse(TmpAcc)) -- end, -- Url#url{port = Port, -- path = "/"}; --parse_url([], get_password, Url, TmpAcc) -> -- %% Ok, what we thought was the username/password was the hostname -- %% and portnumber -- #url{username=User} = Url, -- Port = case TmpAcc of -- [] -> -- default_port(Url#url.protocol); -- _ -> -- list_to_integer(lists:reverse(TmpAcc)) -- end, -- Url#url{host = User, -- port = Port, -- username = undefined, -- password = undefined, -- path = "/"}; --parse_url([], State, Url, TmpAcc) -> -- {invalid_uri_2, State, Url, TmpAcc}. -- --default_port(http) -> 80; --default_port(https) -> 443; --default_port(ftp) -> 21. -- --printable_date() -> -- {{Y,Mo,D},{H, M, S}} = calendar:local_time(), -- {_,_,MicroSecs} = now(), -- [integer_to_list(Y), -- $-, -- integer_to_list(Mo), -- $-, -- integer_to_list(D), -- $_, -- integer_to_list(H), -- $:, -- integer_to_list(M), -- $:, -- integer_to_list(S), -- $:, -- integer_to_list(MicroSecs div 1000)]. -- --do_trace(Fmt, Args) -> -- do_trace(get(my_trace_flag), Fmt, Args). -- ---ifdef(DEBUG). --do_trace(_, Fmt, Args) -> -- io:format("~s -- (~s) - "++Fmt, -- [printable_date(), -- get(ibrowse_trace_token) | Args]). ---else. --do_trace(true, Fmt, Args) -> -- io:format("~s -- (~s) - "++Fmt, -- [printable_date(), -- get(ibrowse_trace_token) | Args]); --do_trace(_, _, _) -> -- ok. ---endif. -diff --git a/src/ibrowse/ibrowse_sup.erl b/src/ibrowse/ibrowse_sup.erl -deleted file mode 100644 -index 1b9b863..0000000 ---- a/src/ibrowse/ibrowse_sup.erl -+++ /dev/null -@@ -1,65 +0,0 @@ --%%%------------------------------------------------------------------- --%%% File : ibrowse_sup.erl --%%% Author : Chandrashekhar Mullaparthi --%%% Description : --%%% --%%% Created : 15 Oct 2003 by Chandrashekhar Mullaparthi --%%%------------------------------------------------------------------- ---module(ibrowse_sup). ---vsn('$Id: ibrowse_sup.erl,v 1.1 2005/05/05 22:28:28 chandrusf Exp $ '). -- ---behaviour(supervisor). --%%-------------------------------------------------------------------- --%% Include files --%%-------------------------------------------------------------------- -- --%%-------------------------------------------------------------------- --%% External exports --%%-------------------------------------------------------------------- ---export([ -- start_link/0 -- ]). -- --%%-------------------------------------------------------------------- --%% Internal exports --%%-------------------------------------------------------------------- ---export([ -- init/1 -- ]). -- --%%-------------------------------------------------------------------- --%% Macros --%%-------------------------------------------------------------------- ---define(SERVER, ?MODULE). -- --%%-------------------------------------------------------------------- --%% Records --%%-------------------------------------------------------------------- -- --%%==================================================================== --%% External functions --%%==================================================================== --%%-------------------------------------------------------------------- --%% Function: start_link/0 --%% Description: Starts the supervisor --%%-------------------------------------------------------------------- --start_link() -> -- supervisor:start_link({local, ?SERVER}, ?MODULE, []). -- --%%==================================================================== --%% Server functions --%%==================================================================== --%%-------------------------------------------------------------------- --%% Func: init/1 --%% Returns: {ok, {SupFlags, [ChildSpec]}} | --%% ignore | --%% {error, Reason} --%%-------------------------------------------------------------------- --init([]) -> -- AChild = {ibrowse,{ibrowse,start_link,[]}, -- permanent,2000,worker,[ibrowse, ibrowse_http_client]}, -- {ok,{{one_for_all,10,1}, [AChild]}}. -- --%%==================================================================== --%% Internal functions --%%==================================================================== -diff --git a/src/ibrowse/ibrowse_test.erl b/src/ibrowse/ibrowse_test.erl -deleted file mode 100644 -index 3dc66ec..0000000 ---- a/src/ibrowse/ibrowse_test.erl -+++ /dev/null -@@ -1,377 +0,0 @@ --%%% File : ibrowse_test.erl --%%% Author : Chandrashekhar Mullaparthi --%%% Description : Test ibrowse --%%% Created : 14 Oct 2003 by Chandrashekhar Mullaparthi -- ---module(ibrowse_test). ---vsn('$Id: ibrowse_test.erl,v 1.4 2009/07/01 22:43:19 chandrusf Exp $ '). ---export([ -- load_test/3, -- send_reqs_1/3, -- do_send_req/2, -- unit_tests/0, -- unit_tests/1, -- unit_tests_1/2, -- drv_ue_test/0, -- drv_ue_test/1, -- ue_test/0, -- ue_test/1, -- verify_chunked_streaming/0, -- verify_chunked_streaming/1, -- i_do_async_req_list/4, -- test_stream_once/3, -- test_stream_once/4 -- ]). -- --test_stream_once(Url, Method, Options) -> -- test_stream_once(Url, Method, Options, 5000). -- --test_stream_once(Url, Method, Options, Timeout) -> -- case ibrowse:send_req(Url, [], Method, [], [{stream_to, {self(), once}} | Options], Timeout) of -- {ibrowse_req_id, Req_id} -> -- case ibrowse:stream_next(Req_id) of -- ok -> -- test_stream_once(Req_id); -- Err -> -- Err -- end; -- Err -> -- Err -- end. -- --test_stream_once(Req_id) -> -- receive -- {ibrowse_async_headers, Req_id, StatCode, Headers} -> -- io:format("Recvd headers~n~p~n", [{ibrowse_async_headers, Req_id, StatCode, Headers}]), -- case ibrowse:stream_next(Req_id) of -- ok -> -- test_stream_once(Req_id); -- Err -> -- Err -- end; -- {ibrowse_async_response, Req_id, {error, Err}} -> -- io:format("Recvd error: ~p~n", [Err]); -- {ibrowse_async_response, Req_id, Body_1} -> -- io:format("Recvd body part: ~n~p~n", [{ibrowse_async_response, Req_id, Body_1}]), -- case ibrowse:stream_next(Req_id) of -- ok -> -- test_stream_once(Req_id); -- Err -> -- Err -- end; -- {ibrowse_async_response_end, Req_id} -> -- ok -- end. --%% Use ibrowse:set_max_sessions/3 and ibrowse:set_max_pipeline_size/3 to --%% tweak settings before running the load test. The defaults are 10 and 10. --load_test(Url, NumWorkers, NumReqsPerWorker) when is_list(Url), -- is_integer(NumWorkers), -- is_integer(NumReqsPerWorker), -- NumWorkers > 0, -- NumReqsPerWorker > 0 -> -- proc_lib:spawn(?MODULE, send_reqs_1, [Url, NumWorkers, NumReqsPerWorker]). -- --send_reqs_1(Url, NumWorkers, NumReqsPerWorker) -> -- Start_time = now(), -- ets:new(pid_table, [named_table, public]), -- ets:new(ibrowse_test_results, [named_table, public]), -- ets:new(ibrowse_errors, [named_table, public, ordered_set]), -- init_results(), -- process_flag(trap_exit, true), -- log_msg("Starting spawning of workers...~n", []), -- spawn_workers(Url, NumWorkers, NumReqsPerWorker), -- log_msg("Finished spawning workers...~n", []), -- do_wait(), -- End_time = now(), -- log_msg("All workers are done...~n", []), -- log_msg("ibrowse_test_results table: ~n~p~n", [ets:tab2list(ibrowse_test_results)]), -- log_msg("Start time: ~1000.p~n", [calendar:now_to_local_time(Start_time)]), -- log_msg("End time : ~1000.p~n", [calendar:now_to_local_time(End_time)]), -- Elapsed_time_secs = trunc(timer:now_diff(End_time, Start_time) / 1000000), -- log_msg("Elapsed : ~p~n", [Elapsed_time_secs]), -- log_msg("Reqs/sec : ~p~n", [round(trunc((NumWorkers*NumReqsPerWorker) / Elapsed_time_secs))]), -- dump_errors(). -- --init_results() -> -- ets:insert(ibrowse_test_results, {crash, 0}), -- ets:insert(ibrowse_test_results, {send_failed, 0}), -- ets:insert(ibrowse_test_results, {other_error, 0}), -- ets:insert(ibrowse_test_results, {success, 0}), -- ets:insert(ibrowse_test_results, {retry_later, 0}), -- ets:insert(ibrowse_test_results, {trid_mismatch, 0}), -- ets:insert(ibrowse_test_results, {success_no_trid, 0}), -- ets:insert(ibrowse_test_results, {failed, 0}), -- ets:insert(ibrowse_test_results, {timeout, 0}), -- ets:insert(ibrowse_test_results, {req_id, 0}). -- --spawn_workers(_Url, 0, _) -> -- ok; --spawn_workers(Url, NumWorkers, NumReqsPerWorker) -> -- Pid = proc_lib:spawn_link(?MODULE, do_send_req, [Url, NumReqsPerWorker]), -- ets:insert(pid_table, {Pid, []}), -- spawn_workers(Url, NumWorkers - 1, NumReqsPerWorker). -- --do_wait() -> -- receive -- {'EXIT', _, normal} -> -- do_wait(); -- {'EXIT', Pid, Reason} -> -- ets:delete(pid_table, Pid), -- ets:insert(ibrowse_errors, {Pid, Reason}), -- ets:update_counter(ibrowse_test_results, crash, 1), -- do_wait(); -- Msg -> -- io:format("Recvd unknown message...~p~n", [Msg]), -- do_wait() -- after 1000 -> -- case ets:info(pid_table, size) of -- 0 -> -- done; -- _ -> -- do_wait() -- end -- end. -- --do_send_req(Url, NumReqs) -> -- do_send_req_1(Url, NumReqs). -- --do_send_req_1(_Url, 0) -> -- ets:delete(pid_table, self()); --do_send_req_1(Url, NumReqs) -> -- Counter = integer_to_list(ets:update_counter(ibrowse_test_results, req_id, 1)), -- case ibrowse:send_req(Url, [{"ib_req_id", Counter}], get, [], [], 10000) of -- {ok, _Status, Headers, _Body} -> -- case lists:keysearch("ib_req_id", 1, Headers) of -- {value, {_, Counter}} -> -- ets:update_counter(ibrowse_test_results, success, 1); -- {value, _} -> -- ets:update_counter(ibrowse_test_results, trid_mismatch, 1); -- false -> -- ets:update_counter(ibrowse_test_results, success_no_trid, 1) -- end; -- {error, req_timedout} -> -- ets:update_counter(ibrowse_test_results, timeout, 1); -- {error, send_failed} -> -- ets:update_counter(ibrowse_test_results, send_failed, 1); -- {error, retry_later} -> -- ets:update_counter(ibrowse_test_results, retry_later, 1); -- Err -> -- ets:insert(ibrowse_errors, {now(), Err}), -- ets:update_counter(ibrowse_test_results, other_error, 1), -- ok -- end, -- do_send_req_1(Url, NumReqs-1). -- --dump_errors() -> -- case ets:info(ibrowse_errors, size) of -- 0 -> -- ok; -- _ -> -- {A, B, C} = now(), -- Filename = lists:flatten( -- io_lib:format("ibrowse_errors_~p_~p_~p.txt" , [A, B, C])), -- case file:open(Filename, [write, delayed_write, raw]) of -- {ok, Iod} -> -- dump_errors(ets:first(ibrowse_errors), Iod); -- Err -> -- io:format("failed to create file ~s. Reason: ~p~n", [Filename, Err]), -- ok -- end -- end. -- --dump_errors('$end_of_table', Iod) -> -- file:close(Iod); --dump_errors(Key, Iod) -> -- [{_, Term}] = ets:lookup(ibrowse_errors, Key), -- file:write(Iod, io_lib:format("~p~n", [Term])), -- dump_errors(ets:next(ibrowse_errors, Key), Iod). -- --%%------------------------------------------------------------------------------ --%% Unit Tests --%%------------------------------------------------------------------------------ ---define(TEST_LIST, [{"http://intranet/messenger", get}, -- {"http://www.google.co.uk", get}, -- {"http://www.google.com", get}, -- {"http://www.google.com", options}, -- {"http://www.sun.com", get}, -- {"http://www.oracle.com", get}, -- {"http://www.bbc.co.uk", get}, -- {"http://www.bbc.co.uk", trace}, -- {"http://www.bbc.co.uk", options}, -- {"http://yaws.hyber.org", get}, -- {"http://jigsaw.w3.org/HTTP/ChunkedScript", get}, -- {"http://jigsaw.w3.org/HTTP/TE/foo.txt", get}, -- {"http://jigsaw.w3.org/HTTP/TE/bar.txt", get}, -- {"http://jigsaw.w3.org/HTTP/connection.html", get}, -- {"http://jigsaw.w3.org/HTTP/cc.html", get}, -- {"http://jigsaw.w3.org/HTTP/cc-private.html", get}, -- {"http://jigsaw.w3.org/HTTP/cc-proxy-revalidate.html", get}, -- {"http://jigsaw.w3.org/HTTP/cc-nocache.html", get}, -- {"http://jigsaw.w3.org/HTTP/h-content-md5.html", get}, -- {"http://jigsaw.w3.org/HTTP/h-retry-after.html", get}, -- {"http://jigsaw.w3.org/HTTP/h-retry-after-date.html", get}, -- {"http://jigsaw.w3.org/HTTP/neg", get}, -- {"http://jigsaw.w3.org/HTTP/negbad", get}, -- {"http://jigsaw.w3.org/HTTP/400/toolong/", get}, -- {"http://jigsaw.w3.org/HTTP/300/", get}, -- {"http://jigsaw.w3.org/HTTP/Basic/", get, [{basic_auth, {"guest", "guest"}}]}, -- {"http://jigsaw.w3.org/HTTP/CL/", get}, -- {"http://www.httpwatch.com/httpgallery/chunked/", get} -- ]). -- --unit_tests() -> -- unit_tests([]). -- --unit_tests(Options) -> -- Options_1 = Options ++ [{connect_timeout, 5000}], -- {Pid, Ref} = erlang:spawn_monitor(?MODULE, unit_tests_1, [self(), Options_1]), -- receive -- {done, Pid} -> -- ok; -- {'DOWN', Ref, _, _, Info} -> -- io:format("Test process crashed: ~p~n", [Info]) -- after 60000 -> -- exit(Pid, kill), -- io:format("Timed out waiting for tests to complete~n", []) -- end. -- --unit_tests_1(Parent, Options) -> -- lists:foreach(fun({Url, Method}) -> -- execute_req(Url, Method, Options); -- ({Url, Method, X_Opts}) -> -- execute_req(Url, Method, X_Opts ++ Options) -- end, ?TEST_LIST), -- Parent ! {done, self()}. -- --verify_chunked_streaming() -> -- verify_chunked_streaming([]). -- --verify_chunked_streaming(Options) -> -- Url = "http://www.httpwatch.com/httpgallery/chunked/", -- io:format("URL: ~s~n", [Url]), -- io:format("Fetching data without streaming...~n", []), -- Result_without_streaming = ibrowse:send_req( -- Url, [], get, [], -- [{response_format, binary} | Options]), -- io:format("Fetching data with streaming as list...~n", []), -- Async_response_list = do_async_req_list( -- Url, get, [{response_format, list} | Options]), -- io:format("Fetching data with streaming as binary...~n", []), -- Async_response_bin = do_async_req_list( -- Url, get, [{response_format, binary} | Options]), -- compare_responses(Result_without_streaming, Async_response_list, Async_response_bin). -- --compare_responses({ok, St_code, _, Body}, {ok, St_code, _, Body}, {ok, St_code, _, Body}) -> -- success; --compare_responses({ok, St_code, _, Body_1}, {ok, St_code, _, Body_2}, {ok, St_code, _, Body_3}) -> -- case Body_1 of -- Body_2 -> -- io:format("Body_1 and Body_2 match~n", []); -- Body_3 -> -- io:format("Body_1 and Body_3 match~n", []); -- _ when Body_2 == Body_3 -> -- io:format("Body_2 and Body_3 match~n", []); -- _ -> -- io:format("All three bodies are different!~n", []) -- end, -- io:format("Body_1 -> ~p~n", [Body_1]), -- io:format("Body_2 -> ~p~n", [Body_2]), -- io:format("Body_3 -> ~p~n", [Body_3]), -- fail_bodies_mismatch; --compare_responses(R1, R2, R3) -> -- io:format("R1 -> ~p~n", [R1]), -- io:format("R2 -> ~p~n", [R2]), -- io:format("R3 -> ~p~n", [R3]), -- fail. -- --%% do_async_req_list(Url) -> --%% do_async_req_list(Url, get). -- --%% do_async_req_list(Url, Method) -> --%% do_async_req_list(Url, Method, [{stream_to, self()}, --%% {stream_chunk_size, 1000}]). -- --do_async_req_list(Url, Method, Options) -> -- {Pid,_} = erlang:spawn_monitor(?MODULE, i_do_async_req_list, -- [self(), Url, Method, -- Options ++ [{stream_chunk_size, 1000}]]), -- io:format("Spawned process ~p~n", [Pid]), -- wait_for_resp(Pid). -- --wait_for_resp(Pid) -> -- receive -- {async_result, Pid, Res} -> -- Res; -- {async_result, Other_pid, _} -> -- io:format("~p: Waiting for result from ~p: got from ~p~n", [self(), Pid, Other_pid]), -- wait_for_resp(Pid); -- {'DOWN', _, _, Pid, Reason} -> -- {'EXIT', Reason}; -- {'DOWN', _, _, _, _} -> -- wait_for_resp(Pid); -- Msg -> -- io:format("Recvd unknown message: ~p~n", [Msg]), -- wait_for_resp(Pid) -- after 10000 -> -- {error, timeout} -- end. -- --i_do_async_req_list(Parent, Url, Method, Options) -> -- Res = ibrowse:send_req(Url, [], Method, [], [{stream_to, self()} | Options]), -- case Res of -- {ibrowse_req_id, Req_id} -> -- Result = wait_for_async_resp(Req_id, undefined, undefined, []), -- Parent ! {async_result, self(), Result}; -- Err -> -- Parent ! {async_result, self(), Err} -- end. -- --wait_for_async_resp(Req_id, Acc_Stat_code, Acc_Headers, Body) -> -- receive -- {ibrowse_async_headers, Req_id, StatCode, Headers} -> -- wait_for_async_resp(Req_id, StatCode, Headers, Body); -- {ibrowse_async_response_end, Req_id} -> -- Body_1 = list_to_binary(lists:reverse(Body)), -- {ok, Acc_Stat_code, Acc_Headers, Body_1}; -- {ibrowse_async_response, Req_id, Data} -> -- wait_for_async_resp(Req_id, Acc_Stat_code, Acc_Headers, [Data | Body]); -- Err -> -- {ok, Acc_Stat_code, Acc_Headers, Err} -- end. -- --execute_req(Url, Method, Options) -> -- io:format("~7.7w, ~50.50s: ", [Method, Url]), -- Result = (catch ibrowse:send_req(Url, [], Method, [], Options)), -- case Result of -- {ok, SCode, _H, _B} -> -- io:format("Status code: ~p~n", [SCode]); -- Err -> -- io:format("Err -> ~p~n", [Err]) -- end. -- --drv_ue_test() -> -- drv_ue_test(lists:duplicate(1024, 127)). --drv_ue_test(Data) -> -- [{port, Port}| _] = ets:lookup(ibrowse_table, port), --% erl_ddll:unload_driver("ibrowse_drv"), --% timer:sleep(1000), --% erl_ddll:load_driver("../priv", "ibrowse_drv"), --% Port = open_port({spawn, "ibrowse_drv"}, []), -- {Time, Res} = timer:tc(ibrowse_lib, drv_ue, [Data, Port]), -- io:format("Time -> ~p~n", [Time]), -- io:format("Data Length -> ~p~n", [length(Data)]), -- io:format("Res Length -> ~p~n", [length(Res)]). --% io:format("Result -> ~s~n", [Res]). -- --ue_test() -> -- ue_test(lists:duplicate(1024, $?)). --ue_test(Data) -> -- {Time, Res} = timer:tc(ibrowse_lib, url_encode, [Data]), -- io:format("Time -> ~p~n", [Time]), -- io:format("Data Length -> ~p~n", [length(Data)]), -- io:format("Res Length -> ~p~n", [length(Res)]). --% io:format("Result -> ~s~n", [Res]). -- --log_msg(Fmt, Args) -> -- io:format("~s -- " ++ Fmt, -- [ibrowse_lib:printable_date() | Args]). -diff --git a/test/etap/test_util.erl.in b/test/etap/test_util.erl.in -index 948958c..2650fbb 100644 ---- a/test/etap/test_util.erl.in -+++ b/test/etap/test_util.erl.in -@@ -22,7 +22,7 @@ builddir() -> - "@abs_top_builddir@". - - init_code_path() -> -- Paths = ["couchdb", "ibrowse"], -+ Paths = ["couchdb"], - lists:foreach(fun(Name) -> - code:add_pathz(filename:join([builddir(), "src", Name])) - end, Paths). --- -1.7.1.1 - diff --git a/couchdb0.11.1-0007-Workaround-for-system-wide-ibrowse.patch b/couchdb0.11.1-0007-Workaround-for-system-wide-ibrowse.patch deleted file mode 100644 index c09b889..0000000 --- a/couchdb0.11.1-0007-Workaround-for-system-wide-ibrowse.patch +++ /dev/null @@ -1,68 +0,0 @@ -From a23fd694426eb38865dd262d775cabb905c85a2f Mon Sep 17 00:00:00 2001 -From: Peter Lemenkov -Date: Tue, 8 Jun 2010 17:30:49 +0400 -Subject: [PATCH 7/9] Workaround for system-wide ibrowse - ---- - src/couchdb/couch_rep_changes_feed.erl | 2 +- - src/couchdb/couch_rep_httpc.erl | 6 +++--- - src/couchdb/couch_rep_reader.erl | 2 +- - 3 files changed, 5 insertions(+), 5 deletions(-) - -diff --git a/src/couchdb/couch_rep_changes_feed.erl b/src/couchdb/couch_rep_changes_feed.erl -index 343b445..fc46925 100644 ---- a/src/couchdb/couch_rep_changes_feed.erl -+++ b/src/couchdb/couch_rep_changes_feed.erl -@@ -20,7 +20,7 @@ - -define(BUFFER_SIZE, 1000). - - -include("couch_db.hrl"). ---include("../ibrowse/ibrowse.hrl"). -+-include_lib("ibrowse/include/ibrowse.hrl"). - - -record (state, { - changes_from = nil, -diff --git a/src/couchdb/couch_rep_httpc.erl b/src/couchdb/couch_rep_httpc.erl -index 4944f55..6608238 100644 ---- a/src/couchdb/couch_rep_httpc.erl -+++ b/src/couchdb/couch_rep_httpc.erl -@@ -12,7 +12,7 @@ - - -module(couch_rep_httpc). - -include("couch_db.hrl"). ---include("../ibrowse/ibrowse.hrl"). -+-include_lib("ibrowse/include/ibrowse.hrl"). - - -export([db_exists/1, db_exists/2, full_url/1, request/1, redirected_request/2, - spawn_worker_process/1, spawn_link_worker_process/1]). -@@ -188,12 +188,12 @@ redirected_request(Req, RedirectUrl) -> - - spawn_worker_process(Req) -> - Url = ibrowse_lib:parse_url(Req#http_db.url), -- {ok, Pid} = ibrowse_http_client:start(Url), -+ {ok, Pid} = ibrowse_http_client:start({undefined, Url, {[{ssl_imp, new}], (Url#url.protocol == https)}}), - Pid. - - spawn_link_worker_process(Req) -> - Url = ibrowse_lib:parse_url(Req#http_db.url), -- {ok, Pid} = ibrowse_http_client:start_link(Url), -+ {ok, Pid} = ibrowse_http_client:start_link({undefined, Url, {[{ssl_imp, new}], (Url#url.protocol == https)}}), - Pid. - - maybe_decompress(Headers, Body) -> -diff --git a/src/couchdb/couch_rep_reader.erl b/src/couchdb/couch_rep_reader.erl -index 949739b..3225f56 100644 ---- a/src/couchdb/couch_rep_reader.erl -+++ b/src/couchdb/couch_rep_reader.erl -@@ -25,7 +25,7 @@ - -define (MAX_PIPELINE_SIZE, 50). - - -include("couch_db.hrl"). ---include("../ibrowse/ibrowse.hrl"). -+-include_lib("ibrowse/include/ibrowse.hrl"). - - -record (state, { - parent, --- -1.7.1.1 - diff --git a/couchdb0.11.1-0008-Remove-pid-file-after-stop.patch b/couchdb0.11.1-0008-Remove-pid-file-after-stop.patch deleted file mode 100644 index 7151e6a..0000000 --- a/couchdb0.11.1-0008-Remove-pid-file-after-stop.patch +++ /dev/null @@ -1,28 +0,0 @@ -From f7ce263dbc08c1127abe44c816a475f1238beae4 Mon Sep 17 00:00:00 2001 -From: Peter Lemenkov -Date: Mon, 7 Jun 2010 15:08:42 +0400 -Subject: [PATCH 8/9] Remove pid-file after stop - ---- - bin/couchdb.tpl.in | 4 +--- - 1 files changed, 1 insertions(+), 3 deletions(-) - -diff --git a/bin/couchdb.tpl.in b/bin/couchdb.tpl.in -index ced524c..4e21c3f 100644 ---- a/bin/couchdb.tpl.in -+++ b/bin/couchdb.tpl.in -@@ -253,10 +253,8 @@ EOF - - stop_couchdb () { - PID=`_get_pid` -+ rm -f $PID_FILE - if test -n "$PID"; then -- if test "$1" = "false"; then -- echo > $PID_FILE -- fi - if kill -0 $PID 2> /dev/null; then - if kill -1 $PID 2> /dev/null; then - if test "$1" = "false"; then --- -1.7.1.1 - diff --git a/couchdb0.11.1-0009-deleting-a-DB-while-it-was-being-opened-would-crash-.patch b/couchdb0.11.1-0009-deleting-a-DB-while-it-was-being-opened-would-crash-.patch deleted file mode 100644 index 4e26ea8..0000000 --- a/couchdb0.11.1-0009-deleting-a-DB-while-it-was-being-opened-would-crash-.patch +++ /dev/null @@ -1,28 +0,0 @@ -From b063843a8e0bb4eef4674e3d9396dc76323a4ddc Mon Sep 17 00:00:00 2001 -From: Adam Kocoloski -Date: Sun, 11 Jul 2010 01:00:50 +0000 -Subject: [PATCH 9/9] deleting a DB while it was being opened would crash couch_server - -(Updated for 0.11.1) - -git-svn-id: https://svn.apache.org/repos/asf/couchdb/trunk@962964 13f79535-47bb-0310-9956-ffa450edef68 ---- - src/couchdb/couch_server.erl | 2 +- - 1 files changed, 1 insertions(+), 1 deletions(-) - -diff --git a/src/couchdb/couch_server.erl b/src/couchdb/couch_server.erl -index d339405..2bf115f 100644 ---- a/src/couchdb/couch_server.erl -+++ b/src/couchdb/couch_server.erl -@@ -308,7 +308,7 @@ handle_call({delete, DbName, _Options}, _From, Server) -> - couch_util:shutdown_sync(Pid), - true = ets:delete(couch_dbs_by_name, DbName), - true = ets:delete(couch_dbs_by_pid, Pid), -- [gen_server:send_result(F, not_found) || F <- Froms], -+ [gen_server:reply(F, not_found) || F <- Froms], - Server#server{dbs_open=Server#server.dbs_open - 1}; - [{_, {opened, Pid, LruTime}}] -> - couch_util:shutdown_sync(Pid), --- -1.7.1.1 - diff --git a/sources b/sources index 1ac4666..83691b4 100644 --- a/sources +++ b/sources @@ -1 +1 @@ -5100ef96f332ab4449ba891a98abde87 apache-couchdb-0.11.1.tar.gz +71e89c4b21c62417f2f413d74a38f079 apache-couchdb-1.0.0.tar.gz