You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@couchdb.apache.org by dc...@apache.org on 2013/04/25 00:18:32 UTC

[1/8] COUCHDB-1696 import mochiweb from tag v2.4.2

Updated Branches:
  refs/heads/master ae6f1ebd8 -> d751a3914


http://git-wip-us.apache.org/repos/asf/couchdb/blob/cbb8a550/src/mochiweb/mochiweb_response.erl
----------------------------------------------------------------------
diff --git a/src/mochiweb/mochiweb_response.erl b/src/mochiweb/mochiweb_response.erl
index ab8ee61..c2a94d9 100644
--- a/src/mochiweb/mochiweb_response.erl
+++ b/src/mochiweb/mochiweb_response.erl
@@ -3,39 +3,47 @@
 
 %% @doc Response abstraction.
 
--module(mochiweb_response, [Request, Code, Headers]).
+-module(mochiweb_response).
 -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]).
+-export([new/3, get_header_value/2, get/2, dump/1]).
+-export([send/2, write_chunk/2]).
 
-%% @spec get_header_value(string() | atom() | binary()) -> string() | undefined
+%% @type response() = {atom(), [Request, Code, Headers]}
+
+%% @spec new(Request, Code, Headers) -> response()
+%% @doc Create a new mochiweb_response instance.
+new(Request, Code, Headers) ->
+    {?MODULE, [Request, Code, Headers]}.
+
+%% @spec get_header_value(string() | atom() | binary(), response()) ->
+%%           string() | undefined
 %% @doc Get the value of the given response header.
-get_header_value(K) ->
+get_header_value(K, {?MODULE, [_Request, _Code, Headers]}) ->
     mochiweb_headers:get_value(K, Headers).
 
-%% @spec get(request | code | headers) -> term()
+%% @spec get(request | code | headers, response()) -> term()
 %% @doc Return the internal representation of the given field.
-get(request) ->
+get(request, {?MODULE, [Request, _Code, _Headers]}) ->
     Request;
-get(code) ->
+get(code, {?MODULE, [_Request, Code, _Headers]}) ->
     Code;
-get(headers) ->
+get(headers, {?MODULE, [_Request, _Code, Headers]}) ->
     Headers.
 
-%% @spec dump() -> {mochiweb_request, [{atom(), term()}]}
+%% @spec dump(response()) -> {mochiweb_request, [{atom(), term()}]}
 %% @doc Dump the internal representation to a "human readable" set of terms
 %%      for debugging/inspection purposes.
-dump() ->
+dump({?MODULE, [Request, Code, Headers]}) ->
     [{request, Request:dump()},
      {code, Code},
      {headers, mochiweb_headers:to_list(Headers)}].
 
-%% @spec send(iodata()) -> ok
+%% @spec send(iodata(), response()) -> ok
 %% @doc Send data over the socket if the method is not HEAD.
-send(Data) ->
+send(Data, {?MODULE, [Request, _Code, _Headers]}) ->
     case Request:get(method) of
         'HEAD' ->
             ok;
@@ -43,22 +51,22 @@ send(Data) ->
             Request:send(Data)
     end.
 
-%% @spec write_chunk(iodata()) -> ok
+%% @spec write_chunk(iodata(), response()) -> 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) ->
+write_chunk(Data, {?MODULE, [Request, _Code, _Headers]}=THIS) ->
     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([io_lib:format("~.16b\r\n", [Length]), Data, <<"\r\n">>], THIS);
         _ ->
-            send(Data)
+            send(Data, THIS)
     end.
 
 
 %%
 %% Tests
 %%
--include_lib("eunit/include/eunit.hrl").
 -ifdef(TEST).
+-include_lib("eunit/include/eunit.hrl").
 -endif.

http://git-wip-us.apache.org/repos/asf/couchdb/blob/cbb8a550/src/mochiweb/mochiweb_socket.erl
----------------------------------------------------------------------
diff --git a/src/mochiweb/mochiweb_socket.erl b/src/mochiweb/mochiweb_socket.erl
index ad27204..76b018c 100644
--- a/src/mochiweb/mochiweb_socket.erl
+++ b/src/mochiweb/mochiweb_socket.erl
@@ -4,11 +4,10 @@
 
 -module(mochiweb_socket).
 
--export([listen/4, accept/1, after_accept/1, recv/3, send/2, close/1, port/1, peername/1,
+-export([listen/4, accept/1, recv/3, send/2, close/1, port/1, peername/1,
          setopts/2, type/1]).
 
 -define(ACCEPT_TIMEOUT, 2000).
--define(SSL_ACCEPT_TIMEOUT, 30000).
 
 listen(Ssl, Port, Opts, SslOpts) ->
     case Ssl of
@@ -26,9 +25,14 @@ listen(Ssl, Port, Opts, SslOpts) ->
 accept({ssl, ListenSocket}) ->
     % There's a bug in ssl:transport_accept/2 at the moment, which is the
     % reason for the try...catch block. Should be fixed in OTP R14.
-    try ssl:transport_accept(ListenSocket, ?ACCEPT_TIMEOUT) of
+    try ssl:transport_accept(ListenSocket) of
         {ok, Socket} ->
-            {ok, {ssl, Socket}};
+            case ssl:ssl_accept(Socket) of
+                ok ->
+                    {ok, {ssl, Socket}};
+                {error, _} = Err ->
+                    Err
+            end;
         {error, _} = Err ->
             Err
     catch
@@ -38,9 +42,6 @@ accept({ssl, ListenSocket}) ->
 accept(ListenSocket) ->
     gen_tcp:accept(ListenSocket, ?ACCEPT_TIMEOUT).
 
-after_accept({ssl, Socket}) -> ssl:ssl_accept(Socket, ?SSL_ACCEPT_TIMEOUT);
-after_accept(_Socket) -> ok.
-
 recv({ssl, Socket}, Length, Timeout) ->
     ssl:recv(Socket, Length, Timeout);
 recv(Socket, Length, Timeout) ->

http://git-wip-us.apache.org/repos/asf/couchdb/blob/cbb8a550/src/mochiweb/mochiweb_socket_server.erl
----------------------------------------------------------------------
diff --git a/src/mochiweb/mochiweb_socket_server.erl b/src/mochiweb/mochiweb_socket_server.erl
index ff0d8f3..029f195 100644
--- a/src/mochiweb/mochiweb_socket_server.erl
+++ b/src/mochiweb/mochiweb_socket_server.erl
@@ -9,7 +9,7 @@
 
 -include("internal.hrl").
 
--export([start/1, stop/1]).
+-export([start/1, start_link/1, stop/1]).
 -export([init/1, handle_call/3, handle_cast/2, terminate/2, code_change/3,
          handle_info/2]).
 -export([get/2, set/3]).
@@ -33,10 +33,22 @@
 
 -define(is_old_state(State), not is_record(State, mochiweb_socket_server)).
 
-start(State=#mochiweb_socket_server{}) ->
-    start_server(State);
+start_link(Options) ->
+    start_server(start_link, parse_options(Options)).
+
 start(Options) ->
-    start(parse_options(Options)).
+    case lists:keytake(link, 1, Options) of
+        {value, {_Key, false}, Options1} ->
+            start_server(start, parse_options(Options1));
+        _ ->
+            %% TODO: https://github.com/mochi/mochiweb/issues/58
+            %%   [X] Phase 1: Add new APIs (Sep 2011)
+            %%   [_] Phase 2: Add deprecation warning
+            %%   [_] Phase 3: Change default to {link, false} and ignore link
+            %%   [_] Phase 4: Add deprecation warning for {link, _} option
+            %%   [_] Phase 5: Remove support for {link, _} option
+            start_link(Options)
+    end.
 
 get(Name, Property) ->
     gen_server:call(Name, {get, Property}).
@@ -61,6 +73,8 @@ stop(Options) ->
 
 %% Internal API
 
+parse_options(State=#mochiweb_socket_server{}) ->
+    State;
 parse_options(Options) ->
     parse_options(Options, #mochiweb_socket_server{}).
 
@@ -116,22 +130,22 @@ parse_options([{profile_fun, ProfileFun} | Rest], State) when is_function(Profil
     parse_options(Rest, State#mochiweb_socket_server{profile_fun=ProfileFun}).
 
 
-start_server(State=#mochiweb_socket_server{ssl=Ssl, name=Name}) ->
-    case Ssl of
-        true ->
-            application:start(crypto),
-            application:start(public_key),
-            application:start(ssl);
-        false ->
-            void
-    end,
+start_server(F, State=#mochiweb_socket_server{ssl=Ssl, name=Name}) ->
+    ok = prep_ssl(Ssl),
     case Name of
         undefined ->
-            gen_server:start_link(?MODULE, State, []);
+            gen_server:F(?MODULE, State, []);
         _ ->
-            gen_server:start_link(Name, ?MODULE, State, [])
+            gen_server:F(Name, ?MODULE, State, [])
     end.
 
+prep_ssl(true) ->
+    ok = mochiweb:ensure_started(crypto),
+    ok = mochiweb:ensure_started(public_key),
+    ok = mochiweb:ensure_started(ssl);
+prep_ssl(false) ->
+    ok.
+
 ensure_int(N) when is_integer(N) ->
     N;
 ensure_int(S) when is_list(S) ->
@@ -165,27 +179,7 @@ init(State=#mochiweb_socket_server{ip=Ip, port=Port, backlog=Backlog, nodelay=No
         {_, _, _, _, _, _, _, _} -> % IPv6
             [inet6, {ip, Ip} | BaseOpts]
     end,
-    case listen(Port, Opts, State) of
-        {stop, eacces} ->
-            case Port < 1024 of
-                true ->
-                    case catch fdsrv:start() of
-                        {ok, _} ->
-                            case fdsrv:bind_socket(tcp, Port) of
-                                {ok, Fd} ->
-                                    listen(Port, [{fd, Fd} | Opts], State);
-                                _ ->
-                                    {stop, fdsrv_bind_failed}
-                            end;
-                        _ ->
-                            {stop, fdsrv_start_failed}
-                    end;
-                false ->
-                    {stop, eacces}
-            end;
-        Other ->
-            Other
-    end.
+    listen(Port, Opts, State).
 
 new_acceptor_pool(Listen,
                   State=#mochiweb_socket_server{acceptor_pool=Pool,
@@ -271,15 +265,8 @@ handle_cast(stop, State) ->
 
 terminate(Reason, State) when ?is_old_state(State) ->
     terminate(Reason, upgrade_state(State));
-terminate(_Reason, #mochiweb_socket_server{listen=Listen, port=Port}) ->
-    mochiweb_socket:close(Listen),
-    case Port < 1024 of
-        true ->
-            catch fdsrv:stop(),
-            ok;
-        false ->
-            ok
-    end.
+terminate(_Reason, #mochiweb_socket_server{listen=Listen}) ->
+    mochiweb_socket:close(Listen).
 
 code_change(_OldVsn, State, _Extra) ->
     State.
@@ -337,8 +324,8 @@ handle_info(Info, State) ->
 %%
 %% Tests
 %%
--include_lib("eunit/include/eunit.hrl").
 -ifdef(TEST).
+-include_lib("eunit/include/eunit.hrl").
 
 upgrade_state_test() ->
     OldState = {mochiweb_socket_server,
@@ -361,4 +348,3 @@ upgrade_state_test() ->
     ?assertEqual(CmpState, State).
 
 -endif.
-

http://git-wip-us.apache.org/repos/asf/couchdb/blob/cbb8a550/src/mochiweb/mochiweb_util.erl
----------------------------------------------------------------------
diff --git a/src/mochiweb/mochiweb_util.erl b/src/mochiweb/mochiweb_util.erl
index 6b88818..4d39990 100644
--- a/src/mochiweb/mochiweb_util.erl
+++ b/src/mochiweb/mochiweb_util.erl
@@ -9,7 +9,7 @@
 -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, cmd_status/1]).
+-export([shell_quote/1, cmd/1, cmd_string/1, cmd_port/2, cmd_status/1, cmd_status/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]).
@@ -124,11 +124,17 @@ cmd_string(Argv) ->
     string:join([shell_quote(X) || X <- Argv], " ").
 
 %% @spec cmd_status([string()]) -> {ExitStatus::integer(), Stdout::binary()}
-%% @doc Accumulate the output and exit status from the given application, will be
-%%      spawned with cmd_port/2.
+%% @doc Accumulate the output and exit status from the given application,
+%%      will be spawned with cmd_port/2.
 cmd_status(Argv) ->
+    cmd_status(Argv, []).
+
+%% @spec cmd_status([string()], [atom()]) -> {ExitStatus::integer(), Stdout::binary()}
+%% @doc Accumulate the output and exit status from the given application,
+%%      will be spawned with cmd_port/2.
+cmd_status(Argv, Options) ->
     Port = cmd_port(Argv, [exit_status, stderr_to_stdout,
-                           use_stdio, binary]),
+                           use_stdio, binary | Options]),
     try cmd_loop(Port, [])
     after catch port_close(Port)
     end.
@@ -578,8 +584,8 @@ make_io(Io) when is_list(Io); is_binary(Io) ->
 %%
 %% Tests
 %%
--include_lib("eunit/include/eunit.hrl").
 -ifdef(TEST).
+-include_lib("eunit/include/eunit.hrl").
 
 make_io_test() ->
     ?assertEqual(

http://git-wip-us.apache.org/repos/asf/couchdb/blob/cbb8a550/src/mochiweb/reloader.erl
----------------------------------------------------------------------
diff --git a/src/mochiweb/reloader.erl b/src/mochiweb/reloader.erl
index c0f5de8..8266b33 100644
--- a/src/mochiweb/reloader.erl
+++ b/src/mochiweb/reloader.erl
@@ -59,7 +59,7 @@ handle_cast(_Req, State) ->
 %% @doc gen_server callback.
 handle_info(doit, State) ->
     Now = stamp(),
-    doit(State#state.last, Now),
+    _ = doit(State#state.last, Now),
     {noreply, State#state{last = Now}};
 handle_info(_Info, State) ->
     {noreply, State}.
@@ -156,6 +156,6 @@ stamp() ->
 %%
 %% Tests
 %%
--include_lib("eunit/include/eunit.hrl").
 -ifdef(TEST).
+-include_lib("eunit/include/eunit.hrl").
 -endif.


[8/8] git commit: updated refs/heads/master to d751a39

Posted by dc...@apache.org.
COUCHDB-1696 final commit and update docs


Project: http://git-wip-us.apache.org/repos/asf/couchdb/repo
Commit: http://git-wip-us.apache.org/repos/asf/couchdb/commit/d751a391
Tree: http://git-wip-us.apache.org/repos/asf/couchdb/tree/d751a391
Diff: http://git-wip-us.apache.org/repos/asf/couchdb/diff/d751a391

Branch: refs/heads/master
Commit: d751a3914ed8eafb7fab209187bca4d22e392ed5
Parents: f3e6391
Author: Dave Cottlehuber <dc...@apache.org>
Authored: Wed Apr 24 23:44:41 2013 +0200
Committer: Dave Cottlehuber <dc...@apache.org>
Committed: Thu Apr 25 00:14:41 2013 +0200

----------------------------------------------------------------------
 CHANGES         |    6 ++++++
 INSTALL.Unix    |    2 +-
 INSTALL.Windows |    2 +-
 NEWS            |    4 ++++
 configure.ac    |    8 ++------
 5 files changed, 14 insertions(+), 8 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/couchdb/blob/d751a391/CHANGES
----------------------------------------------------------------------
diff --git a/CHANGES b/CHANGES
index 00fceb0..5d5e52b 100644
--- a/CHANGES
+++ b/CHANGES
@@ -13,6 +13,12 @@ Apache CouchDB CHANGES
 # Futon:
 #
 #  * Disabled the link to the Futon test suite.
+#
+# Dependencies:
+#
+#  * Support Erlang/OTP R16.
+#  * Bump Mochiweb dependency to v2.4.2.
+#  * Minimum Erlang/OTP version is now R14B.
 
 # Version 1.3.1
 # -------------

http://git-wip-us.apache.org/repos/asf/couchdb/blob/d751a391/INSTALL.Unix
----------------------------------------------------------------------
diff --git a/INSTALL.Unix b/INSTALL.Unix
index f517f08..854fd13 100644
--- a/INSTALL.Unix
+++ b/INSTALL.Unix
@@ -35,7 +35,7 @@ Dependencies
 
 You should have the following installed:
 
- * Erlang OTP (>=R13B04, <R16)  (http://erlang.org/)
+ * Erlang OTP (>=R13B04, <R17)  (http://erlang.org/)
  * ICU                          (http://icu-project.org/)
  * OpenSSL                      (http://www.openssl.org/)
  * Mozilla SpiderMonkey (1.7)   (http://www.mozilla.org/js/spidermonkey/)

http://git-wip-us.apache.org/repos/asf/couchdb/blob/d751a391/INSTALL.Windows
----------------------------------------------------------------------
diff --git a/INSTALL.Windows b/INSTALL.Windows
index 353654b..04a413f 100644
--- a/INSTALL.Windows
+++ b/INSTALL.Windows
@@ -29,7 +29,7 @@ Dependencies
 
 You will need the following installed:
 
- * Erlang OTP (>=14B01, <R16)    (http://erlang.org/)
+ * Erlang OTP (>=14B01, <R17)    (http://erlang.org/)
  * ICU        (>=4.*)            (http://icu-project.org/)
  * OpenSSL    (>=0.9.8r)         (http://www.openssl.org/)
  * Mozilla SpiderMonkey (=1.8.5) (http://www.mozilla.org/js/spidermonkey/)

http://git-wip-us.apache.org/repos/asf/couchdb/blob/d751a391/NEWS
----------------------------------------------------------------------
diff --git a/NEWS b/NEWS
index 7d7e7b3..b5a006d 100644
--- a/NEWS
+++ b/NEWS
@@ -11,6 +11,10 @@ Each release section notes when backwards incompatible changes have been made.
 # -------------
 #
 # This version has not been released yet.
+#
+#  * Support Erlang/OTP R16.
+#  * Bump Mochiweb dependency to v2.4.2.
+#  * Minimum Erlang/OTP version is now R14B.
 
 # Version 1.3.1
 # -------------

http://git-wip-us.apache.org/repos/asf/couchdb/blob/d751a391/configure.ac
----------------------------------------------------------------------
diff --git a/configure.ac b/configure.ac
index 53e23f1..0a9a9e3 100644
--- a/configure.ac
+++ b/configure.ac
@@ -409,7 +409,7 @@ esac
 
 { $as_echo "$as_me:${as_lineno-$LINENO}: checking Erlang version compatibility" >&5
 $as_echo_n "checking Erlang version compatibility... " >&6; }
-erlang_version_error="The installed Erlang version must be >= R13B04 (erts-5.7.4) and < R16A (erts-5.10)"
+erlang_version_error="The installed Erlang version must be >= R14B (erts-5.8.1) and <R17 (erts-5.11)"
 
 version="`${ERL} -version 2>&1 | ${SED} 's/[[^0-9]]/ /g'` 0 0 0"
 major_version=`echo $version | ${AWK} "{print \\$1}"`
@@ -420,11 +420,7 @@ if test $major_version -ne 5; then
     as_fn_error $? "$erlang_version_error" "$LINENO" 5
 fi
 
-if test $minor_version -lt 7 -o $minor_version -gt 9; then
-    as_fn_error $? "$erlang_version_error" "$LINENO" 5
-fi
-
-if test $minor_version -eq 7 -a $patch_version -lt 4; then
+if test $minor_version -lt 8 -o $minor_version -gt 10; then
     as_fn_error $? "$erlang_version_error" "$LINENO" 5
 fi
 


[7/8] git commit: updated refs/heads/master to d751a39

Posted by dc...@apache.org.
COUCHDB-1696 sort ETS table results in test suite to support R16B


Project: http://git-wip-us.apache.org/repos/asf/couchdb/repo
Commit: http://git-wip-us.apache.org/repos/asf/couchdb/commit/f3e6391d
Tree: http://git-wip-us.apache.org/repos/asf/couchdb/tree/f3e6391d
Diff: http://git-wip-us.apache.org/repos/asf/couchdb/diff/f3e6391d

Branch: refs/heads/master
Commit: f3e6391d92e135d64c4b2a6f7722ebb5d852b6b3
Parents: cdf8949
Author: Dave Cottlehuber <dc...@apache.org>
Authored: Tue Mar 19 10:12:02 2013 +0100
Committer: Dave Cottlehuber <dc...@apache.org>
Committed: Thu Apr 25 00:14:28 2013 +0200

----------------------------------------------------------------------
 test/etap/120-stats-collect.t |   14 +++++++-------
 1 files changed, 7 insertions(+), 7 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/couchdb/blob/f3e6391d/test/etap/120-stats-collect.t
----------------------------------------------------------------------
diff --git a/test/etap/120-stats-collect.t b/test/etap/120-stats-collect.t
index dee8876..a30f9ac 100755
--- a/test/etap/120-stats-collect.t
+++ b/test/etap/120-stats-collect.t
@@ -84,7 +84,7 @@ test_proc_counting() ->
     etap:is(
         couch_stats_collector:get(hoopla),
         1,
-        "track_process_count incrememnts the counter."
+        "track_process_count increments the counter."
     ),
     
     TwicePid = spawn(fun() ->
@@ -124,21 +124,21 @@ test_all() ->
     couch_stats_collector:record(bar, 0.0),
     couch_stats_collector:record(bar, 1.0),
     etap:is(
-        couch_stats_collector:all(),
-        [{foo, 0}, {hoopla, 0}, {bar, [1.0, 0.0]}],
+        lists:sort(couch_stats_collector:all()),
+        [ {bar,[1.0,0.0]}, {foo,0}, { hoopla,0} ],
         "all/0 returns all counters and absolute values."
     ),
     
     etap:is(
-        couch_stats_collector:all(incremental),
-        [{foo, 0}, {hoopla, 0}],
+        lists:sort(couch_stats_collector:all(incremental)),
+        [ {foo, 0}, {hoopla, 0} ],
         "all/1 returns only the specified type."
     ),
     
     couch_stats_collector:record(zing, 90),
     etap:is(
-        couch_stats_collector:all(absolute),
-        [{zing, [90]}, {bar, [1.0, 0.0]}],
+        lists:sort(couch_stats_collector:all(absolute)),
+        [ {bar,[1.0,0.0]}, {zing,"Z"} ],
         "all/1 returns only the specified type."
     ),
     ok.


[5/8] git commit: updated refs/heads/master to d751a39

Posted by dc...@apache.org.
COUCHDB-1696 fix Mochiweb acceptor blocked in ssl handshake

Acceptor cannot be recycled until ssl handshake is done,
so it's possible and easy for all acceptors be blocked between the point
where the new socket's connected and the ssl handshake is done.


Project: http://git-wip-us.apache.org/repos/asf/couchdb/repo
Commit: http://git-wip-us.apache.org/repos/asf/couchdb/commit/b67b03ef
Tree: http://git-wip-us.apache.org/repos/asf/couchdb/tree/b67b03ef
Diff: http://git-wip-us.apache.org/repos/asf/couchdb/diff/b67b03ef

Branch: refs/heads/master
Commit: b67b03efc90f080e44fe3a3331e42f2de3e64070
Parents: cbb8a55
Author: Wei Cao <cy...@gmail.com>
Authored: Thu Aug 16 10:53:07 2012 +0800
Committer: Dave Cottlehuber <dc...@apache.org>
Committed: Thu Apr 25 00:13:44 2013 +0200

----------------------------------------------------------------------
 src/mochiweb/mochiweb_acceptor.erl |    7 ++++---
 src/mochiweb/mochiweb_socket.erl   |   15 +++++++--------
 2 files changed, 11 insertions(+), 11 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/couchdb/blob/b67b03ef/src/mochiweb/mochiweb_acceptor.erl
----------------------------------------------------------------------
diff --git a/src/mochiweb/mochiweb_acceptor.erl b/src/mochiweb/mochiweb_acceptor.erl
index efedfbd..c1192e2 100644
--- a/src/mochiweb/mochiweb_acceptor.erl
+++ b/src/mochiweb/mochiweb_acceptor.erl
@@ -18,13 +18,14 @@ init(Server, Listen, Loop) ->
     case catch mochiweb_socket:accept(Listen) of
         {ok, Socket} ->
             gen_server:cast(Server, {accepted, self(), timer:now_diff(now(), T1)}),
-            call_loop(Loop, Socket);
+            case mochiweb_socket:after_accept(Socket) of
+                ok -> call_loop(Loop, Socket);
+                {error, _} -> exit(normal)
+            end;
         {error, closed} ->
             exit(normal);
         {error, timeout} ->
             init(Server, Listen, Loop);
-        {error, esslaccept} ->
-            exit(normal);
         Other ->
             error_logger:error_report(
               [{application, mochiweb},

http://git-wip-us.apache.org/repos/asf/couchdb/blob/b67b03ef/src/mochiweb/mochiweb_socket.erl
----------------------------------------------------------------------
diff --git a/src/mochiweb/mochiweb_socket.erl b/src/mochiweb/mochiweb_socket.erl
index 76b018c..ad27204 100644
--- a/src/mochiweb/mochiweb_socket.erl
+++ b/src/mochiweb/mochiweb_socket.erl
@@ -4,10 +4,11 @@
 
 -module(mochiweb_socket).
 
--export([listen/4, accept/1, recv/3, send/2, close/1, port/1, peername/1,
+-export([listen/4, accept/1, after_accept/1, recv/3, send/2, close/1, port/1, peername/1,
          setopts/2, type/1]).
 
 -define(ACCEPT_TIMEOUT, 2000).
+-define(SSL_ACCEPT_TIMEOUT, 30000).
 
 listen(Ssl, Port, Opts, SslOpts) ->
     case Ssl of
@@ -25,14 +26,9 @@ listen(Ssl, Port, Opts, SslOpts) ->
 accept({ssl, ListenSocket}) ->
     % There's a bug in ssl:transport_accept/2 at the moment, which is the
     % reason for the try...catch block. Should be fixed in OTP R14.
-    try ssl:transport_accept(ListenSocket) of
+    try ssl:transport_accept(ListenSocket, ?ACCEPT_TIMEOUT) of
         {ok, Socket} ->
-            case ssl:ssl_accept(Socket) of
-                ok ->
-                    {ok, {ssl, Socket}};
-                {error, _} = Err ->
-                    Err
-            end;
+            {ok, {ssl, Socket}};
         {error, _} = Err ->
             Err
     catch
@@ -42,6 +38,9 @@ accept({ssl, ListenSocket}) ->
 accept(ListenSocket) ->
     gen_tcp:accept(ListenSocket, ?ACCEPT_TIMEOUT).
 
+after_accept({ssl, Socket}) -> ssl:ssl_accept(Socket, ?SSL_ACCEPT_TIMEOUT);
+after_accept(_Socket) -> ok.
+
 recv({ssl, Socket}, Length, Timeout) ->
     ssl:recv(Socket, Length, Timeout);
 recv(Socket, Length, Timeout) ->


[2/8] COUCHDB-1696 import mochiweb from tag v2.4.2

Posted by dc...@apache.org.
http://git-wip-us.apache.org/repos/asf/couchdb/blob/cbb8a550/src/mochiweb/mochiweb_html.erl
----------------------------------------------------------------------
diff --git a/src/mochiweb/mochiweb_html.erl b/src/mochiweb/mochiweb_html.erl
index 0f281db..965c846 100644
--- a/src/mochiweb/mochiweb_html.erl
+++ b/src/mochiweb/mochiweb_html.erl
@@ -95,7 +95,12 @@ 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)}]).
+    case is_singleton(Tag) of 
+        true ->
+            to_tokens([], [{start_tag, Tag, Attrs, true}]);
+        false ->
+            to_tokens([{Tag, Acc}], [{start_tag, Tag, Attrs, false}])
+    end.
 
 %% @spec to_html([html_token()] | html_node()) -> iolist()
 %% @doc Convert a list of html_token() to a HTML document.
@@ -312,7 +317,8 @@ tokenize(B, S=#decoder{offset=O}) ->
             {Tag, S1} = tokenize_literal(B, ?ADV_COL(S, 2)),
             {S2, _} = find_gt(B, S1),
             {{end_tag, Tag}, S2};
-        <<_:O/binary, "<", C, _/binary>> when ?IS_WHITESPACE(C) ->
+        <<_:O/binary, "<", C, _/binary>> 
+                when ?IS_WHITESPACE(C); not ?IS_LITERAL_SAFE(C) ->
             %% This isn't really strict HTML
             {{data, Data, _Whitespace}, S1} = tokenize_data(B, ?INC_COL(S)),
             {{data, <<$<, Data/binary>>, false}, S1};
@@ -480,7 +486,7 @@ tokenize_attr_value(Attr, B, S) ->
         _ ->
             {Attr, S1}
     end.
-    
+
 tokenize_quoted_or_unquoted_attr_value(B, S=#decoder{offset=O}) ->
     case B of
         <<_:O/binary>> ->
@@ -491,7 +497,7 @@ tokenize_quoted_or_unquoted_attr_value(B, S=#decoder{offset=O}) ->
         <<_:O/binary, _/binary>> ->
             tokenize_unquoted_attr_value(B, S, [])
     end.
-    
+
 tokenize_quoted_attr_value(B, S=#decoder{offset=O}, Acc, Q) ->
     case B of
         <<_:O/binary>> ->
@@ -501,12 +507,10 @@ tokenize_quoted_attr_value(B, S=#decoder{offset=O}, Acc, Q) ->
             tokenize_quoted_attr_value(B, S1, [Data|Acc], Q);
         <<_:O/binary, Q, _/binary>> ->
             { iolist_to_binary(lists:reverse(Acc)), ?INC_COL(S) };
-        <<_:O/binary, $\n, _/binary>> ->
-            { iolist_to_binary(lists:reverse(Acc)), ?INC_LINE(S) };
         <<_:O/binary, C, _/binary>> ->
             tokenize_quoted_attr_value(B, ?INC_COL(S), [C|Acc], Q)
     end.
-    
+
 tokenize_unquoted_attr_value(B, S=#decoder{offset=O}, Acc) ->
     case B of
         <<_:O/binary>> ->
@@ -520,7 +524,7 @@ tokenize_unquoted_attr_value(B, S=#decoder{offset=O}, Acc) ->
             { iolist_to_binary(lists:reverse(Acc)), S };
         <<_:O/binary, C, _/binary>> ->
             tokenize_unquoted_attr_value(B, ?INC_COL(S), [C|Acc])
-    end.   
+    end.
 
 skip_whitespace(B, S=#decoder{offset=O}) ->
     case B of
@@ -603,32 +607,33 @@ find_gt(Bin, S=#decoder{offset=O}, HasSlash) ->
     end.
 
 tokenize_charref(Bin, S=#decoder{offset=O}) ->
-    tokenize_charref(Bin, S, O).
+    try
+        tokenize_charref(Bin, S, O)
+    catch
+        throw:invalid_charref ->
+            {{data, <<"&">>, false}, S}
+    end.
 
 tokenize_charref(Bin, S=#decoder{offset=O}, Start) ->
     case Bin of
         <<_:O/binary>> ->
-            <<_:Start/binary, Raw/binary>> = Bin,
-            {{data, Raw, false}, S};
+            throw(invalid_charref);
         <<_: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};
+            throw(invalid_charref);
         <<_: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 ->
-                           mochiutf8:codepoint_to_bytes(Unichar)
+                           throw(invalid_charref);
+                       Unichar when is_integer(Unichar) ->
+                           mochiutf8:codepoint_to_bytes(Unichar);
+                       Unichars when is_list(Unichars) ->
+                           unicode:characters_to_binary(Unichars)
                    end,
             {{data, Data, false}, ?INC_COL(S)};
         _ ->
@@ -759,8 +764,8 @@ tokenize_textarea(Bin, S=#decoder{offset=O}, Start) ->
 %%
 %% Tests
 %%
--include_lib("eunit/include/eunit.hrl").
 -ifdef(TEST).
+-include_lib("eunit/include/eunit.hrl").
 
 to_html_test() ->
     ?assertEqual(
@@ -1195,43 +1200,51 @@ parse_unquoted_attr_test() ->
             { <<"img">>, [ { <<"src">>, <<"/images/icon.png">> } ], [] }
         ]},
         mochiweb_html:parse(D0)),
-    
+
     D1 = <<"<html><img src=/images/icon.png></img></html>">>,
         ?assertEqual(
             {<<"html">>,[],[
                 { <<"img">>, [ { <<"src">>, <<"/images/icon.png">> } ], [] }
             ]},
             mochiweb_html:parse(D1)),
-    
+
     D2 = <<"<html><img src=/images/icon&gt;.png width=100></img></html>">>,
         ?assertEqual(
             {<<"html">>,[],[
                 { <<"img">>, [ { <<"src">>, <<"/images/icon>.png">> }, { <<"width">>, <<"100">> } ], [] }
             ]},
             mochiweb_html:parse(D2)),
-    ok.        
-    
-parse_quoted_attr_test() ->    
+    ok.
+
+parse_quoted_attr_test() ->
     D0 = <<"<html><img src='/images/icon.png'></html>">>,
     ?assertEqual(
         {<<"html">>,[],[
             { <<"img">>, [ { <<"src">>, <<"/images/icon.png">> } ], [] }
         ]},
-        mochiweb_html:parse(D0)),     
-        
+        mochiweb_html:parse(D0)),
+
     D1 = <<"<html><img src=\"/images/icon.png'></html>">>,
     ?assertEqual(
         {<<"html">>,[],[
             { <<"img">>, [ { <<"src">>, <<"/images/icon.png'></html>">> } ], [] }
         ]},
-        mochiweb_html:parse(D1)),     
+        mochiweb_html:parse(D1)),
 
     D2 = <<"<html><img src=\"/images/icon&gt;.png\"></html>">>,
     ?assertEqual(
         {<<"html">>,[],[
             { <<"img">>, [ { <<"src">>, <<"/images/icon>.png">> } ], [] }
         ]},
-        mochiweb_html:parse(D2)),     
+        mochiweb_html:parse(D2)),
+
+    %% Quoted attributes can contain whitespace and newlines
+    D3 = <<"<html><a href=\"#\" onclick=\"javascript: test(1,\ntrue);\"></html>">>,
+    ?assertEqual(
+        {<<"html">>,[],[
+            { <<"a">>, [ { <<"href">>, <<"#">> }, {<<"onclick">>, <<"javascript: test(1,\ntrue);">>} ], [] }
+        ]},
+        mochiweb_html:parse(D3)),     
     ok.
 
 parse_missing_attr_name_test() ->
@@ -1245,7 +1258,7 @@ parse_broken_pi_test() ->
 	D0 = <<"<html><?xml:namespace prefix = o ns = \"urn:schemas-microsoft-com:office:office\" /></html>">>,
 	?assertEqual(
 		{<<"html">>, [], [
-			{ pi, <<"xml:namespace">>, [ { <<"prefix">>, <<"o">> }, 
+			{ pi, <<"xml:namespace">>, [ { <<"prefix">>, <<"o">> },
 			                             { <<"ns">>, <<"urn:schemas-microsoft-com:office:office">> } ] }
 		] },
 		mochiweb_html:parse(D0)),
@@ -1260,5 +1273,60 @@ parse_funny_singletons_test() ->
 		] },
 		mochiweb_html:parse(D0)),
 	ok.
-    
+
+to_html_singleton_test() ->
+    D0 = <<"<link />">>,
+    T0 = {<<"link">>,[],[]},
+    ?assertEqual(D0, iolist_to_binary(to_html(T0))),
+
+    D1 = <<"<head><link /></head>">>,
+    T1 = {<<"head">>,[],[{<<"link">>,[],[]}]},
+    ?assertEqual(D1, iolist_to_binary(to_html(T1))),
+
+    D2 = <<"<head><link /><link /></head>">>,
+    T2 = {<<"head">>,[],[{<<"link">>,[],[]}, {<<"link">>,[],[]}]},
+    ?assertEqual(D2, iolist_to_binary(to_html(T2))),
+
+    %% Make sure singletons are converted to singletons.
+    D3 = <<"<head><link /></head>">>,
+    T3 = {<<"head">>,[],[{<<"link">>,[],[<<"funny">>]}]},
+    ?assertEqual(D3, iolist_to_binary(to_html(T3))),
+
+    D4 = <<"<link />">>,
+    T4 = {<<"link">>,[],[<<"funny">>]},
+    ?assertEqual(D4, iolist_to_binary(to_html(T4))),
+
+    ok.
+
+parse_amp_test_() ->
+    [?_assertEqual(
+       {<<"html">>,[],
+        [{<<"body">>,[{<<"onload">>,<<"javascript:A('1&2')">>}],[]}]},
+       mochiweb_html:parse("<html><body onload=\"javascript:A('1&2')\"></body></html>")),
+     ?_assertEqual(
+        {<<"html">>,[],
+         [{<<"body">>,[{<<"onload">>,<<"javascript:A('1& 2')">>}],[]}]},
+        mochiweb_html:parse("<html><body onload=\"javascript:A('1& 2')\"></body></html>")),
+     ?_assertEqual(
+        {<<"html">>,[],
+         [{<<"body">>,[],[<<"& ">>]}]},
+        mochiweb_html:parse("<html><body>& </body></html>")),
+     ?_assertEqual(
+        {<<"html">>,[],
+         [{<<"body">>,[],[<<"&">>]}]},
+        mochiweb_html:parse("<html><body>&</body></html>"))].
+
+parse_unescaped_lt_test() ->
+    D1 = <<"<div> < < <a href=\"/\">Back</a></div>">>,
+    ?assertEqual(
+        {<<"div">>, [], [<<" < < ">>, {<<"a">>, [{<<"href">>, <<"/">>}], 
+                                       [<<"Back">>]}]},
+        mochiweb_html:parse(D1)),
+
+    D2 = <<"<div> << <a href=\"/\">Back</a></div>">>,
+    ?assertEqual(
+        {<<"div">>, [], [<<" << ">>, {<<"a">>, [{<<"href">>, <<"/">>}], 
+                                      [<<"Back">>]}]},
+    mochiweb_html:parse(D2)).
+
 -endif.

http://git-wip-us.apache.org/repos/asf/couchdb/blob/cbb8a550/src/mochiweb/mochiweb_http.erl
----------------------------------------------------------------------
diff --git a/src/mochiweb/mochiweb_http.erl b/src/mochiweb/mochiweb_http.erl
index 23a4752..4f7e947 100644
--- a/src/mochiweb/mochiweb_http.erl
+++ b/src/mochiweb/mochiweb_http.erl
@@ -5,13 +5,13 @@
 
 -module(mochiweb_http).
 -author('bob@mochimedia.com').
--export([start/0, start/1, stop/0, stop/1]).
--export([loop/2, default_body/1]).
+-export([start/1, start_link/1, stop/0, stop/1]).
+-export([loop/2]).
 -export([after_response/2, reentry/1]).
 -export([parse_range_request/1, range_skip_length/2]).
 
--define(REQUEST_RECV_TIMEOUT, 300000).   % timeout waiting for request line
--define(HEADERS_RECV_TIMEOUT, 30000). % timeout waiting for headers
+-define(REQUEST_RECV_TIMEOUT, 300000).   %% timeout waiting for request line
+-define(HEADERS_RECV_TIMEOUT, 30000).    %% timeout waiting for headers
 
 -define(MAX_HEADERS, 1000).
 -define(DEFAULTS, [{name, ?MODULE},
@@ -19,9 +19,7 @@
 
 parse_options(Options) ->
     {loop, HttpLoop} = proplists:lookup(loop, Options),
-    Loop = fun (S) ->
-                   ?MODULE:loop(S, HttpLoop)
-           end,
+    Loop = {?MODULE, loop, [HttpLoop]},
     Options1 = [{loop, Loop} | proplists:delete(loop, Options)],
     mochilists:set_defaults(?DEFAULTS, Options1).
 
@@ -31,15 +29,12 @@ stop() ->
 stop(Name) ->
     mochiweb_socket_server:stop(Name).
 
-start() ->
-    start([{ip, "127.0.0.1"},
-           {loop, {?MODULE, default_body}}]).
-
 %% @spec start(Options) -> ServerRet
 %%     Options = [option()]
 %%     Option = {name, atom()} | {ip, string() | tuple()} | {backlog, integer()}
 %%              | {nodelay, boolean()} | {acceptor_pool_size, integer()}
 %%              | {ssl, boolean()} | {profile_fun, undefined | (Props) -> ok}
+%%              | {link, false}
 %% @doc Start a mochiweb server.
 %%      profile_fun is used to profile accept timing.
 %%      After each accept, if defined, profile_fun is called with a proplist of a subset of the mochiweb_socket_server state and timing information.
@@ -48,62 +43,18 @@ start() ->
 start(Options) ->
     mochiweb_socket_server:start(parse_options(Options)).
 
-frm(Body) ->
-    ["<html><head></head><body>"
-     "<form method=\"POST\">"
-     "<input type=\"hidden\" value=\"message\" name=\"hidden\"/>"
-     "<input type=\"submit\" value=\"regular POST\">"
-     "</form>"
-     "<br />"
-     "<form method=\"POST\" enctype=\"multipart/form-data\""
-     " action=\"/multipart\">"
-     "<input type=\"hidden\" value=\"multipart message\" name=\"hidden\"/>"
-     "<input type=\"file\" name=\"file\"/>"
-     "<input type=\"submit\" value=\"multipart POST\" />"
-     "</form>"
-     "<pre>", Body, "</pre>"
-     "</body></html>"].
-
-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)).
+start_link(Options) ->
+    mochiweb_socket_server:start_link(parse_options(Options)).
 
 loop(Socket, Body) ->
-    mochiweb_socket:setopts(Socket, [{packet, http}]),
+    ok = mochiweb_socket:setopts(Socket, [{packet, http}]),
     request(Socket, Body).
 
 request(Socket, Body) ->
-    mochiweb_socket:setopts(Socket, [{active, once}]),
+    ok = mochiweb_socket:setopts(Socket, [{active, once}]),
     receive
         {Protocol, _, {http_request, Method, Path, Version}} when Protocol == http orelse Protocol == ssl ->
-            mochiweb_socket:setopts(Socket, [{packet, httph}]),
+            ok = mochiweb_socket:setopts(Socket, [{packet, httph}]),
             headers(Socket, {Method, Path, Version}, [], Body, 0);
         {Protocol, _, {http_error, "\r\n"}} when Protocol == http orelse Protocol == ssl ->
             request(Socket, Body);
@@ -112,6 +63,13 @@ request(Socket, Body) ->
         {tcp_closed, _} ->
             mochiweb_socket:close(Socket),
             exit(normal);
+        {ssl_closed, _} ->
+            mochiweb_socket:close(Socket),
+            exit(normal);
+        {tcp_error,_,emsgsize} ->
+            % R15B02 returns this then closes the socket, so close and exit
+            mochiweb_socket:close(Socket),
+            exit(normal);
         _Other ->
             handle_invalid_request(Socket)
     after ?REQUEST_RECV_TIMEOUT ->
@@ -126,10 +84,10 @@ reentry(Body) ->
 
 headers(Socket, Request, Headers, _Body, ?MAX_HEADERS) ->
     %% Too many headers sent, bad request.
-    mochiweb_socket:setopts(Socket, [{packet, raw}]),
+    ok = mochiweb_socket:setopts(Socket, [{packet, raw}]),
     handle_invalid_request(Socket, Request, Headers);
 headers(Socket, Request, Headers, Body, HeaderCount) ->
-    mochiweb_socket:setopts(Socket, [{active, once}]),
+    ok = mochiweb_socket:setopts(Socket, [{active, once}]),
     receive
         {Protocol, _, http_eoh} when Protocol == http orelse Protocol == ssl ->
             Req = new_request(Socket, Request, Headers),
@@ -141,6 +99,10 @@ headers(Socket, Request, Headers, Body, HeaderCount) ->
         {tcp_closed, _} ->
             mochiweb_socket:close(Socket),
             exit(normal);
+        {tcp_error,_,emsgsize} ->
+            % R15B02 returns this then closes the socket, so close and exit
+            mochiweb_socket:close(Socket),
+            exit(normal);
         _Other ->
             handle_invalid_request(Socket, Request, Headers)
     after ?HEADERS_RECV_TIMEOUT ->
@@ -148,14 +110,19 @@ headers(Socket, Request, Headers, Body, HeaderCount) ->
         exit(normal)
     end.
 
+call_body({M, F, A}, Req) ->
+    erlang:apply(M, F, [Req | A]);
 call_body({M, F}, Req) ->
     M:F(Req);
 call_body(Body, Req) ->
     Body(Req).
 
+-spec handle_invalid_request(term()) -> no_return().
 handle_invalid_request(Socket) ->
-    handle_invalid_request(Socket, {'GET', {abs_path, "/"}, {0,9}}, []).
+    handle_invalid_request(Socket, {'GET', {abs_path, "/"}, {0,9}}, []),
+    exit(normal).
 
+-spec handle_invalid_request(term(), term(), term()) -> no_return().
 handle_invalid_request(Socket, Request, RevHeaders) ->
     Req = new_request(Socket, Request, RevHeaders),
     Req:respond({400, [], []}),
@@ -163,7 +130,7 @@ handle_invalid_request(Socket, Request, RevHeaders) ->
     exit(normal).
 
 new_request(Socket, Request, RevHeaders) ->
-    mochiweb_socket:setopts(Socket, [{packet, raw}]),
+    ok = mochiweb_socket:setopts(Socket, [{packet, raw}]),
     mochiweb:new_request({Socket, Request, lists:reverse(RevHeaders)}).
 
 after_response(Body, Req) ->
@@ -174,6 +141,7 @@ after_response(Body, Req) ->
             exit(normal);
         false ->
             Req:cleanup(),
+            erlang:garbage_collect(),
             ?MODULE:loop(Socket, Body)
     end.
 
@@ -211,6 +179,8 @@ range_skip_length(Spec, Size) ->
             invalid_range;
         {Start, End} when 0 =< Start, Start =< End, End < Size ->
             {Start, End - Start + 1};
+        {Start, End} when 0 =< Start, Start =< End, End >= Size ->
+            {Start, Size - Start};
         {_OutOfRange, _End} ->
             invalid_range
     end.
@@ -218,8 +188,8 @@ range_skip_length(Spec, Size) ->
 %%
 %% Tests
 %%
--include_lib("eunit/include/eunit.hrl").
 -ifdef(TEST).
+-include_lib("eunit/include/eunit.hrl").
 
 range_test() ->
     %% valid, single ranges
@@ -265,19 +235,23 @@ range_skip_length_test() ->
     BodySizeLess1 = BodySize - 1,
     ?assertEqual({BodySizeLess1, 1},
                  range_skip_length({BodySize - 1, none}, BodySize)),
+    ?assertEqual({BodySizeLess1, 1},
+                 range_skip_length({BodySize - 1, BodySize+5}, BodySize)),
+    ?assertEqual({BodySizeLess1, 1},
+                 range_skip_length({BodySize - 1, BodySize}, BodySize)),
 
     %% out of range, return whole thing
     ?assertEqual({0, BodySize},
                  range_skip_length({none, BodySize + 1}, BodySize)),
     ?assertEqual({0, BodySize},
                  range_skip_length({none, -1}, BodySize)),
+    ?assertEqual({0, BodySize},
+                 range_skip_length({0, BodySize + 1}, BodySize)),
 
     %% invalid ranges
     ?assertEqual(invalid_range,
                  range_skip_length({-1, 30}, BodySize)),
     ?assertEqual(invalid_range,
-                 range_skip_length({0, BodySize + 1}, BodySize)),
-    ?assertEqual(invalid_range,
                  range_skip_length({-1, BodySize + 1}, BodySize)),
     ?assertEqual(invalid_range,
                  range_skip_length({BodySize, 40}, BodySize)),

http://git-wip-us.apache.org/repos/asf/couchdb/blob/cbb8a550/src/mochiweb/mochiweb_io.erl
----------------------------------------------------------------------
diff --git a/src/mochiweb/mochiweb_io.erl b/src/mochiweb/mochiweb_io.erl
index 6ce57ec..8454b43 100644
--- a/src/mochiweb/mochiweb_io.erl
+++ b/src/mochiweb/mochiweb_io.erl
@@ -38,9 +38,6 @@ iodevice_size(IoDevice) ->
 %%
 %% Tests
 %%
--include_lib("eunit/include/eunit.hrl").
 -ifdef(TEST).
-
-
-
+-include_lib("eunit/include/eunit.hrl").
 -endif.

http://git-wip-us.apache.org/repos/asf/couchdb/blob/cbb8a550/src/mochiweb/mochiweb_mime.erl
----------------------------------------------------------------------
diff --git a/src/mochiweb/mochiweb_mime.erl b/src/mochiweb/mochiweb_mime.erl
index 5344aee..7d9f249 100644
--- a/src/mochiweb/mochiweb_mime.erl
+++ b/src/mochiweb/mochiweb_mime.erl
@@ -11,72 +11,393 @@
 %% @doc Given a filename extension (e.g. ".html") return a guess for the MIME
 %%      type such as "text/html". Will return the atom undefined if no good
 %%      guess is available.
-from_extension(".html") ->
-    "text/html";
-from_extension(".xhtml") ->
-    "application/xhtml+xml";
-from_extension(".xml") ->
-    "application/xml";
-from_extension(".css") ->
-    "text/css";
+
+from_extension(".stl") ->
+    "application/SLA";
+from_extension(".stp") ->
+    "application/STEP";
+from_extension(".step") ->
+    "application/STEP";
+from_extension(".dwg") ->
+    "application/acad";
+from_extension(".ez") ->
+    "application/andrew-inset";
+from_extension(".ccad") ->
+    "application/clariscad";
+from_extension(".drw") ->
+    "application/drafting";
+from_extension(".tsp") ->
+    "application/dsptype";
+from_extension(".dxf") ->
+    "application/dxf";
+from_extension(".xls") ->
+    "application/excel";
+from_extension(".unv") ->
+    "application/i-deas";
+from_extension(".jar") ->
+    "application/java-archive";
+from_extension(".hqx") ->
+    "application/mac-binhex40";
+from_extension(".cpt") ->
+    "application/mac-compactpro";
+from_extension(".pot") ->
+    "application/vnd.ms-powerpoint";
+from_extension(".ppt") ->
+    "application/vnd.ms-powerpoint";
+from_extension(".dms") ->
+    "application/octet-stream";
+from_extension(".lha") ->
+    "application/octet-stream";
+from_extension(".lzh") ->
+    "application/octet-stream";
+from_extension(".oda") ->
+    "application/oda";
+from_extension(".ogg") ->
+    "application/ogg";
+from_extension(".ogm") ->
+    "application/ogg";
+from_extension(".pdf") ->
+    "application/pdf";
+from_extension(".pgp") ->
+    "application/pgp";
+from_extension(".ai") ->
+    "application/postscript";
+from_extension(".eps") ->
+    "application/postscript";
+from_extension(".ps") ->
+    "application/postscript";
+from_extension(".prt") ->
+    "application/pro_eng";
+from_extension(".rtf") ->
+    "application/rtf";
+from_extension(".smi") ->
+    "application/smil";
+from_extension(".smil") ->
+    "application/smil";
+from_extension(".sol") ->
+    "application/solids";
+from_extension(".vda") ->
+    "application/vda";
+from_extension(".xlm") ->
+    "application/vnd.ms-excel";
+from_extension(".cod") ->
+    "application/vnd.rim.cod";
+from_extension(".pgn") ->
+    "application/x-chess-pgn";
+from_extension(".cpio") ->
+    "application/x-cpio";
+from_extension(".csh") ->
+    "application/x-csh";
+from_extension(".deb") ->
+    "application/x-debian-package";
+from_extension(".dcr") ->
+    "application/x-director";
+from_extension(".dir") ->
+    "application/x-director";
+from_extension(".dxr") ->
+    "application/x-director";
+from_extension(".gz") ->
+    "application/x-gzip";
+from_extension(".hdf") ->
+    "application/x-hdf";
+from_extension(".ipx") ->
+    "application/x-ipix";
+from_extension(".ips") ->
+    "application/x-ipscript";
 from_extension(".js") ->
     "application/x-javascript";
-from_extension(".jpg") ->
-    "image/jpeg";
-from_extension(".gif") ->
-    "image/gif";
-from_extension(".png") ->
-    "image/png";
+from_extension(".skd") ->
+    "application/x-koan";
+from_extension(".skm") ->
+    "application/x-koan";
+from_extension(".skp") ->
+    "application/x-koan";
+from_extension(".skt") ->
+    "application/x-koan";
+from_extension(".latex") ->
+    "application/x-latex";
+from_extension(".lsp") ->
+    "application/x-lisp";
+from_extension(".scm") ->
+    "application/x-lotusscreencam";
+from_extension(".mif") ->
+    "application/x-mif";
+from_extension(".com") ->
+    "application/x-msdos-program";
+from_extension(".exe") ->
+    "application/octet-stream";
+from_extension(".cdf") ->
+    "application/x-netcdf";
+from_extension(".nc") ->
+    "application/x-netcdf";
+from_extension(".pl") ->
+    "application/x-perl";
+from_extension(".pm") ->
+    "application/x-perl";
+from_extension(".rar") ->
+    "application/x-rar-compressed";
+from_extension(".sh") ->
+    "application/x-sh";
+from_extension(".shar") ->
+    "application/x-shar";
 from_extension(".swf") ->
     "application/x-shockwave-flash";
-from_extension(".zip") ->
-    "application/zip";
-from_extension(".bz2") ->
-    "application/x-bzip2";
-from_extension(".gz") ->
-    "application/x-gzip";
+from_extension(".sit") ->
+    "application/x-stuffit";
+from_extension(".sv4cpio") ->
+    "application/x-sv4cpio";
+from_extension(".sv4crc") ->
+    "application/x-sv4crc";
+from_extension(".tar.gz") ->
+    "application/x-tar-gz";
+from_extension(".tgz") ->
+    "application/x-tar-gz";
 from_extension(".tar") ->
     "application/x-tar";
-from_extension(".tgz") ->
-    "application/x-gzip";
+from_extension(".tcl") ->
+    "application/x-tcl";
+from_extension(".texi") ->
+    "application/x-texinfo";
+from_extension(".texinfo") ->
+    "application/x-texinfo";
+from_extension(".man") ->
+    "application/x-troff-man";
+from_extension(".me") ->
+    "application/x-troff-me";
+from_extension(".ms") ->
+    "application/x-troff-ms";
+from_extension(".roff") ->
+    "application/x-troff";
+from_extension(".t") ->
+    "application/x-troff";
+from_extension(".tr") ->
+    "application/x-troff";
+from_extension(".ustar") ->
+    "application/x-ustar";
+from_extension(".src") ->
+    "application/x-wais-source";
+from_extension(".zip") ->
+    "application/zip";
+from_extension(".tsi") ->
+    "audio/TSP-audio";
+from_extension(".au") ->
+    "audio/basic";
+from_extension(".snd") ->
+    "audio/basic";
+from_extension(".kar") ->
+    "audio/midi";
+from_extension(".mid") ->
+    "audio/midi";
+from_extension(".midi") ->
+    "audio/midi";
+from_extension(".mp2") ->
+    "audio/mpeg";
+from_extension(".mp3") ->
+    "audio/mpeg";
+from_extension(".mpga") ->
+    "audio/mpeg";
+from_extension(".aif") ->
+    "audio/x-aiff";
+from_extension(".aifc") ->
+    "audio/x-aiff";
+from_extension(".aiff") ->
+    "audio/x-aiff";
+from_extension(".m3u") ->
+    "audio/x-mpegurl";
+from_extension(".wax") ->
+    "audio/x-ms-wax";
+from_extension(".wma") ->
+    "audio/x-ms-wma";
+from_extension(".rpm") ->
+    "audio/x-pn-realaudio-plugin";
+from_extension(".ram") ->
+    "audio/x-pn-realaudio";
+from_extension(".rm") ->
+    "audio/x-pn-realaudio";
+from_extension(".ra") ->
+    "audio/x-realaudio";
+from_extension(".wav") ->
+    "audio/x-wav";
+from_extension(".pdb") ->
+    "chemical/x-pdb";
+from_extension(".ras") ->
+    "image/cmu-raster";
+from_extension(".gif") ->
+    "image/gif";
+from_extension(".ief") ->
+    "image/ief";
+from_extension(".jpe") ->
+    "image/jpeg";
+from_extension(".jpeg") ->
+    "image/jpeg";
+from_extension(".jpg") ->
+    "image/jpeg";
+from_extension(".jp2") ->
+    "image/jp2";
+from_extension(".png") ->
+    "image/png";
+from_extension(".tif") ->
+    "image/tiff";
+from_extension(".tiff") ->
+    "image/tiff";
+from_extension(".pnm") ->
+    "image/x-portable-anymap";
+from_extension(".pbm") ->
+    "image/x-portable-bitmap";
+from_extension(".pgm") ->
+    "image/x-portable-graymap";
+from_extension(".ppm") ->
+    "image/x-portable-pixmap";
+from_extension(".rgb") ->
+    "image/x-rgb";
+from_extension(".xbm") ->
+    "image/x-xbitmap";
+from_extension(".xwd") ->
+    "image/x-xwindowdump";
+from_extension(".iges") ->
+    "model/iges";
+from_extension(".igs") ->
+    "model/iges";
+from_extension(".mesh") ->
+    "model/mesh";
+from_extension(".") ->
+    "";
+from_extension(".msh") ->
+    "model/mesh";
+from_extension(".silo") ->
+    "model/mesh";
+from_extension(".vrml") ->
+    "model/vrml";
+from_extension(".wrl") ->
+    "model/vrml";
+from_extension(".css") ->
+    "text/css";
+from_extension(".htm") ->
+    "text/html";
+from_extension(".html") ->
+    "text/html";
+from_extension(".asc") ->
+    "text/plain";
+from_extension(".c") ->
+    "text/plain";
+from_extension(".cc") ->
+    "text/plain";
+from_extension(".f90") ->
+    "text/plain";
+from_extension(".f") ->
+    "text/plain";
+from_extension(".hh") ->
+    "text/plain";
+from_extension(".m") ->
+    "text/plain";
 from_extension(".txt") ->
     "text/plain";
-from_extension(".doc") ->
-    "application/msword";
-from_extension(".pdf") ->
-    "application/pdf";
-from_extension(".xls") ->
-    "application/vnd.ms-excel";
-from_extension(".rtf") ->
-    "application/rtf";
+from_extension(".rtx") ->
+    "text/richtext";
+from_extension(".sgm") ->
+    "text/sgml";
+from_extension(".sgml") ->
+    "text/sgml";
+from_extension(".tsv") ->
+    "text/tab-separated-values";
+from_extension(".jad") ->
+    "text/vnd.sun.j2me.app-descriptor";
+from_extension(".etx") ->
+    "text/x-setext";
+from_extension(".xml") ->
+    "application/xml";
+from_extension(".dl") ->
+    "video/dl";
+from_extension(".fli") ->
+    "video/fli";
+from_extension(".flv") ->
+    "video/x-flv";
+from_extension(".gl") ->
+    "video/gl";
+from_extension(".mp4") ->
+    "video/mp4";
+from_extension(".mpe") ->
+    "video/mpeg";
+from_extension(".mpeg") ->
+    "video/mpeg";
+from_extension(".mpg") ->
+    "video/mpeg";
 from_extension(".mov") ->
     "video/quicktime";
-from_extension(".mp3") ->
-    "audio/mpeg";
+from_extension(".qt") ->
+    "video/quicktime";
+from_extension(".viv") ->
+    "video/vnd.vivo";
+from_extension(".vivo") ->
+    "video/vnd.vivo";
+from_extension(".asf") ->
+    "video/x-ms-asf";
+from_extension(".asx") ->
+    "video/x-ms-asx";
+from_extension(".wmv") ->
+    "video/x-ms-wmv";
+from_extension(".wmx") ->
+    "video/x-ms-wmx";
+from_extension(".wvx") ->
+    "video/x-ms-wvx";
+from_extension(".avi") ->
+    "video/x-msvideo";
+from_extension(".movie") ->
+    "video/x-sgi-movie";
+from_extension(".mime") ->
+    "www/mime";
+from_extension(".ice") ->
+    "x-conference/x-cooltalk";
+from_extension(".vrm") ->
+    "x-world/x-vrml";
+from_extension(".spx") ->
+    "audio/ogg";
+from_extension(".xhtml") ->
+    "application/xhtml+xml";
+from_extension(".bz2") ->
+    "application/x-bzip2";
+from_extension(".doc") ->
+    "application/msword";
 from_extension(".z") ->
     "application/x-compress";
-from_extension(".wav") ->
-    "audio/x-wav";
 from_extension(".ico") ->
     "image/x-icon";
 from_extension(".bmp") ->
     "image/bmp";
 from_extension(".m4a") ->
     "audio/mpeg";
-from_extension(".m3u") ->
-    "audio/x-mpegurl";
-from_extension(".exe") ->
-    "application/octet-stream";
 from_extension(".csv") ->
     "text/csv";
+from_extension(".eot") ->
+    "application/vnd.ms-fontobject";
+from_extension(".m4v") ->
+    "video/mp4";
+from_extension(".svg") ->
+    "image/svg+xml";
+from_extension(".svgz") ->
+    "image/svg+xml";
+from_extension(".ttc") ->
+    "application/x-font-ttf";
+from_extension(".ttf") ->
+    "application/x-font-ttf";
+from_extension(".vcf") ->
+    "text/x-vcard";
+from_extension(".webm") ->
+    "video/web";
+from_extension(".webp") ->
+    "image/web";
+from_extension(".woff") ->
+    "application/x-font-woff";
+from_extension(".otf") ->
+    "font/opentype";
 from_extension(_) ->
     undefined.
 
 %%
 %% Tests
 %%
--include_lib("eunit/include/eunit.hrl").
 -ifdef(TEST).
+-include_lib("eunit/include/eunit.hrl").
 
 exhaustive_from_extension_test() ->
     T = mochiweb_cover:clause_lookup_table(?MODULE, from_extension),

http://git-wip-us.apache.org/repos/asf/couchdb/blob/cbb8a550/src/mochiweb/mochiweb_multipart.erl
----------------------------------------------------------------------
diff --git a/src/mochiweb/mochiweb_multipart.erl b/src/mochiweb/mochiweb_multipart.erl
index 3069cf4..a83a88c 100644
--- a/src/mochiweb/mochiweb_multipart.erl
+++ b/src/mochiweb/mochiweb_multipart.erl
@@ -128,7 +128,7 @@ default_file_handler_1(Filename, ContentType, Acc) ->
 
 parse_multipart_request(Req, Callback) ->
     %% TODO: Support chunked?
-    Length = list_to_integer(Req:get_header_value("content-length")),
+    Length = list_to_integer(Req:get_combined_header_value("content-length")),
     Boundary = iolist_to_binary(
                  get_boundary(Req:get_header_value("content-type"))),
     Prefix = <<"\r\n--", Boundary/binary>>,
@@ -240,24 +240,22 @@ get_boundary(ContentType) ->
             S
     end.
 
-find_in_binary(B, Data) when size(B) > 0 ->
-    case size(Data) - size(B) of
+%% @spec find_in_binary(Pattern::binary(), Data::binary()) ->
+%%            {exact, N} | {partial, N, K} | not_found
+%% @doc Searches for the given pattern in the given binary.
+find_in_binary(P, Data) when size(P) > 0 ->
+    PS = size(P),
+    DS = size(Data),
+    case DS - PS of
         Last when Last < 0 ->
-            partial_find(B, Data, 0, size(Data));
+            partial_find(P, Data, 0, DS);
         Last ->
-            find_in_binary(B, size(B), Data, 0, Last)
+            case binary:match(Data, P) of
+                {Pos, _} -> {exact, Pos};
+                nomatch -> partial_find(P, Data, Last+1, PS-1)
+            end
     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) ->
@@ -295,8 +293,8 @@ find_boundary(Prefix, Data) ->
 %%
 %% Tests
 %%
--include_lib("eunit/include/eunit.hrl").
 -ifdef(TEST).
+-include_lib("eunit/include/eunit.hrl").
 
 ssl_cert_opts() ->
     EbinDir = filename:dirname(code:which(?MODULE)),
@@ -313,7 +311,7 @@ with_socket_server(Transport, ServerFun, ClientFun) ->
         ssl ->
             ServerOpts0 ++ [{ssl, true}, {ssl_opts, ssl_cert_opts()}]
     end,
-    {ok, Server} = mochiweb_socket_server:start(ServerOpts),
+    {ok, Server} = mochiweb_socket_server:start_link(ServerOpts),
     Port = mochiweb_socket_server:get(Server, port),
     ClientOpts = [binary, {active, false}],
     {ok, Client} = case Transport of
@@ -378,7 +376,7 @@ parse3(Transport) ->
     TestCallback = fun (Next) -> test_callback(Next, Expect) end,
     ServerFun = fun (Socket) ->
                         ok = mochiweb_socket:send(Socket, BinContent),
-			exit(normal)
+                        exit(normal)
                 end,
     ClientFun = fun (Socket) ->
                         Req = fake_request(Socket, ContentType,
@@ -414,7 +412,7 @@ parse2(Transport) ->
     TestCallback = fun (Next) -> test_callback(Next, Expect) end,
     ServerFun = fun (Socket) ->
                         ok = mochiweb_socket:send(Socket, BinContent),
-			exit(normal)
+                        exit(normal)
                 end,
     ClientFun = fun (Socket) ->
                         Req = fake_request(Socket, ContentType,
@@ -451,7 +449,7 @@ do_parse_form(Transport) ->
     BinContent = iolist_to_binary(Content),
     ServerFun = fun (Socket) ->
                         ok = mochiweb_socket:send(Socket, BinContent),
-			exit(normal)
+                        exit(normal)
                 end,
     ClientFun = fun (Socket) ->
                         Req = fake_request(Socket, ContentType,
@@ -504,7 +502,7 @@ do_parse(Transport) ->
     TestCallback = fun (Next) -> test_callback(Next, Expect) end,
     ServerFun = fun (Socket) ->
                         ok = mochiweb_socket:send(Socket, BinContent),
-			exit(normal)
+                        exit(normal)
                 end,
     ClientFun = fun (Socket) ->
                         Req = fake_request(Socket, ContentType,
@@ -556,7 +554,7 @@ parse_partial_body_boundary(Transport) ->
     TestCallback = fun (Next) -> test_callback(Next, Expect) end,
     ServerFun = fun (Socket) ->
                         ok = mochiweb_socket:send(Socket, BinContent),
-			exit(normal)
+                        exit(normal)
                 end,
     ClientFun = fun (Socket) ->
                         Req = fake_request(Socket, ContentType,
@@ -609,7 +607,7 @@ parse_large_header(Transport) ->
     TestCallback = fun (Next) -> test_callback(Next, Expect) end,
     ServerFun = fun (Socket) ->
                         ok = mochiweb_socket:send(Socket, BinContent),
-			exit(normal)
+                        exit(normal)
                 end,
     ClientFun = fun (Socket) ->
                         Req = fake_request(Socket, ContentType,
@@ -685,7 +683,7 @@ flash_parse(Transport) ->
     TestCallback = fun (Next) -> test_callback(Next, Expect) end,
     ServerFun = fun (Socket) ->
                         ok = mochiweb_socket:send(Socket, BinContent),
-			exit(normal)
+                        exit(normal)
                 end,
     ClientFun = fun (Socket) ->
                         Req = fake_request(Socket, ContentType,
@@ -733,7 +731,7 @@ flash_parse2(Transport) ->
     TestCallback = fun (Next) -> test_callback(Next, Expect) end,
     ServerFun = fun (Socket) ->
                         ok = mochiweb_socket:send(Socket, BinContent),
-			exit(normal)
+                        exit(normal)
                 end,
     ClientFun = fun (Socket) ->
                         Req = fake_request(Socket, ContentType,
@@ -821,4 +819,54 @@ multipart_body_test() ->
                                        10))),
     ok.
 
+%% @todo Move somewhere more appropriate than in the test suite
+
+multipart_parsing_benchmark_test() ->
+  run_multipart_parsing_benchmark(1).
+
+run_multipart_parsing_benchmark(0) -> ok;
+run_multipart_parsing_benchmark(N) ->
+     multipart_parsing_benchmark(),
+     run_multipart_parsing_benchmark(N-1).
+
+multipart_parsing_benchmark() ->
+    ContentType = "multipart/form-data; boundary=----------ei4GI3GI3Ij5Ef1ae0KM7Ij5ei4Ij5",
+    Chunk = binary:copy(<<"This Is_%Some=Quite0Long4String2Used9For7BenchmarKing.5">>, 102400),
+    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 = mochiweb_socket: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(plain, ServerFun, ClientFun),
+    ok.
 -endif.

http://git-wip-us.apache.org/repos/asf/couchdb/blob/cbb8a550/src/mochiweb/mochiweb_request.erl
----------------------------------------------------------------------
diff --git a/src/mochiweb/mochiweb_request.erl b/src/mochiweb/mochiweb_request.erl
index 980f5ad..1b431d3 100644
--- a/src/mochiweb/mochiweb_request.erl
+++ b/src/mochiweb/mochiweb_request.erl
@@ -3,7 +3,7 @@
 
 %% @doc MochiWeb HTTP Request abstraction.
 
--module(mochiweb_request, [Socket, Method, RawPath, Version, Headers]).
+-module(mochiweb_request).
 -author('bob@mochimedia.com').
 
 -include_lib("kernel/include/file.hrl").
@@ -11,17 +11,18 @@
 
 -define(QUIP, "Any of you quaids got a smint?").
 
--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([accepts_content_type/1]).
+-export([new/5]).
+-export([get_header_value/2, get_primary_header_value/2, get_combined_header_value/2, get/2, dump/1]).
+-export([send/2, recv/2, recv/3, recv_body/1, recv_body/2, stream_body/4]).
+-export([start_response/2, start_response_length/2, start_raw_response/2]).
+-export([respond/2, ok/2]).
+-export([not_found/1, not_found/2]).
+-export([parse_post/1, parse_qs/1]).
+-export([should_close/1, cleanup/1]).
+-export([parse_cookie/1, get_cookie_value/2]).
+-export([serve_file/3, serve_file/4]).
+-export([accepted_encodings/2]).
+-export([accepts_content_type/2, accepted_content_types/2]).
 
 -define(SAVE_QS, mochiweb_request_qs).
 -define(SAVE_PATH, mochiweb_request_path).
@@ -32,11 +33,10 @@
 -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 request() = {mochiweb_request,[_Socket,_Method,_RawPath,_Version,_Headers]}
 %% @type response(). A mochiweb_response parameterized module instance.
 %% @type ioheaders() = headers() | [{key(), value()}].
 
@@ -46,50 +46,58 @@
 % Maximum recv_body() length of 1MB
 -define(MAX_RECV_BODY, (1024*1024)).
 
-%% @spec get_header_value(K) -> undefined | Value
+%% @spec new(Socket, Method, RawPath, Version, headers()) -> request()
+%% @doc Create a new request instance.
+new(Socket, Method, RawPath, Version, Headers) ->
+    {?MODULE, [Socket, Method, RawPath, Version, Headers]}.
+
+%% @spec get_header_value(K, request()) -> undefined | Value
 %% @doc Get the value of a given request header.
-get_header_value(K) ->
+get_header_value(K, {?MODULE, [_Socket, _Method, _RawPath, _Version, Headers]}) ->
     mochiweb_headers:get_value(K, Headers).
 
-get_primary_header_value(K) ->
+get_primary_header_value(K, {?MODULE, [_Socket, _Method, _RawPath, _Version, Headers]}) ->
     mochiweb_headers:get_primary_value(K, Headers).
 
+get_combined_header_value(K, {?MODULE, [_Socket, _Method, _RawPath, _Version, Headers]}) ->
+    mochiweb_headers:get_combined_value(K, Headers).
+
 %% @type field() = socket | scheme | method | raw_path | version | headers | peer | path | body_length | range
 
-%% @spec get(field()) -> term()
+%% @spec get(field(), request()) -> term()
 %% @doc Return the internal representation of the given field. If
 %%      <code>socket</code> is requested on a HTTPS connection, then
 %%      an ssl socket will be returned as <code>{ssl, SslSocket}</code>.
 %%      You can use <code>SslSocket</code> with the <code>ssl</code>
 %%      application, eg: <code>ssl:peercert(SslSocket)</code>.
-get(socket) ->
+get(socket, {?MODULE, [Socket, _Method, _RawPath, _Version, _Headers]}) ->
     Socket;
-get(scheme) ->
+get(scheme, {?MODULE, [Socket, _Method, _RawPath, _Version, _Headers]}) ->
     case mochiweb_socket:type(Socket) of
         plain ->
             http;
         ssl ->
             https
     end;
-get(method) ->
+get(method, {?MODULE, [_Socket, Method, _RawPath, _Version, _Headers]}) ->
     Method;
-get(raw_path) ->
+get(raw_path, {?MODULE, [_Socket, _Method, RawPath, _Version, _Headers]}) ->
     RawPath;
-get(version) ->
+get(version, {?MODULE, [_Socket, _Method, _RawPath, Version, _Headers]}) ->
     Version;
-get(headers) ->
+get(headers, {?MODULE, [_Socket, _Method, _RawPath, _Version, Headers]}) ->
     Headers;
-get(peer) ->
+get(peer, {?MODULE, [Socket, _Method, _RawPath, _Version, _Headers]}=THIS) ->
     case mochiweb_socket:peername(Socket) of
         {ok, {Addr={10, _, _, _}, _Port}} ->
-            case get_header_value("x-forwarded-for") of
+            case get_header_value("x-forwarded-for", THIS) 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
+            case get_header_value("x-forwarded-for", THIS) of
                 undefined ->
                     "127.0.0.1";
                 Hosts ->
@@ -100,7 +108,7 @@ get(peer) ->
         {error, enotconn} ->
             exit(normal)
     end;
-get(path) ->
+get(path, {?MODULE, [_Socket, _Method, RawPath, _Version, _Headers]}) ->
     case erlang:get(?SAVE_PATH) of
         undefined ->
             {Path0, _, _} = mochiweb_util:urlsplit_path(RawPath),
@@ -110,35 +118,35 @@ get(path) ->
         Cached ->
             Cached
     end;
-get(body_length) ->
+get(body_length, {?MODULE, [_Socket, _Method, _RawPath, _Version, _Headers]}=THIS) ->
     case erlang:get(?SAVE_BODY_LENGTH) of
         undefined ->
-            BodyLength = body_length(),
+            BodyLength = body_length(THIS),
             put(?SAVE_BODY_LENGTH, {cached, BodyLength}),
             BodyLength;
         {cached, Cached} ->
             Cached
     end;
-get(range) ->
-    case get_header_value(range) of
+get(range, {?MODULE, [_Socket, _Method, _RawPath, _Version, _Headers]}=THIS) ->
+    case get_header_value(range, THIS) of
         undefined ->
             undefined;
         RawRange ->
             mochiweb_http:parse_range_request(RawRange)
     end.
 
-%% @spec dump() -> {mochiweb_request, [{atom(), term()}]}
+%% @spec dump(request()) -> {mochiweb_request, [{atom(), term()}]}
 %% @doc Dump the internal representation to a "human readable" set of terms
 %%      for debugging/inspection purposes.
-dump() ->
+dump({?MODULE, [_Socket, Method, RawPath, Version, Headers]}) ->
     {?MODULE, [{method, Method},
                {version, Version},
                {raw_path, RawPath},
                {headers, mochiweb_headers:to_list(Headers)}]}.
 
-%% @spec send(iodata()) -> ok
+%% @spec send(iodata(), request()) -> ok
 %% @doc Send data over the socket.
-send(Data) ->
+send(Data, {?MODULE, [Socket, _Method, _RawPath, _Version, _Headers]}) ->
     case mochiweb_socket:send(Socket, Data) of
         ok ->
             ok;
@@ -146,16 +154,16 @@ send(Data) ->
             exit(normal)
     end.
 
-%% @spec recv(integer()) -> binary()
+%% @spec recv(integer(), request()) -> binary()
 %% @doc Receive Length bytes from the client as a binary, with the default
 %%      idle timeout.
-recv(Length) ->
-    recv(Length, ?IDLE_TIMEOUT).
+recv(Length, {?MODULE, [_Socket, _Method, _RawPath, _Version, _Headers]}=THIS) ->
+    recv(Length, ?IDLE_TIMEOUT, THIS).
 
-%% @spec recv(integer(), integer()) -> binary()
+%% @spec recv(integer(), integer(), request()) -> binary()
 %% @doc Receive Length bytes from the client as a binary, with the given
 %%      Timeout in msec.
-recv(Length, Timeout) ->
+recv(Length, Timeout, {?MODULE, [Socket, _Method, _RawPath, _Version, _Headers]}) ->
     case mochiweb_socket:recv(Socket, Length, Timeout) of
         {ok, Data} ->
             put(?SAVE_RECV, true),
@@ -164,12 +172,12 @@ recv(Length, Timeout) ->
             exit(normal)
     end.
 
-%% @spec body_length() -> undefined | chunked | unknown_transfer_encoding | integer()
+%% @spec body_length(request()) -> 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
+body_length({?MODULE, [_Socket, _Method, _RawPath, _Version, _Headers]}=THIS) ->
+    case get_header_value("transfer-encoding", THIS) of
         undefined ->
-            case get_header_value("content-length") of
+            case get_combined_header_value("content-length", THIS) of
                 undefined ->
                     undefined;
                 Length ->
@@ -182,16 +190,16 @@ body_length() ->
     end.
 
 
-%% @spec recv_body() -> binary()
+%% @spec recv_body(request()) -> 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).
+recv_body({?MODULE, [_Socket, _Method, _RawPath, _Version, _Headers]}=THIS) ->
+    recv_body(?MAX_RECV_BODY, THIS).
 
-%% @spec recv_body(integer()) -> binary()
+%% @spec recv_body(integer(), request()) -> binary()
 %% @doc Receive the body of the HTTP request (defined by Content-Length).
 %%      Will receive up to MaxBody bytes.
-recv_body(MaxBody) ->
+recv_body(MaxBody, {?MODULE, [_Socket, _Method, _RawPath, _Version, _Headers]}=THIS) ->
     case erlang:get(?SAVE_BODY) of
         undefined ->
             % we could use a sane constant for max chunk size
@@ -205,17 +213,18 @@ recv_body(MaxBody) ->
                     true ->
                         {NewLength, [Bin | BinAcc]}
                     end
-                end, {0, []}, MaxBody),
+                end, {0, []}, MaxBody, THIS),
             put(?SAVE_BODY, Body),
             Body;
         Cached -> Cached
     end.
 
-stream_body(MaxChunkSize, ChunkFun, FunState) ->
-    stream_body(MaxChunkSize, ChunkFun, FunState, undefined).
+stream_body(MaxChunkSize, ChunkFun, FunState, {?MODULE,[_Socket,_Method,_RawPath,_Version,_Headers]}=THIS) ->
+    stream_body(MaxChunkSize, ChunkFun, FunState, undefined, THIS).
 
-stream_body(MaxChunkSize, ChunkFun, FunState, MaxBodyLength) ->
-    Expect = case get_header_value("expect") of
+stream_body(MaxChunkSize, ChunkFun, FunState, MaxBodyLength,
+            {?MODULE, [_Socket, _Method, _RawPath, _Version, _Headers]}=THIS) ->
+    Expect = case get_header_value("expect", THIS) of
                  undefined ->
                      undefined;
                  Value when is_list(Value) ->
@@ -223,11 +232,12 @@ stream_body(MaxChunkSize, ChunkFun, FunState, MaxBodyLength) ->
              end,
     case Expect of
         "100-continue" ->
-            start_raw_response({100, gb_trees:empty()});
+            _ = start_raw_response({100, gb_trees:empty()}, THIS),
+            ok;
         _Else ->
             ok
     end,
-    case body_length() of
+    case body_length(THIS) of
         undefined ->
             undefined;
         {unknown_transfer_encoding, Unknown} ->
@@ -236,7 +246,7 @@ stream_body(MaxChunkSize, ChunkFun, FunState, MaxBodyLength) ->
             % In this case the MaxBody is actually used to
             % determine the maximum allowed size of a single
             % chunk.
-            stream_chunked_body(MaxChunkSize, ChunkFun, FunState);
+            stream_chunked_body(MaxChunkSize, ChunkFun, FunState, THIS);
         0 ->
             <<>>;
         Length when is_integer(Length) ->
@@ -244,62 +254,64 @@ stream_body(MaxChunkSize, ChunkFun, FunState, MaxBodyLength) ->
             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})
+                stream_unchunked_body(Length, ChunkFun, FunState, THIS)
+            end
     end.
 
 
-%% @spec start_response({integer(), ioheaders()}) -> response()
+%% @spec start_response({integer(), ioheaders()}, request()) -> 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}) ->
+start_response({Code, ResponseHeaders}, {?MODULE, [_Socket, _Method, _RawPath, _Version, _Headers]}=THIS) ->
     HResponse = mochiweb_headers:make(ResponseHeaders),
     HResponse1 = mochiweb_headers:default_from_list(server_headers(),
                                                     HResponse),
-    start_raw_response({Code, HResponse1}).
+    start_raw_response({Code, HResponse1}, THIS).
 
-%% @spec start_raw_response({integer(), headers()}) -> response()
+%% @spec start_raw_response({integer(), headers()}, request()) -> response()
 %% @doc Start the HTTP response by sending the Code HTTP response and
 %%      ResponseHeaders.
-start_raw_response({Code, ResponseHeaders}) ->
+start_raw_response({Code, ResponseHeaders}, {?MODULE, [_Socket, _Method, _RawPath, Version, _Headers]}=THIS) ->
     F = fun ({K, V}, Acc) ->
                 [mochiweb_util: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]),
+    send([make_version(Version), make_code(Code), <<"\r\n">> | End], THIS),
     mochiweb:new_response({THIS, Code, ResponseHeaders}).
 
 
-%% @spec start_response_length({integer(), ioheaders(), integer()}) -> response()
+%% @spec start_response_length({integer(), ioheaders(), integer()}, request()) -> 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}) ->
+start_response_length({Code, ResponseHeaders, Length},
+                      {?MODULE, [_Socket, _Method, _RawPath, _Version, _Headers]}=THIS) ->
     HResponse = mochiweb_headers:make(ResponseHeaders),
     HResponse1 = mochiweb_headers:enter("Content-Length", Length, HResponse),
-    start_response({Code, HResponse1}).
+    start_response({Code, HResponse1}, THIS).
 
-%% @spec respond({integer(), ioheaders(), iodata() | chunked | {file, IoDevice}}) -> response()
+%% @spec respond({integer(), ioheaders(), iodata() | chunked | {file, IoDevice}}, request()) -> 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}}) ->
+respond({Code, ResponseHeaders, {file, IoDevice}},
+        {?MODULE, [_Socket, Method, _RawPath, _Version, _Headers]}=THIS) ->
     Length = mochiweb_io:iodevice_size(IoDevice),
-    Response = start_response_length({Code, ResponseHeaders, Length}),
+    Response = start_response_length({Code, ResponseHeaders, Length}, THIS),
     case Method of
         'HEAD' ->
             ok;
         _ ->
-            mochiweb_io:iodevice_stream(fun send/1, IoDevice)
+            mochiweb_io:iodevice_stream(
+              fun (Body) -> send(Body, THIS) end,
+              IoDevice)
     end,
     Response;
-respond({Code, ResponseHeaders, chunked}) ->
+respond({Code, ResponseHeaders, chunked}, {?MODULE, [_Socket, Method, _RawPath, Version, _Headers]}=THIS) ->
     HResponse = mochiweb_headers:make(ResponseHeaders),
     HResponse1 = case Method of
                      'HEAD' ->
@@ -320,35 +332,35 @@ respond({Code, ResponseHeaders, chunked}) ->
                          put(?SAVE_FORCE_CLOSE, true),
                          HResponse
                  end,
-    start_response({Code, HResponse1});
-respond({Code, ResponseHeaders, Body}) ->
-    Response = start_response_length({Code, ResponseHeaders, iolist_size(Body)}),
+    start_response({Code, HResponse1}, THIS);
+respond({Code, ResponseHeaders, Body}, {?MODULE, [_Socket, Method, _RawPath, _Version, _Headers]}=THIS) ->
+    Response = start_response_length({Code, ResponseHeaders, iolist_size(Body)}, THIS),
     case Method of
         'HEAD' ->
             ok;
         _ ->
-            send(Body)
+            send(Body, THIS)
     end,
     Response.
 
-%% @spec not_found() -> response()
+%% @spec not_found(request()) -> response()
 %% @doc Alias for <code>not_found([])</code>.
-not_found() ->
-    not_found([]).
+not_found({?MODULE, [_Socket, _Method, _RawPath, _Version, _Headers]}=THIS) ->
+    not_found([], THIS).
 
-%% @spec not_found(ExtraHeaders) -> response()
+%% @spec not_found(ExtraHeaders, request()) -> response()
 %% @doc Alias for <code>respond({404, [{"Content-Type", "text/plain"}
 %% | ExtraHeaders], &lt;&lt;"Not found."&gt;&gt;})</code>.
-not_found(ExtraHeaders) ->
+not_found(ExtraHeaders, {?MODULE, [_Socket, _Method, _RawPath, _Version, _Headers]}=THIS) ->
     respond({404, [{"Content-Type", "text/plain"} | ExtraHeaders],
-             <<"Not found.">>}).
+             <<"Not found.">>}, THIS).
 
-%% @spec ok({value(), iodata()} | {value(), ioheaders(), iodata() | {file, IoDevice}}) ->
+%% @spec ok({value(), iodata()} | {value(), ioheaders(), iodata() | {file, IoDevice}}, request()) ->
 %%           response()
 %% @doc respond({200, [{"Content-Type", ContentType} | Headers], Body}).
-ok({ContentType, Body}) ->
-    ok({ContentType, [], Body});
-ok({ContentType, ResponseHeaders, Body}) ->
+ok({ContentType, Body}, {?MODULE, [_Socket, _Method, _RawPath, _Version, _Headers]}=THIS) ->
+    ok({ContentType, [], Body}, THIS);
+ok({ContentType, ResponseHeaders, Body}, {?MODULE, [_Socket, _Method, _RawPath, _Version, _Headers]}=THIS) ->
     HResponse = mochiweb_headers:make(ResponseHeaders),
     case THIS:get(range) of
         X when (X =:= undefined orelse X =:= fail) orelse Body =:= chunked ->
@@ -357,7 +369,7 @@ ok({ContentType, ResponseHeaders, Body}) ->
             %% full response.
             HResponse1 = mochiweb_headers:enter("Content-Type", ContentType,
                                                 HResponse),
-            respond({200, HResponse1, Body});
+            respond({200, HResponse1, Body}, THIS);
         Ranges ->
             {PartList, Size} = range_parts(Body, Ranges),
             case PartList of
@@ -366,7 +378,7 @@ ok({ContentType, ResponseHeaders, Body}) ->
                                                         ContentType,
                                                         HResponse),
                     %% could be 416, for now we'll just return 200
-                    respond({200, HResponse1, Body});
+                    respond({200, HResponse1, Body}, THIS);
                 PartList ->
                     {RangeHeaders, RangeBody} =
                         mochiweb_multipart:parts_to_body(PartList, ContentType, Size),
@@ -374,46 +386,50 @@ ok({ContentType, ResponseHeaders, Body}) ->
                                    [{"Accept-Ranges", "bytes"} |
                                     RangeHeaders],
                                    HResponse),
-                    respond({206, HResponse1, RangeBody})
+                    respond({206, HResponse1, RangeBody}, THIS)
             end
     end.
 
-%% @spec should_close() -> bool()
+%% @spec should_close(request()) -> bool()
 %% @doc Return true if the connection must be closed. If false, using
 %%      Keep-Alive should be safe.
-should_close() ->
+should_close({?MODULE, [_Socket, _Method, _RawPath, Version, _Headers]}=THIS) ->
     ForceClose = erlang:get(?SAVE_FORCE_CLOSE) =/= undefined,
     DidNotRecv = erlang:get(?SAVE_RECV) =:= undefined,
     ForceClose orelse Version < {1, 0}
         %% Connection: close
-        orelse get_header_value("connection") =:= "close"
+        orelse is_close(get_header_value("connection", THIS))
         %% HTTP 1.0 requires Connection: Keep-Alive
         orelse (Version =:= {1, 0}
-                andalso get_header_value("connection") =/= "Keep-Alive")
+                andalso get_header_value("connection", THIS) =/= "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)
+                andalso get_combined_header_value("content-length", THIS) =/= undefined
+                andalso list_to_integer(get_combined_header_value("content-length", THIS)) > 0)
         orelse (DidNotRecv
-                andalso get_header_value("transfer-encoding") =:= "chunked").
+                andalso get_header_value("transfer-encoding", THIS) =:= "chunked").
 
-%% @spec cleanup() -> ok
+is_close("close") ->
+    true;
+is_close(S=[_C, _L, _O, _S, _E]) ->
+    string:to_lower(S) =:= "close";
+is_close(_) ->
+    false.
+
+%% @spec cleanup(request()) -> 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_BODY_LENGTH,
-                       ?SAVE_POST,
-                       ?SAVE_COOKIE,
-                       ?SAVE_FORCE_CLOSE]],
+cleanup({?MODULE, [_Socket, _Method, _RawPath, _Version, _Headers]}) ->
+    L = [?SAVE_QS, ?SAVE_PATH, ?SAVE_RECV, ?SAVE_BODY, ?SAVE_BODY_LENGTH,
+         ?SAVE_POST, ?SAVE_COOKIE, ?SAVE_FORCE_CLOSE],
+    lists:foreach(fun(K) ->
+                          erase(K)
+                  end, L),
     ok.
 
-%% @spec parse_qs() -> [{Key::string(), Value::string()}]
+%% @spec parse_qs(request()) -> [{Key::string(), Value::string()}]
 %% @doc Parse the query string of the URL.
-parse_qs() ->
+parse_qs({?MODULE, [_Socket, _Method, RawPath, _Version, _Headers]}) ->
     case erlang:get(?SAVE_QS) of
         undefined ->
             {_, QueryString, _} = mochiweb_util:urlsplit_path(RawPath),
@@ -424,17 +440,17 @@ parse_qs() ->
             Cached
     end.
 
-%% @spec get_cookie_value(Key::string) -> string() | undefined
+%% @spec get_cookie_value(Key::string, request()) -> string() | undefined
 %% @doc Get the value of the given cookie.
-get_cookie_value(Key) ->
-    proplists:get_value(Key, parse_cookie()).
+get_cookie_value(Key, {?MODULE, [_Socket, _Method, _RawPath, _Version, _Headers]}=THIS) ->
+    proplists:get_value(Key, parse_cookie(THIS)).
 
-%% @spec parse_cookie() -> [{Key::string(), Value::string()}]
+%% @spec parse_cookie(request()) -> [{Key::string(), Value::string()}]
 %% @doc Parse the cookie header.
-parse_cookie() ->
+parse_cookie({?MODULE, [_Socket, _Method, _RawPath, _Version, _Headers]}=THIS) ->
     case erlang:get(?SAVE_COOKIE) of
         undefined ->
-            Cookies = case get_header_value("cookie") of
+            Cookies = case get_header_value("cookie", THIS) of
                           undefined ->
                               [];
                           Value ->
@@ -446,17 +462,17 @@ parse_cookie() ->
             Cached
     end.
 
-%% @spec parse_post() -> [{Key::string(), Value::string()}]
+%% @spec parse_post(request()) -> [{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() ->
+parse_post({?MODULE, [_Socket, _Method, _RawPath, _Version, _Headers]}=THIS) ->
     case erlang:get(?SAVE_POST) of
         undefined ->
-            Parsed = case recv_body() of
+            Parsed = case recv_body(THIS) of
                          undefined ->
                              [];
                          Binary ->
-                             case get_primary_header_value("content-type") of
+                             case get_primary_header_value("content-type",THIS) of
                                  "application/x-www-form-urlencoded" ++ _ ->
                                      mochiweb_util:parse_qs(Binary);
                                  _ ->
@@ -469,41 +485,43 @@ parse_post() ->
             Cached
     end.
 
-%% @spec stream_chunked_body(integer(), fun(), term()) -> term()
+%% @spec stream_chunked_body(integer(), fun(), term(), request()) -> 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
+stream_chunked_body(MaxChunkSize, Fun, FunState,
+                    {?MODULE, [_Socket, _Method, _RawPath, _Version, _Headers]}=THIS) ->
+    case read_chunk_length(THIS) of
         0 ->
-            Fun({0, read_chunk(0)}, FunState);
+            Fun({0, read_chunk(0, THIS)}, FunState);
         Length when Length > MaxChunkSize ->
-            NewState = read_sub_chunks(Length, MaxChunkSize, Fun, FunState),
-            stream_chunked_body(MaxChunkSize, Fun, NewState);
+            NewState = read_sub_chunks(Length, MaxChunkSize, Fun, FunState, THIS),
+            stream_chunked_body(MaxChunkSize, Fun, NewState, THIS);
         Length ->
-            NewState = Fun({Length, read_chunk(Length)}, FunState),
-            stream_chunked_body(MaxChunkSize, Fun, NewState)
+            NewState = Fun({Length, read_chunk(Length, THIS)}, FunState),
+            stream_chunked_body(MaxChunkSize, Fun, NewState, THIS)
     end.
 
-stream_unchunked_body(0, Fun, FunState) ->
+stream_unchunked_body(0, Fun, FunState, {?MODULE, [_Socket, _Method, _RawPath, _Version, _Headers]}) ->
     Fun({0, <<>>}, FunState);
-stream_unchunked_body(Length, Fun, FunState) when Length > 0 ->
+stream_unchunked_body(Length, Fun, FunState,
+                      {?MODULE, [_Socket, _Method, _RawPath, _Version, _Headers]}=THIS) when Length > 0 ->
     PktSize = case Length > ?RECBUF_SIZE of
         true ->
             ?RECBUF_SIZE;
         false ->
             Length
     end,
-    Bin = recv(PktSize),
+    Bin = recv(PktSize, THIS),
     NewState = Fun({PktSize, Bin}, FunState),
-    stream_unchunked_body(Length - PktSize, Fun, NewState).
+    stream_unchunked_body(Length - PktSize, Fun, NewState, THIS).
 
-%% @spec read_chunk_length() -> integer()
+%% @spec read_chunk_length(request()) -> integer()
 %% @doc Read the length of the next HTTP chunk.
-read_chunk_length() ->
-    mochiweb_socket:setopts(Socket, [{packet, line}]),
+read_chunk_length({?MODULE, [Socket, _Method, _RawPath, _Version, _Headers]}) ->
+    ok = mochiweb_socket:setopts(Socket, [{packet, line}]),
     case mochiweb_socket:recv(Socket, 0, ?IDLE_TIMEOUT) of
         {ok, Header} ->
-            mochiweb_socket:setopts(Socket, [{packet, raw}]),
+            ok = mochiweb_socket:setopts(Socket, [{packet, raw}]),
             Splitter = fun (C) ->
                                C =/= $\r andalso C =/= $\n andalso C =/= $
                        end,
@@ -513,11 +531,11 @@ read_chunk_length() ->
             exit(normal)
     end.
 
-%% @spec read_chunk(integer()) -> Chunk::binary() | [Footer::binary()]
+%% @spec read_chunk(integer(), request()) -> 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) ->
-    mochiweb_socket:setopts(Socket, [{packet, line}]),
+read_chunk(0, {?MODULE, [Socket, _Method, _RawPath, _Version, _Headers]}) ->
+    ok = mochiweb_socket:setopts(Socket, [{packet, line}]),
     F = fun (F1, Acc) ->
                 case mochiweb_socket:recv(Socket, 0, ?IDLE_TIMEOUT) of
                     {ok, <<"\r\n">>} ->
@@ -529,10 +547,10 @@ read_chunk(0) ->
                 end
         end,
     Footers = F(F, []),
-    mochiweb_socket:setopts(Socket, [{packet, raw}]),
+    ok = mochiweb_socket:setopts(Socket, [{packet, raw}]),
     put(?SAVE_RECV, true),
     Footers;
-read_chunk(Length) ->
+read_chunk(Length, {?MODULE, [Socket, _Method, _RawPath, _Version, _Headers]}) ->
     case mochiweb_socket:recv(Socket, 2 + Length, ?IDLE_TIMEOUT) of
         {ok, <<Chunk:Length/binary, "\r\n">>} ->
             Chunk;
@@ -540,32 +558,34 @@ read_chunk(Length) ->
             exit(normal)
     end.
 
-read_sub_chunks(Length, MaxChunkSize, Fun, FunState) when Length > MaxChunkSize ->
-    Bin = recv(MaxChunkSize),
+read_sub_chunks(Length, MaxChunkSize, Fun, FunState,
+                {?MODULE, [_Socket, _Method, _RawPath, _Version, _Headers]}=THIS) when Length > MaxChunkSize ->
+    Bin = recv(MaxChunkSize, THIS),
     NewState = Fun({size(Bin), Bin}, FunState),
-    read_sub_chunks(Length - MaxChunkSize, MaxChunkSize, Fun, NewState);
+    read_sub_chunks(Length - MaxChunkSize, MaxChunkSize, Fun, NewState, THIS);
 
-read_sub_chunks(Length, _MaxChunkSize, Fun, FunState) ->
-    Fun({Length, read_chunk(Length)}, FunState).
+read_sub_chunks(Length, _MaxChunkSize, Fun, FunState,
+                {?MODULE, [_Socket, _Method, _RawPath, _Version, _Headers]}=THIS) ->
+    Fun({Length, read_chunk(Length, THIS)}, FunState).
 
-%% @spec serve_file(Path, DocRoot) -> Response
+%% @spec serve_file(Path, DocRoot, request()) -> Response
 %% @doc Serve a file relative to DocRoot.
-serve_file(Path, DocRoot) ->
-    serve_file(Path, DocRoot, []).
+serve_file(Path, DocRoot, {?MODULE, [_Socket, _Method, _RawPath, _Version, _Headers]}=THIS) ->
+    serve_file(Path, DocRoot, [], THIS).
 
-%% @spec serve_file(Path, DocRoot, ExtraHeaders) -> Response
+%% @spec serve_file(Path, DocRoot, ExtraHeaders, request()) -> Response
 %% @doc Serve a file relative to DocRoot.
-serve_file(Path, DocRoot, ExtraHeaders) ->
+serve_file(Path, DocRoot, ExtraHeaders, {?MODULE, [_Socket, _Method, _RawPath, _Version, _Headers]}=THIS) ->
     case mochiweb_util:safe_relative_path(Path) of
         undefined ->
-            not_found(ExtraHeaders);
+            not_found(ExtraHeaders, THIS);
         RelPath ->
             FullPath = filename:join([DocRoot, RelPath]),
             case filelib:is_dir(FullPath) of
                 true ->
-                    maybe_redirect(RelPath, FullPath, ExtraHeaders);
+                    maybe_redirect(RelPath, FullPath, ExtraHeaders, THIS);
                 false ->
-                    maybe_serve_file(FullPath, ExtraHeaders)
+                    maybe_serve_file(FullPath, ExtraHeaders, THIS)
             end
     end.
 
@@ -575,13 +595,14 @@ serve_file(Path, DocRoot, ExtraHeaders) ->
 directory_index(FullPath) ->
     filename:join([FullPath, "index.html"]).
 
-maybe_redirect([], FullPath, ExtraHeaders) ->
-    maybe_serve_file(directory_index(FullPath), ExtraHeaders);
+maybe_redirect([], FullPath, ExtraHeaders, {?MODULE, [_Socket, _Method, _RawPath, _Version, _Headers]}=THIS) ->
+    maybe_serve_file(directory_index(FullPath), ExtraHeaders, THIS);
 
-maybe_redirect(RelPath, FullPath, ExtraHeaders) ->
+maybe_redirect(RelPath, FullPath, ExtraHeaders,
+               {?MODULE, [_Socket, _Method, _RawPath, _Version, Headers]}=THIS) ->
     case string:right(RelPath, 1) of
         "/" ->
-            maybe_serve_file(directory_index(FullPath), ExtraHeaders);
+            maybe_serve_file(directory_index(FullPath), ExtraHeaders, THIS);
         _   ->
             Host = mochiweb_headers:get_value("host", Headers),
             Location = "http://" ++ Host  ++ "/" ++ RelPath ++ "/",
@@ -596,16 +617,16 @@ maybe_redirect(RelPath, FullPath, ExtraHeaders) ->
             "<p>The document has moved <a href=\"">>,
             Bottom = <<">here</a>.</p></body></html>\n">>,
             Body = <<Top/binary, LocationBin/binary, Bottom/binary>>,
-            respond({301, MoreHeaders, Body})
+            respond({301, MoreHeaders, Body}, THIS)
     end.
 
-maybe_serve_file(File, ExtraHeaders) ->
-    case read_file_info(File) of
+maybe_serve_file(File, ExtraHeaders, {?MODULE, [_Socket, _Method, _RawPath, _Version, _Headers]}=THIS) ->
+    case file:read_file_info(File) of
         {ok, FileInfo} ->
-            LastModified = couch_util:rfc1123_date(FileInfo#file_info.mtime),
-            case get_header_value("if-modified-since") of
+            LastModified = httpd_util:rfc1123_date(FileInfo#file_info.mtime),
+            case get_header_value("if-modified-since", THIS) of
                 LastModified ->
-                    respond({304, ExtraHeaders, ""});
+                    respond({304, ExtraHeaders, ""}, THIS);
                 _ ->
                     case file:open(File, [raw, binary]) of
                         {ok, IoDevice} ->
@@ -613,39 +634,20 @@ maybe_serve_file(File, ExtraHeaders) ->
                             Res = ok({ContentType,
                                       [{"last-modified", LastModified}
                                        | ExtraHeaders],
-                                      {file, IoDevice}}),
-                            file:close(IoDevice),
+                                      {file, IoDevice}}, THIS),
+                            ok = file:close(IoDevice),
                             Res;
                         _ ->
-                            not_found(ExtraHeaders)
+                            not_found(ExtraHeaders, THIS)
                     end
             end;
         {error, _} ->
-            not_found(ExtraHeaders)
-    end.
-
-read_file_info(File) ->
-    try
-        file:read_file_info(File, [{time, universal}])
-    catch error:undef ->
-        case file:read_file_info(File) of
-            {ok, FileInfo} ->
-                {ok, FileInfo#file_info{
-                       atime=to_universal(FileInfo#file_info.atime),
-                       mtime=to_universal(FileInfo#file_info.mtime),
-                       ctime=to_universal(FileInfo#file_info.ctime)
-                      }};
-            Else ->
-                Else
-        end
+            not_found(ExtraHeaders, THIS)
     end.
 
-to_universal(LocalTime) ->
-    erlang:localtime_to_universaltime(LocalTime).
-
 server_headers() ->
     [{"Server", "MochiWeb/1.0 (" ++ ?QUIP ++ ")"},
-     {"Date", couch_util:rfc1123_date()}].
+     {"Date", httpd_util:rfc1123_date()}].
 
 make_code(X) when is_integer(X) ->
     [integer_to_list(X), [" " | httpd_util:reason_phrase(X)]];
@@ -688,7 +690,7 @@ range_parts(Body0, Ranges) ->
         end,
     {lists:foldr(F, [], Ranges), Size}.
 
-%% @spec accepted_encodings([encoding()]) -> [encoding()] | bad_accept_encoding_value
+%% @spec accepted_encodings([encoding()], request()) -> [encoding()] | bad_accept_encoding_value
 %% @type encoding() = string().
 %%
 %% @doc Returns a list of encodings accepted by a request. Encodings that are
@@ -712,8 +714,8 @@ range_parts(Body0, Ranges) ->
 %%         accepted_encodings(["gzip", "deflate", "identity"]) ->
 %%            ["deflate", "gzip", "identity"]
 %%
-accepted_encodings(SupportedEncodings) ->
-    AcceptEncodingHeader = case get_header_value("Accept-Encoding") of
+accepted_encodings(SupportedEncodings, {?MODULE, [_Socket, _Method, _RawPath, _Version, _Headers]}=THIS) ->
+    AcceptEncodingHeader = case get_header_value("Accept-Encoding", THIS) of
         undefined ->
             "";
         Value ->
@@ -728,7 +730,7 @@ accepted_encodings(SupportedEncodings) ->
             )
     end.
 
-%% @spec accepts_content_type(string() | binary()) -> boolean() | bad_accept_header
+%% @spec accepts_content_type(string() | binary(), request()) -> boolean() | bad_accept_header
 %%
 %% @doc Determines whether a request accepts a given media type by analyzing its
 %%      "Accept" header.
@@ -750,16 +752,9 @@ accepted_encodings(SupportedEncodings) ->
 %%      5) For an "Accept" header with value "text/*; q=0.0, */*":
 %%         accepts_content_type("text/plain") -> false
 %%
-accepts_content_type(ContentType) when is_binary(ContentType) ->
-    accepts_content_type(binary_to_list(ContentType));
-accepts_content_type(ContentType1) ->
+accepts_content_type(ContentType1, {?MODULE, [_Socket, _Method, _RawPath, _Version, _Headers]}=THIS) ->
     ContentType = re:replace(ContentType1, "\\s", "", [global, {return, list}]),
-    AcceptHeader = case get_header_value("Accept") of
-        undefined ->
-            "*/*";
-        Value ->
-            Value
-    end,
+    AcceptHeader = accept_header(THIS),
     case mochiweb_util:parse_qvalues(AcceptHeader) of
         invalid_qvalue_string ->
             bad_accept_header;
@@ -780,9 +775,83 @@ accepts_content_type(ContentType1) ->
             (not lists:member({SuperType, 0.0}, QList))
     end.
 
+%% @spec accepted_content_types([string() | binary()], request()) -> [string()] | bad_accept_header
+%%
+%% @doc Filters which of the given media types this request accepts. This filtering
+%%      is performed by analyzing the "Accept" header. The returned list is sorted
+%%      according to the preferences specified in the "Accept" header (higher Q values
+%%      first). If two or more types have the same preference (Q value), they're order
+%%      in the returned list is the same as they're order in the input list.
+%%
+%%      Examples
+%%
+%%      1) For a missing "Accept" header:
+%%         accepted_content_types(["text/html", "application/json"]) ->
+%%             ["text/html", "application/json"]
+%%
+%%      2) For an "Accept" header with value "text/html, application/*":
+%%         accepted_content_types(["application/json", "text/html"]) ->
+%%             ["application/json", "text/html"]
+%%
+%%      3) For an "Accept" header with value "text/html, */*; q=0.0":
+%%         accepted_content_types(["text/html", "application/json"]) ->
+%%             ["text/html"]
+%%
+%%      4) For an "Accept" header with value "text/html; q=0.5, */*; q=0.1":
+%%         accepts_content_types(["application/json", "text/html"]) ->
+%%             ["text/html", "application/json"]
+%%
+accepted_content_types(Types1, {?MODULE, [_Socket, _Method, _RawPath, _Version, _Headers]}=THIS) ->
+    Types = lists:map(
+        fun(T) -> re:replace(T, "\\s", "", [global, {return, list}]) end,
+        Types1),
+    AcceptHeader = accept_header(THIS),
+    case mochiweb_util:parse_qvalues(AcceptHeader) of
+        invalid_qvalue_string ->
+            bad_accept_header;
+        QList ->
+            TypesQ = lists:foldr(
+                fun(T, Acc) ->
+                    case proplists:get_value(T, QList) of
+                        undefined ->
+                            [MainType, _SubType] = string:tokens(T, "/"),
+                            case proplists:get_value(MainType ++ "/*", QList) of
+                                undefined ->
+                                    case proplists:get_value("*/*", QList) of
+                                        Q when is_float(Q), Q > 0.0 ->
+                                            [{Q, T} | Acc];
+                                        _ ->
+                                            Acc
+                                    end;
+                                Q when Q > 0.0 ->
+                                    [{Q, T} | Acc];
+                                _ ->
+                                    Acc
+                            end;
+                        Q when Q > 0.0 ->
+                            [{Q, T} | Acc];
+                        _ ->
+                            Acc
+                    end
+                end,
+                [], Types),
+            % Note: Stable sort. If 2 types have the same Q value we leave them in the
+            % same order as in the input list.
+            SortFun = fun({Q1, _}, {Q2, _}) -> Q1 >= Q2 end,
+            [Type || {_Q, Type} <- lists:sort(SortFun, TypesQ)]
+    end.
+
+accept_header({?MODULE, [_Socket, _Method, _RawPath, _Version, _Headers]}=THIS) ->
+    case get_header_value("Accept", THIS) of
+        undefined ->
+            "*/*";
+        Value ->
+            Value
+    end.
+
 %%
 %% Tests
 %%
--include_lib("eunit/include/eunit.hrl").
 -ifdef(TEST).
+-include_lib("eunit/include/eunit.hrl").
 -endif.

http://git-wip-us.apache.org/repos/asf/couchdb/blob/cbb8a550/src/mochiweb/mochiweb_request_tests.erl
----------------------------------------------------------------------
diff --git a/src/mochiweb/mochiweb_request_tests.erl b/src/mochiweb/mochiweb_request_tests.erl
index b61a583..b40c867 100644
--- a/src/mochiweb/mochiweb_request_tests.erl
+++ b/src/mochiweb/mochiweb_request_tests.erl
@@ -1,12 +1,13 @@
 -module(mochiweb_request_tests).
 
--include_lib("eunit/include/eunit.hrl").
 -ifdef(TEST).
+-include_lib("eunit/include/eunit.hrl").
 
 accepts_content_type_test() ->
     Req1 = mochiweb_request:new(nil, 'GET', "/foo", {1, 1},
         mochiweb_headers:make([{"Accept", "multipart/related"}])),
     ?assertEqual(true, Req1:accepts_content_type("multipart/related")),
+    ?assertEqual(true, Req1:accepts_content_type(<<"multipart/related">>)),
 
     Req2 = mochiweb_request:new(nil, 'GET', "/foo", {1, 1},
         mochiweb_headers:make([{"Accept", "text/html"}])),
@@ -60,4 +61,122 @@ accepts_content_type_test() ->
         mochiweb_headers:make([{"Accept", "text/html;level=1;q=0.1, text/html"}])),
     ?assertEqual(true, Req14:accepts_content_type("text/html; level=1")).
 
+accepted_encodings_test() ->
+    Req1 = mochiweb_request:new(nil, 'GET', "/foo", {1, 1},
+                                mochiweb_headers:make([])),
+    ?assertEqual(["identity"],
+                 Req1:accepted_encodings(["gzip", "identity"])),
+
+    Req2 = mochiweb_request:new(nil, 'GET', "/foo", {1, 1},
+        mochiweb_headers:make([{"Accept-Encoding", "gzip, deflate"}])),
+    ?assertEqual(["gzip", "identity"],
+                 Req2:accepted_encodings(["gzip", "identity"])),
+
+    Req3 = mochiweb_request:new(nil, 'GET', "/foo", {1, 1},
+        mochiweb_headers:make([{"Accept-Encoding", "gzip;q=0.5, deflate"}])),
+    ?assertEqual(["deflate", "gzip", "identity"],
+                 Req3:accepted_encodings(["gzip", "deflate", "identity"])),
+
+    Req4 = mochiweb_request:new(nil, 'GET', "/foo", {1, 1},
+        mochiweb_headers:make([{"Accept-Encoding", "identity, *;q=0"}])),
+    ?assertEqual(["identity"],
+                 Req4:accepted_encodings(["gzip", "deflate", "identity"])),
+
+    Req5 = mochiweb_request:new(nil, 'GET', "/foo", {1, 1},
+        mochiweb_headers:make([{"Accept-Encoding", "gzip; q=0.1, *;q=0"}])),
+    ?assertEqual(["gzip"],
+                 Req5:accepted_encodings(["gzip", "deflate", "identity"])),
+
+    Req6 = mochiweb_request:new(nil, 'GET', "/foo", {1, 1},
+        mochiweb_headers:make([{"Accept-Encoding", "gzip; q=, *;q=0"}])),
+    ?assertEqual(bad_accept_encoding_value,
+                 Req6:accepted_encodings(["gzip", "deflate", "identity"])),
+
+    Req7 = mochiweb_request:new(nil, 'GET', "/foo", {1, 1},
+        mochiweb_headers:make([{"Accept-Encoding", "gzip;q=2.0, *;q=0"}])),
+    ?assertEqual(bad_accept_encoding_value,
+                 Req7:accepted_encodings(["gzip", "identity"])),
+
+    Req8 = mochiweb_request:new(nil, 'GET', "/foo", {1, 1},
+        mochiweb_headers:make([{"Accept-Encoding", "deflate, *;q=0.0"}])),
+    ?assertEqual([],
+                 Req8:accepted_encodings(["gzip", "identity"])).
+
+accepted_content_types_test() ->
+    Req1 = mochiweb_request:new(nil, 'GET', "/foo", {1, 1},
+        mochiweb_headers:make([{"Accept", "text/html"}])),
+    ?assertEqual(["text/html"],
+        Req1:accepted_content_types(["text/html", "application/json"])),
+
+    Req2 = mochiweb_request:new(nil, 'GET', "/foo", {1, 1},
+        mochiweb_headers:make([{"Accept", "text/html, */*;q=0"}])),
+    ?assertEqual(["text/html"],
+        Req2:accepted_content_types(["text/html", "application/json"])),
+
+    Req3 = mochiweb_request:new(nil, 'GET', "/foo", {1, 1},
+        mochiweb_headers:make([{"Accept", "text/*, */*;q=0"}])),
+    ?assertEqual(["text/html"],
+        Req3:accepted_content_types(["text/html", "application/json"])),
+
+    Req4 = mochiweb_request:new(nil, 'GET', "/foo", {1, 1},
+        mochiweb_headers:make([{"Accept", "text/*;q=0.8, */*;q=0.5"}])),
+    ?assertEqual(["text/html", "application/json"],
+        Req4:accepted_content_types(["application/json", "text/html"])),
+
+    Req5 = mochiweb_request:new(nil, 'GET', "/foo", {1, 1},
+        mochiweb_headers:make([{"Accept", "text/*;q=0.8, */*;q=0.5"}])),
+    ?assertEqual(["text/html", "application/json"],
+        Req5:accepted_content_types(["text/html", "application/json"])),
+
+    Req6 = mochiweb_request:new(nil, 'GET', "/foo", {1, 1},
+        mochiweb_headers:make([{"Accept", "text/*;q=0.5, */*;q=0.5"}])),
+    ?assertEqual(["application/json", "text/html"],
+        Req6:accepted_content_types(["application/json", "text/html"])),
+
+    Req7 = mochiweb_request:new(nil, 'GET', "/foo", {1, 1},
+        mochiweb_headers:make(
+            [{"Accept", "text/html;q=0.5, application/json;q=0.5"}])),
+    ?assertEqual(["application/json", "text/html"],
+        Req7:accepted_content_types(["application/json", "text/html"])),
+
+    Req8 = mochiweb_request:new(nil, 'GET', "/foo", {1, 1},
+        mochiweb_headers:make([{"Accept", "text/html"}])),
+    ?assertEqual([],
+        Req8:accepted_content_types(["application/json"])),
+
+    Req9 = mochiweb_request:new(nil, 'GET', "/foo", {1, 1},
+        mochiweb_headers:make([{"Accept", "text/*;q=0.9, text/html;q=0.5, */*;q=0.7"}])),
+    ?assertEqual(["application/json", "text/html"],
+        Req9:accepted_content_types(["text/html", "application/json"])).
+
+should_close_test() ->
+    F = fun (V, H) ->
+                (mochiweb_request:new(
+                   nil, 'GET', "/", V,
+                   mochiweb_headers:make(H)
+                  )):should_close()
+        end,
+    ?assertEqual(
+       true,
+       F({1, 1}, [{"Connection", "close"}])),
+    ?assertEqual(
+       true,
+       F({1, 0}, [{"Connection", "close"}])),
+    ?assertEqual(
+       true,
+       F({1, 1}, [{"Connection", "ClOSe"}])),
+    ?assertEqual(
+       false,
+       F({1, 1}, [{"Connection", "closer"}])),
+    ?assertEqual(
+       false,
+       F({1, 1}, [])),
+    ?assertEqual(
+       true,
+       F({1, 0}, [])),
+    ?assertEqual(
+       false,
+       F({1, 0}, [{"Connection", "Keep-Alive"}])),
+    ok.
+
 -endif.


[4/8] git commit: updated refs/heads/master to d751a39

Posted by dc...@apache.org.
COUCHDB-1696 import mochiweb from tag v2.4.2


Project: http://git-wip-us.apache.org/repos/asf/couchdb/repo
Commit: http://git-wip-us.apache.org/repos/asf/couchdb/commit/cbb8a550
Tree: http://git-wip-us.apache.org/repos/asf/couchdb/tree/cbb8a550
Diff: http://git-wip-us.apache.org/repos/asf/couchdb/diff/cbb8a550

Branch: refs/heads/master
Commit: cbb8a55082a19897e8d1350db5351bf41689185d
Parents: ae6f1eb
Author: Dave Cottlehuber <dc...@apache.org>
Authored: Tue Mar 12 20:11:08 2013 +0100
Committer: Dave Cottlehuber <dc...@apache.org>
Committed: Thu Apr 25 00:13:20 2013 +0200

----------------------------------------------------------------------
 src/mochiweb/mochifmt.erl               |    2 +-
 src/mochiweb/mochifmt_records.erl       |   14 +-
 src/mochiweb/mochifmt_std.erl           |   19 +-
 src/mochiweb/mochiglobal.erl            |    4 +-
 src/mochiweb/mochihex.erl               |    5 +-
 src/mochiweb/mochijson.erl              |    4 +-
 src/mochiweb/mochijson2.erl             |   64 +-
 src/mochiweb/mochilists.erl             |    2 +-
 src/mochiweb/mochilogfile2.erl          |    2 +-
 src/mochiweb/mochinum.erl               |    2 +-
 src/mochiweb/mochitemp.erl              |    3 +-
 src/mochiweb/mochiutf8.erl              |    7 +-
 src/mochiweb/mochiweb.app.src           |    6 +-
 src/mochiweb/mochiweb.erl               |   46 +-
 src/mochiweb/mochiweb_acceptor.erl      |   11 +-
 src/mochiweb/mochiweb_charref.erl       | 2387 +++++++++++++++++++++++---
 src/mochiweb/mochiweb_cookies.erl       |   42 +-
 src/mochiweb/mochiweb_cover.erl         |    2 +-
 src/mochiweb/mochiweb_echo.erl          |   15 +-
 src/mochiweb/mochiweb_headers.erl       |  129 ++-
 src/mochiweb/mochiweb_html.erl          |  132 ++-
 src/mochiweb/mochiweb_http.erl          |  108 +-
 src/mochiweb/mochiweb_io.erl            |    5 +-
 src/mochiweb/mochiweb_mime.erl          |  399 ++++-
 src/mochiweb/mochiweb_multipart.erl     |   98 +-
 src/mochiweb/mochiweb_request.erl       |  477 +++---
 src/mochiweb/mochiweb_request_tests.erl |  121 ++-
 src/mochiweb/mochiweb_response.erl      |   44 +-
 src/mochiweb/mochiweb_socket.erl        |   15 +-
 src/mochiweb/mochiweb_socket_server.erl |   80 +-
 src/mochiweb/mochiweb_util.erl          |   16 +-
 src/mochiweb/reloader.erl               |    4 +-
 32 files changed, 3460 insertions(+), 805 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/couchdb/blob/cbb8a550/src/mochiweb/mochifmt.erl
----------------------------------------------------------------------
diff --git a/src/mochiweb/mochifmt.erl b/src/mochiweb/mochifmt.erl
index 5bc6b9c..fc95e4f 100644
--- a/src/mochiweb/mochifmt.erl
+++ b/src/mochiweb/mochifmt.erl
@@ -369,8 +369,8 @@ parse_std_conversion([Type], Acc) ->
 %%
 %% Tests
 %%
--include_lib("eunit/include/eunit.hrl").
 -ifdef(TEST).
+-include_lib("eunit/include/eunit.hrl").
 
 tokenize_test() ->
     {?MODULE, [{raw, "ABC"}]} = tokenize("ABC"),

http://git-wip-us.apache.org/repos/asf/couchdb/blob/cbb8a550/src/mochiweb/mochifmt_records.erl
----------------------------------------------------------------------
diff --git a/src/mochiweb/mochifmt_records.erl b/src/mochiweb/mochifmt_records.erl
index 2326d1d..7d166ff 100644
--- a/src/mochiweb/mochifmt_records.erl
+++ b/src/mochiweb/mochifmt_records.erl
@@ -9,11 +9,15 @@
 %%    M:format("{0.bar}", [#rec{bar=foo}]).
 %%    foo
 
--module(mochifmt_records, [Recs]).
+-module(mochifmt_records).
 -author('bob@mochimedia.com').
--export([get_value/2]).
+-export([new/1, get_value/3]).
 
-get_value(Key, Rec) when is_tuple(Rec) and is_atom(element(1, Rec)) ->
+new([{_Rec, RecFields}]=Recs) when is_list(RecFields) ->
+    {?MODULE, Recs}.
+
+get_value(Key, Rec, {?MODULE, Recs})
+  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),
@@ -21,7 +25,7 @@ get_value(Key, Rec) when is_tuple(Rec) and is_atom(element(1, Rec)) ->
         end
     catch error:_ -> mochifmt:get_value(Key, Rec)
     end;
-get_value(Key, Args) ->
+get_value(Key, Args, {?MODULE, _Recs}) ->
     mochifmt:get_value(Key, Args).
 
 get_rec_index(Atom, [Atom | _], Index) ->
@@ -33,6 +37,6 @@ get_rec_index(Atom, [_ | Rest], Index) ->
 %%
 %% Tests
 %%
--include_lib("eunit/include/eunit.hrl").
 -ifdef(TEST).
+-include_lib("eunit/include/eunit.hrl").
 -endif.

http://git-wip-us.apache.org/repos/asf/couchdb/blob/cbb8a550/src/mochiweb/mochifmt_std.erl
----------------------------------------------------------------------
diff --git a/src/mochiweb/mochifmt_std.erl b/src/mochiweb/mochifmt_std.erl
index d4d74f6..ea68c4a 100644
--- a/src/mochiweb/mochifmt_std.erl
+++ b/src/mochiweb/mochifmt_std.erl
@@ -3,28 +3,31 @@
 
 %% @doc Template module for a mochifmt formatter.
 
--module(mochifmt_std, []).
+-module(mochifmt_std).
 -author('bob@mochimedia.com').
--export([format/2, get_value/2, format_field/2, get_field/2, convert_field/2]).
+-export([new/0, format/3, get_value/3, format_field/3, get_field/3, convert_field/3]).
 
-format(Format, Args) ->
+new() ->
+    {?MODULE}.
+
+format(Format, Args, {?MODULE}=THIS) ->
     mochifmt:format(Format, Args, THIS).
 
-get_field(Key, Args) ->
+get_field(Key, Args, {?MODULE}=THIS) ->
     mochifmt:get_field(Key, Args, THIS).
 
-convert_field(Key, Args) ->
+convert_field(Key, Args, {?MODULE}) ->
     mochifmt:convert_field(Key, Args).
 
-get_value(Key, Args) ->
+get_value(Key, Args, {?MODULE}) ->
     mochifmt:get_value(Key, Args).
 
-format_field(Arg, Format) ->
+format_field(Arg, Format, {?MODULE}=THIS) ->
     mochifmt:format_field(Arg, Format, THIS).
 
 %%
 %% Tests
 %%
--include_lib("eunit/include/eunit.hrl").
 -ifdef(TEST).
+-include_lib("eunit/include/eunit.hrl").
 -endif.

http://git-wip-us.apache.org/repos/asf/couchdb/blob/cbb8a550/src/mochiweb/mochiglobal.erl
----------------------------------------------------------------------
diff --git a/src/mochiweb/mochiglobal.erl b/src/mochiweb/mochiglobal.erl
index c740b87..ea645b0 100644
--- a/src/mochiweb/mochiglobal.erl
+++ b/src/mochiweb/mochiglobal.erl
@@ -30,7 +30,7 @@ put(K, V) ->
 put(_K, V, Mod) ->
     Bin = compile(Mod, V),
     code:purge(Mod),
-    code:load_binary(Mod, atom_to_list(Mod) ++ ".erl", Bin),
+    {module, Mod} = code:load_binary(Mod, atom_to_list(Mod) ++ ".erl", Bin),
     ok.
 
 -spec delete(atom()) -> boolean().
@@ -77,8 +77,8 @@ term_to_abstract(Module, Getter, T) ->
 %%
 %% Tests
 %%
--include_lib("eunit/include/eunit.hrl").
 -ifdef(TEST).
+-include_lib("eunit/include/eunit.hrl").
 get_put_delete_test() ->
     K = '$$test$$mochiglobal',
     delete(K),

http://git-wip-us.apache.org/repos/asf/couchdb/blob/cbb8a550/src/mochiweb/mochihex.erl
----------------------------------------------------------------------
diff --git a/src/mochiweb/mochihex.erl b/src/mochiweb/mochihex.erl
index 44a2aa7..796f3ad 100644
--- a/src/mochiweb/mochihex.erl
+++ b/src/mochiweb/mochihex.erl
@@ -8,9 +8,6 @@
 
 -export([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) ->
@@ -68,8 +65,8 @@ to_bin([C1, C2 | Rest], Acc) ->
 %%
 %% Tests
 %%
--include_lib("eunit/include/eunit.hrl").
 -ifdef(TEST).
+-include_lib("eunit/include/eunit.hrl").
 
 to_hex_test() ->
     "ff000ff1" = to_hex([255, 0, 15, 241]),

http://git-wip-us.apache.org/repos/asf/couchdb/blob/cbb8a550/src/mochiweb/mochijson.erl
----------------------------------------------------------------------
diff --git a/src/mochiweb/mochijson.erl b/src/mochiweb/mochijson.erl
index 2e3d145..d283189 100644
--- a/src/mochiweb/mochijson.erl
+++ b/src/mochiweb/mochijson.erl
@@ -15,8 +15,6 @@
 -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()]}
@@ -406,8 +404,8 @@ tokenize(L=[C | _], S) when C >= $0, C =< $9; C == $- ->
 %%
 %% Tests
 %%
--include_lib("eunit/include/eunit.hrl").
 -ifdef(TEST).
+-include_lib("eunit/include/eunit.hrl").
 
 %% testing constructs borrowed from the Yaws JSON implementation.
 

http://git-wip-us.apache.org/repos/asf/couchdb/blob/cbb8a550/src/mochiweb/mochijson2.erl
----------------------------------------------------------------------
diff --git a/src/mochiweb/mochijson2.erl b/src/mochiweb/mochijson2.erl
index bdf6d77..2b8d16e 100644
--- a/src/mochiweb/mochijson2.erl
+++ b/src/mochiweb/mochijson2.erl
@@ -40,9 +40,9 @@
 -module(mochijson2).
 -author('bob@mochimedia.com').
 -export([encoder/1, encode/1]).
--export([decoder/1, decode/1]).
+-export([decoder/1, decode/1, decode/2]).
 
-% This is a macro to placate syntax highlighters..
+%% 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}).
@@ -64,15 +64,14 @@
 -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_eep18_object() = {[{json_string(), json_term()}]}
 %% @type json_iolist() = {json, iolist()}
 %% @type json_term() = json_string() | json_number() | json_array() |
-%%                     json_object() | json_iolist()
+%%                     json_object() | json_eep18_object() | json_iolist()
 
 -record(encoder, {handler=null,
                   utf8=false}).
@@ -102,6 +101,14 @@ decoder(Options) ->
     State = parse_decoder_options(Options, #decoder{}),
     fun (O) -> json_decode(O, State) end.
 
+%% @spec decode(iolist(), [{format, proplist | eep18 | struct}]) -> json_term()
+%% @doc Decode the given iolist to Erlang terms using the given object format
+%%      for decoding, where proplist returns JSON objects as [{binary(), json_term()}]
+%%      proplists, eep18 returns JSON objects as {[binary(), json_term()]}, and struct
+%%      returns them as-is.
+decode(S, Options) ->
+    json_decode(S, parse_decoder_options(Options, #decoder{})).
+
 %% @spec decode(iolist()) -> json_term()
 %% @doc Decode the given iolist to Erlang terms.
 decode(S) ->
@@ -119,7 +126,10 @@ parse_encoder_options([{utf8, Switch} | Rest], State) ->
 parse_decoder_options([], State) ->
     State;
 parse_decoder_options([{object_hook, Hook} | Rest], State) ->
-    parse_decoder_options(Rest, State#decoder{object_hook=Hook}).
+    parse_decoder_options(Rest, State#decoder{object_hook=Hook});
+parse_decoder_options([{format, Format} | Rest], State)
+  when Format =:= struct orelse Format =:= eep18 orelse Format =:= proplist ->
+    parse_decoder_options(Rest, State#decoder{object_hook=Format}).
 
 json_encode(true, _State) ->
     <<"true">>;
@@ -139,6 +149,10 @@ json_encode([{K, _}|_] = Props, State) when (K =/= struct andalso
     json_encode_proplist(Props, State);
 json_encode({struct, Props}, State) when is_list(Props) ->
     json_encode_proplist(Props, State);
+json_encode({Props}, State) when is_list(Props) ->
+    json_encode_proplist(Props, State);
+json_encode({}, State) ->
+    json_encode_proplist([], State);
 json_encode(Array, State) when is_list(Array) ->
     json_encode_array(Array, State);
 json_encode({array, Array}, State) when is_list(Array) ->
@@ -318,8 +332,12 @@ decode1(B, S=#decoder{state=null}) ->
             decode_object(B, S1)
     end.
 
-make_object(V, #decoder{object_hook=null}) ->
+make_object(V, #decoder{object_hook=N}) when N =:= null orelse N =:= struct ->
     V;
+make_object({struct, P}, #decoder{object_hook=eep18}) ->
+    {P};
+make_object({struct, P}, #decoder{object_hook=proplist}) ->
+    P;
 make_object(V, #decoder{object_hook=Hook}) ->
     Hook(V).
 
@@ -559,8 +577,8 @@ tokenize(B, S=#decoder{offset=O}) ->
 %%
 %% Tests
 %%
--include_lib("eunit/include/eunit.hrl").
 -ifdef(TEST).
+-include_lib("eunit/include/eunit.hrl").
 
 
 %% testing constructs borrowed from the Yaws JSON implementation.
@@ -838,12 +856,34 @@ float_test() ->
 
 handler_test() ->
     ?assertEqual(
-       {'EXIT',{json_encode,{bad_term,{}}}},
-       catch encode({})),
-    F = fun ({}) -> [] end,
+       {'EXIT',{json_encode,{bad_term,{x,y}}}},
+       catch encode({x,y})),
+    F = fun ({x,y}) -> [] end,
     ?assertEqual(
        <<"[]">>,
-       iolist_to_binary((encoder([{handler, F}]))({}))),
+       iolist_to_binary((encoder([{handler, F}]))({x, y}))),
     ok.
 
+encode_empty_test_() ->
+    [{A, ?_assertEqual(<<"{}">>, iolist_to_binary(encode(B)))}
+     || {A, B} <- [{"eep18 {}", {}},
+                   {"eep18 {[]}", {[]}},
+                   {"{struct, []}", {struct, []}}]].
+
+encode_test_() ->
+    P = [{<<"k">>, <<"v">>}],
+    JSON = iolist_to_binary(encode({struct, P})),
+    [{atom_to_list(F),
+      ?_assertEqual(JSON, iolist_to_binary(encode(decode(JSON, [{format, F}]))))}
+     || F <- [struct, eep18, proplist]].
+
+format_test_() ->
+    P = [{<<"k">>, <<"v">>}],
+    JSON = iolist_to_binary(encode({struct, P})),
+    [{atom_to_list(F),
+      ?_assertEqual(A, decode(JSON, [{format, F}]))}
+     || {F, A} <- [{struct, {struct, P}},
+                   {eep18, {P}},
+                   {proplist, P}]].
+
 -endif.

http://git-wip-us.apache.org/repos/asf/couchdb/blob/cbb8a550/src/mochiweb/mochilists.erl
----------------------------------------------------------------------
diff --git a/src/mochiweb/mochilists.erl b/src/mochiweb/mochilists.erl
index 8981e7b..d93b241 100644
--- a/src/mochiweb/mochilists.erl
+++ b/src/mochiweb/mochilists.erl
@@ -55,8 +55,8 @@ get_value(Key, Proplist, Default) ->
 %%
 %% Tests
 %%
--include_lib("eunit/include/eunit.hrl").
 -ifdef(TEST).
+-include_lib("eunit/include/eunit.hrl").
 
 set_defaults_test() ->
     ?assertEqual(

http://git-wip-us.apache.org/repos/asf/couchdb/blob/cbb8a550/src/mochiweb/mochilogfile2.erl
----------------------------------------------------------------------
diff --git a/src/mochiweb/mochilogfile2.erl b/src/mochiweb/mochilogfile2.erl
index c34ee73..b4a7e3c 100644
--- a/src/mochiweb/mochilogfile2.erl
+++ b/src/mochiweb/mochilogfile2.erl
@@ -57,8 +57,8 @@ find_last_newline(FD, Location) ->
 %%
 %% Tests
 %%
--include_lib("eunit/include/eunit.hrl").
 -ifdef(TEST).
+-include_lib("eunit/include/eunit.hrl").
 name_test() ->
     D = mochitemp:mkdtemp(),
     FileName = filename:join(D, "open_close_test.log"),

http://git-wip-us.apache.org/repos/asf/couchdb/blob/cbb8a550/src/mochiweb/mochinum.erl
----------------------------------------------------------------------
diff --git a/src/mochiweb/mochinum.erl b/src/mochiweb/mochinum.erl
index 3c96b13..c52b15c 100644
--- a/src/mochiweb/mochinum.erl
+++ b/src/mochiweb/mochinum.erl
@@ -243,8 +243,8 @@ frexp_int(F) ->
 %%
 %% Tests
 %%
--include_lib("eunit/include/eunit.hrl").
 -ifdef(TEST).
+-include_lib("eunit/include/eunit.hrl").
 
 int_ceil_test() ->
     ?assertEqual(1, int_ceil(0.0001)),

http://git-wip-us.apache.org/repos/asf/couchdb/blob/cbb8a550/src/mochiweb/mochitemp.erl
----------------------------------------------------------------------
diff --git a/src/mochiweb/mochitemp.erl b/src/mochiweb/mochitemp.erl
index bb23d2a..dda7863 100644
--- a/src/mochiweb/mochitemp.erl
+++ b/src/mochiweb/mochitemp.erl
@@ -135,8 +135,9 @@ normalize_dir(L) ->
 %%
 %% Tests
 %%
--include_lib("eunit/include/eunit.hrl").
 -ifdef(TEST).
+-include_lib("eunit/include/eunit.hrl").
+
 pushenv(L) ->
     [{K, os:getenv(K)} || K <- L].
 popenv(L) ->

http://git-wip-us.apache.org/repos/asf/couchdb/blob/cbb8a550/src/mochiweb/mochiutf8.erl
----------------------------------------------------------------------
diff --git a/src/mochiweb/mochiutf8.erl b/src/mochiweb/mochiutf8.erl
index 206e118..28f28c1 100644
--- a/src/mochiweb/mochiutf8.erl
+++ b/src/mochiweb/mochiutf8.erl
@@ -5,8 +5,9 @@
 %%      invalid bytes.
 
 -module(mochiutf8).
--export([valid_utf8_bytes/1, codepoint_to_bytes/1, bytes_to_codepoints/1]).
--export([bytes_foldl/3, codepoint_foldl/3, read_codepoint/1, len/1]).
+-export([valid_utf8_bytes/1, codepoint_to_bytes/1, codepoints_to_bytes/1]).
+-export([bytes_to_codepoints/1, bytes_foldl/3, codepoint_foldl/3]).
+-export([read_codepoint/1, len/1]).
 
 %% External API
 
@@ -192,8 +193,8 @@ invalid_utf8_indexes(<<>>, _N, Acc) ->
 %%
 %% Tests
 %%
--include_lib("eunit/include/eunit.hrl").
 -ifdef(TEST).
+-include_lib("eunit/include/eunit.hrl").
 
 binary_skip_bytes_test() ->
     ?assertEqual(<<"foo">>,

http://git-wip-us.apache.org/repos/asf/couchdb/blob/cbb8a550/src/mochiweb/mochiweb.app.src
----------------------------------------------------------------------
diff --git a/src/mochiweb/mochiweb.app.src b/src/mochiweb/mochiweb.app.src
index 37a21fb..baced90 100644
--- a/src/mochiweb/mochiweb.app.src
+++ b/src/mochiweb/mochiweb.app.src
@@ -1,9 +1,9 @@
 %% This is generated from src/mochiweb.app.src
 {application, mochiweb,
  [{description, "MochiMedia Web Server"},
-  {vsn, "1.4.1"},
+  {vsn, "2.4.2"},
   {modules, []},
   {registered, []},
-  {mod, {mochiweb_app, []}},
   {env, []},
-  {applications, [kernel, stdlib, crypto, inets]}]}.
+  {applications, [kernel, stdlib, crypto, inets, ssl, xmerl,
+                  compiler, syntax_tools]}]}.

http://git-wip-us.apache.org/repos/asf/couchdb/blob/cbb8a550/src/mochiweb/mochiweb.erl
----------------------------------------------------------------------
diff --git a/src/mochiweb/mochiweb.erl b/src/mochiweb/mochiweb.erl
index 3118028..250beb5 100644
--- a/src/mochiweb/mochiweb.erl
+++ b/src/mochiweb/mochiweb.erl
@@ -6,22 +6,9 @@
 -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]).
-
-%% @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.
+-export([ensure_started/1]).
 
 reload() ->
     [c:l(Module) || Module <- all_loaded()].
@@ -78,8 +65,8 @@ new_response({Request, Code, Headers}) ->
                           Code,
                           mochiweb_headers:make(Headers)).
 
-%% Internal API
-
+%% @spec ensure_started(App::atom()) -> ok
+%% @doc Start the given App if it has not been started already.
 ensure_started(App) ->
     case application:start(App) of
         ok ->
@@ -88,12 +75,11 @@ ensure_started(App) ->
             ok
     end.
 
-
 %%
 %% Tests
 %%
--include_lib("eunit/include/eunit.hrl").
 -ifdef(TEST).
+-include_lib("eunit/include/eunit.hrl").
 
 -record(treq, {path, body= <<>>, xreply= <<>>}).
 
@@ -112,7 +98,7 @@ with_server(Transport, ServerFun, ClientFun) ->
         ssl ->
             ServerOpts0 ++ [{ssl, true}, {ssl_opts, ssl_cert_opts()}]
     end,
-    {ok, Server} = mochiweb_http:start(ServerOpts),
+    {ok, Server} = mochiweb_http:start_link(ServerOpts),
     Port = mochiweb_socket_server:get(Server, port),
     Res = (catch ClientFun(Transport, Port)),
     mochiweb_http:stop(Server),
@@ -123,6 +109,8 @@ request_test() ->
     "/foo/bar/baz wibble quux" = R:get(path),
     ok.
 
+-define(LARGE_TIMEOUT, 60).
+
 single_http_GET_test() ->
     do_GET(plain, 1).
 
@@ -135,11 +123,13 @@ multiple_http_GET_test() ->
 multiple_https_GET_test() ->
     do_GET(ssl, 3).
 
-hundred_http_GET_test() ->
-    do_GET(plain, 100).
+hundred_http_GET_test_() -> % note the underscore
+    {timeout, ?LARGE_TIMEOUT,
+     fun() -> ?assertEqual(ok, do_GET(plain,100)) end}.
 
-hundred_https_GET_test() ->
-    do_GET(ssl, 100).
+hundred_https_GET_test_() -> % note the underscore
+    {timeout, ?LARGE_TIMEOUT,
+     fun() -> ?assertEqual(ok, do_GET(ssl,100)) end}.
 
 single_128_http_POST_test() ->
     do_POST(plain, 128, 1).
@@ -165,11 +155,13 @@ multiple_100k_http_POST_test() ->
 multiple_100K_https_POST_test() ->
     do_POST(ssl, 102400, 3).
 
-hundred_128_http_POST_test() ->
-    do_POST(plain, 128, 100).
+hundred_128_http_POST_test_() -> % note the underscore
+    {timeout, ?LARGE_TIMEOUT,
+     fun() -> ?assertEqual(ok, do_POST(plain, 128, 100)) end}.
 
-hundred_128_https_POST_test() ->
-    do_POST(ssl, 128, 100).
+hundred_128_https_POST_test_() -> % note the underscore
+    {timeout, ?LARGE_TIMEOUT,
+     fun() -> ?assertEqual(ok, do_POST(ssl, 128, 100)) end}.
 
 do_GET(Transport, Times) ->
     PathPrefix = "/whatever/",

http://git-wip-us.apache.org/repos/asf/couchdb/blob/cbb8a550/src/mochiweb/mochiweb_acceptor.erl
----------------------------------------------------------------------
diff --git a/src/mochiweb/mochiweb_acceptor.erl b/src/mochiweb/mochiweb_acceptor.erl
index 893f99b..efedfbd 100644
--- a/src/mochiweb/mochiweb_acceptor.erl
+++ b/src/mochiweb/mochiweb_acceptor.erl
@@ -18,14 +18,13 @@ init(Server, Listen, Loop) ->
     case catch mochiweb_socket:accept(Listen) of
         {ok, Socket} ->
             gen_server:cast(Server, {accepted, self(), timer:now_diff(now(), T1)}),
-            case mochiweb_socket:after_accept(Socket) of
-                ok -> call_loop(Loop, Socket);
-                {error, _} -> exit(normal)
-            end;
+            call_loop(Loop, Socket);
         {error, closed} ->
             exit(normal);
         {error, timeout} ->
             init(Server, Listen, Loop);
+        {error, esslaccept} ->
+            exit(normal);
         Other ->
             error_logger:error_report(
               [{application, mochiweb},
@@ -36,6 +35,8 @@ init(Server, Listen, Loop) ->
 
 call_loop({M, F}, Socket) ->
     M:F(Socket);
+call_loop({M, F, [A1]}, Socket) ->
+    M:F(Socket, A1);
 call_loop({M, F, A}, Socket) ->
     erlang:apply(M, F, [Socket | A]);
 call_loop(Loop, Socket) ->
@@ -44,6 +45,6 @@ call_loop(Loop, Socket) ->
 %%
 %% Tests
 %%
--include_lib("eunit/include/eunit.hrl").
 -ifdef(TEST).
+-include_lib("eunit/include/eunit.hrl").
 -endif.


[3/8] COUCHDB-1696 import mochiweb from tag v2.4.2

Posted by dc...@apache.org.
http://git-wip-us.apache.org/repos/asf/couchdb/blob/cbb8a550/src/mochiweb/mochiweb_charref.erl
----------------------------------------------------------------------
diff --git a/src/mochiweb/mochiweb_charref.erl b/src/mochiweb/mochiweb_charref.erl
index 99cd550..193c7c7 100644
--- a/src/mochiweb/mochiweb_charref.erl
+++ b/src/mochiweb/mochiweb_charref.erl
@@ -1,17 +1,17 @@
 %% @author Bob Ippolito <bo...@mochimedia.com>
 %% @copyright 2007 Mochi Media, Inc.
 
-%% @doc Converts HTML 4 charrefs and entities to codepoints.
+%% @doc Converts HTML 5 charrefs and entities to codepoints (or lists of code points).
 -module(mochiweb_charref).
 -export([charref/1]).
 
 %% 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.
+-spec charref(binary() | string()) -> integer() | [integer()] | undefined.
 charref(B) when is_binary(B) ->
     charref(binary_to_list(B));
 charref([$#, C | L]) when C =:= $x orelse C =:= $X ->
@@ -29,266 +29,2141 @@ charref(L) ->
 
 %% 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.
+%% [2011-10-14] Generated from:
+%% http://www.w3.org/TR/html5/named-character-references.html
 
+entity("AElig") -> 16#000C6;
+entity("AMP") -> 16#00026;
+entity("Aacute") -> 16#000C1;
+entity("Abreve") -> 16#00102;
+entity("Acirc") -> 16#000C2;
+entity("Acy") -> 16#00410;
+entity("Afr") -> 16#1D504;
+entity("Agrave") -> 16#000C0;
+entity("Alpha") -> 16#00391;
+entity("Amacr") -> 16#00100;
+entity("And") -> 16#02A53;
+entity("Aogon") -> 16#00104;
+entity("Aopf") -> 16#1D538;
+entity("ApplyFunction") -> 16#02061;
+entity("Aring") -> 16#000C5;
+entity("Ascr") -> 16#1D49C;
+entity("Assign") -> 16#02254;
+entity("Atilde") -> 16#000C3;
+entity("Auml") -> 16#000C4;
+entity("Backslash") -> 16#02216;
+entity("Barv") -> 16#02AE7;
+entity("Barwed") -> 16#02306;
+entity("Bcy") -> 16#00411;
+entity("Because") -> 16#02235;
+entity("Bernoullis") -> 16#0212C;
+entity("Beta") -> 16#00392;
+entity("Bfr") -> 16#1D505;
+entity("Bopf") -> 16#1D539;
+entity("Breve") -> 16#002D8;
+entity("Bscr") -> 16#0212C;
+entity("Bumpeq") -> 16#0224E;
+entity("CHcy") -> 16#00427;
+entity("COPY") -> 16#000A9;
+entity("Cacute") -> 16#00106;
+entity("Cap") -> 16#022D2;
+entity("CapitalDifferentialD") -> 16#02145;
+entity("Cayleys") -> 16#0212D;
+entity("Ccaron") -> 16#0010C;
+entity("Ccedil") -> 16#000C7;
+entity("Ccirc") -> 16#00108;
+entity("Cconint") -> 16#02230;
+entity("Cdot") -> 16#0010A;
+entity("Cedilla") -> 16#000B8;
+entity("CenterDot") -> 16#000B7;
+entity("Cfr") -> 16#0212D;
+entity("Chi") -> 16#003A7;
+entity("CircleDot") -> 16#02299;
+entity("CircleMinus") -> 16#02296;
+entity("CirclePlus") -> 16#02295;
+entity("CircleTimes") -> 16#02297;
+entity("ClockwiseContourIntegral") -> 16#02232;
+entity("CloseCurlyDoubleQuote") -> 16#0201D;
+entity("CloseCurlyQuote") -> 16#02019;
+entity("Colon") -> 16#02237;
+entity("Colone") -> 16#02A74;
+entity("Congruent") -> 16#02261;
+entity("Conint") -> 16#0222F;
+entity("ContourIntegral") -> 16#0222E;
+entity("Copf") -> 16#02102;
+entity("Coproduct") -> 16#02210;
+entity("CounterClockwiseContourIntegral") -> 16#02233;
+entity("Cross") -> 16#02A2F;
+entity("Cscr") -> 16#1D49E;
+entity("Cup") -> 16#022D3;
+entity("CupCap") -> 16#0224D;
+entity("DD") -> 16#02145;
+entity("DDotrahd") -> 16#02911;
+entity("DJcy") -> 16#00402;
+entity("DScy") -> 16#00405;
+entity("DZcy") -> 16#0040F;
+entity("Dagger") -> 16#02021;
+entity("Darr") -> 16#021A1;
+entity("Dashv") -> 16#02AE4;
+entity("Dcaron") -> 16#0010E;
+entity("Dcy") -> 16#00414;
+entity("Del") -> 16#02207;
+entity("Delta") -> 16#00394;
+entity("Dfr") -> 16#1D507;
+entity("DiacriticalAcute") -> 16#000B4;
+entity("DiacriticalDot") -> 16#002D9;
+entity("DiacriticalDoubleAcute") -> 16#002DD;
+entity("DiacriticalGrave") -> 16#00060;
+entity("DiacriticalTilde") -> 16#002DC;
+entity("Diamond") -> 16#022C4;
+entity("DifferentialD") -> 16#02146;
+entity("Dopf") -> 16#1D53B;
+entity("Dot") -> 16#000A8;
+entity("DotDot") -> 16#020DC;
+entity("DotEqual") -> 16#02250;
+entity("DoubleContourIntegral") -> 16#0222F;
+entity("DoubleDot") -> 16#000A8;
+entity("DoubleDownArrow") -> 16#021D3;
+entity("DoubleLeftArrow") -> 16#021D0;
+entity("DoubleLeftRightArrow") -> 16#021D4;
+entity("DoubleLeftTee") -> 16#02AE4;
+entity("DoubleLongLeftArrow") -> 16#027F8;
+entity("DoubleLongLeftRightArrow") -> 16#027FA;
+entity("DoubleLongRightArrow") -> 16#027F9;
+entity("DoubleRightArrow") -> 16#021D2;
+entity("DoubleRightTee") -> 16#022A8;
+entity("DoubleUpArrow") -> 16#021D1;
+entity("DoubleUpDownArrow") -> 16#021D5;
+entity("DoubleVerticalBar") -> 16#02225;
+entity("DownArrow") -> 16#02193;
+entity("DownArrowBar") -> 16#02913;
+entity("DownArrowUpArrow") -> 16#021F5;
+entity("DownBreve") -> 16#00311;
+entity("DownLeftRightVector") -> 16#02950;
+entity("DownLeftTeeVector") -> 16#0295E;
+entity("DownLeftVector") -> 16#021BD;
+entity("DownLeftVectorBar") -> 16#02956;
+entity("DownRightTeeVector") -> 16#0295F;
+entity("DownRightVector") -> 16#021C1;
+entity("DownRightVectorBar") -> 16#02957;
+entity("DownTee") -> 16#022A4;
+entity("DownTeeArrow") -> 16#021A7;
+entity("Downarrow") -> 16#021D3;
+entity("Dscr") -> 16#1D49F;
+entity("Dstrok") -> 16#00110;
+entity("ENG") -> 16#0014A;
+entity("ETH") -> 16#000D0;
+entity("Eacute") -> 16#000C9;
+entity("Ecaron") -> 16#0011A;
+entity("Ecirc") -> 16#000CA;
+entity("Ecy") -> 16#0042D;
+entity("Edot") -> 16#00116;
+entity("Efr") -> 16#1D508;
+entity("Egrave") -> 16#000C8;
+entity("Element") -> 16#02208;
+entity("Emacr") -> 16#00112;
+entity("EmptySmallSquare") -> 16#025FB;
+entity("EmptyVerySmallSquare") -> 16#025AB;
+entity("Eogon") -> 16#00118;
+entity("Eopf") -> 16#1D53C;
+entity("Epsilon") -> 16#00395;
+entity("Equal") -> 16#02A75;
+entity("EqualTilde") -> 16#02242;
+entity("Equilibrium") -> 16#021CC;
+entity("Escr") -> 16#02130;
+entity("Esim") -> 16#02A73;
+entity("Eta") -> 16#00397;
+entity("Euml") -> 16#000CB;
+entity("Exists") -> 16#02203;
+entity("ExponentialE") -> 16#02147;
+entity("Fcy") -> 16#00424;
+entity("Ffr") -> 16#1D509;
+entity("FilledSmallSquare") -> 16#025FC;
+entity("FilledVerySmallSquare") -> 16#025AA;
+entity("Fopf") -> 16#1D53D;
+entity("ForAll") -> 16#02200;
+entity("Fouriertrf") -> 16#02131;
+entity("Fscr") -> 16#02131;
+entity("GJcy") -> 16#00403;
+entity("GT") -> 16#0003E;
+entity("Gamma") -> 16#00393;
+entity("Gammad") -> 16#003DC;
+entity("Gbreve") -> 16#0011E;
+entity("Gcedil") -> 16#00122;
+entity("Gcirc") -> 16#0011C;
+entity("Gcy") -> 16#00413;
+entity("Gdot") -> 16#00120;
+entity("Gfr") -> 16#1D50A;
+entity("Gg") -> 16#022D9;
+entity("Gopf") -> 16#1D53E;
+entity("GreaterEqual") -> 16#02265;
+entity("GreaterEqualLess") -> 16#022DB;
+entity("GreaterFullEqual") -> 16#02267;
+entity("GreaterGreater") -> 16#02AA2;
+entity("GreaterLess") -> 16#02277;
+entity("GreaterSlantEqual") -> 16#02A7E;
+entity("GreaterTilde") -> 16#02273;
+entity("Gscr") -> 16#1D4A2;
+entity("Gt") -> 16#0226B;
+entity("HARDcy") -> 16#0042A;
+entity("Hacek") -> 16#002C7;
+entity("Hat") -> 16#0005E;
+entity("Hcirc") -> 16#00124;
+entity("Hfr") -> 16#0210C;
+entity("HilbertSpace") -> 16#0210B;
+entity("Hopf") -> 16#0210D;
+entity("HorizontalLine") -> 16#02500;
+entity("Hscr") -> 16#0210B;
+entity("Hstrok") -> 16#00126;
+entity("HumpDownHump") -> 16#0224E;
+entity("HumpEqual") -> 16#0224F;
+entity("IEcy") -> 16#00415;
+entity("IJlig") -> 16#00132;
+entity("IOcy") -> 16#00401;
+entity("Iacute") -> 16#000CD;
+entity("Icirc") -> 16#000CE;
+entity("Icy") -> 16#00418;
+entity("Idot") -> 16#00130;
+entity("Ifr") -> 16#02111;
+entity("Igrave") -> 16#000CC;
+entity("Im") -> 16#02111;
+entity("Imacr") -> 16#0012A;
+entity("ImaginaryI") -> 16#02148;
+entity("Implies") -> 16#021D2;
+entity("Int") -> 16#0222C;
+entity("Integral") -> 16#0222B;
+entity("Intersection") -> 16#022C2;
+entity("InvisibleComma") -> 16#02063;
+entity("InvisibleTimes") -> 16#02062;
+entity("Iogon") -> 16#0012E;
+entity("Iopf") -> 16#1D540;
+entity("Iota") -> 16#00399;
+entity("Iscr") -> 16#02110;
+entity("Itilde") -> 16#00128;
+entity("Iukcy") -> 16#00406;
+entity("Iuml") -> 16#000CF;
+entity("Jcirc") -> 16#00134;
+entity("Jcy") -> 16#00419;
+entity("Jfr") -> 16#1D50D;
+entity("Jopf") -> 16#1D541;
+entity("Jscr") -> 16#1D4A5;
+entity("Jsercy") -> 16#00408;
+entity("Jukcy") -> 16#00404;
+entity("KHcy") -> 16#00425;
+entity("KJcy") -> 16#0040C;
+entity("Kappa") -> 16#0039A;
+entity("Kcedil") -> 16#00136;
+entity("Kcy") -> 16#0041A;
+entity("Kfr") -> 16#1D50E;
+entity("Kopf") -> 16#1D542;
+entity("Kscr") -> 16#1D4A6;
+entity("LJcy") -> 16#00409;
+entity("LT") -> 16#0003C;
+entity("Lacute") -> 16#00139;
+entity("Lambda") -> 16#0039B;
+entity("Lang") -> 16#027EA;
+entity("Laplacetrf") -> 16#02112;
+entity("Larr") -> 16#0219E;
+entity("Lcaron") -> 16#0013D;
+entity("Lcedil") -> 16#0013B;
+entity("Lcy") -> 16#0041B;
+entity("LeftAngleBracket") -> 16#027E8;
+entity("LeftArrow") -> 16#02190;
+entity("LeftArrowBar") -> 16#021E4;
+entity("LeftArrowRightArrow") -> 16#021C6;
+entity("LeftCeiling") -> 16#02308;
+entity("LeftDoubleBracket") -> 16#027E6;
+entity("LeftDownTeeVector") -> 16#02961;
+entity("LeftDownVector") -> 16#021C3;
+entity("LeftDownVectorBar") -> 16#02959;
+entity("LeftFloor") -> 16#0230A;
+entity("LeftRightArrow") -> 16#02194;
+entity("LeftRightVector") -> 16#0294E;
+entity("LeftTee") -> 16#022A3;
+entity("LeftTeeArrow") -> 16#021A4;
+entity("LeftTeeVector") -> 16#0295A;
+entity("LeftTriangle") -> 16#022B2;
+entity("LeftTriangleBar") -> 16#029CF;
+entity("LeftTriangleEqual") -> 16#022B4;
+entity("LeftUpDownVector") -> 16#02951;
+entity("LeftUpTeeVector") -> 16#02960;
+entity("LeftUpVector") -> 16#021BF;
+entity("LeftUpVectorBar") -> 16#02958;
+entity("LeftVector") -> 16#021BC;
+entity("LeftVectorBar") -> 16#02952;
+entity("Leftarrow") -> 16#021D0;
+entity("Leftrightarrow") -> 16#021D4;
+entity("LessEqualGreater") -> 16#022DA;
+entity("LessFullEqual") -> 16#02266;
+entity("LessGreater") -> 16#02276;
+entity("LessLess") -> 16#02AA1;
+entity("LessSlantEqual") -> 16#02A7D;
+entity("LessTilde") -> 16#02272;
+entity("Lfr") -> 16#1D50F;
+entity("Ll") -> 16#022D8;
+entity("Lleftarrow") -> 16#021DA;
+entity("Lmidot") -> 16#0013F;
+entity("LongLeftArrow") -> 16#027F5;
+entity("LongLeftRightArrow") -> 16#027F7;
+entity("LongRightArrow") -> 16#027F6;
+entity("Longleftarrow") -> 16#027F8;
+entity("Longleftrightarrow") -> 16#027FA;
+entity("Longrightarrow") -> 16#027F9;
+entity("Lopf") -> 16#1D543;
+entity("LowerLeftArrow") -> 16#02199;
+entity("LowerRightArrow") -> 16#02198;
+entity("Lscr") -> 16#02112;
+entity("Lsh") -> 16#021B0;
+entity("Lstrok") -> 16#00141;
+entity("Lt") -> 16#0226A;
+entity("Map") -> 16#02905;
+entity("Mcy") -> 16#0041C;
+entity("MediumSpace") -> 16#0205F;
+entity("Mellintrf") -> 16#02133;
+entity("Mfr") -> 16#1D510;
+entity("MinusPlus") -> 16#02213;
+entity("Mopf") -> 16#1D544;
+entity("Mscr") -> 16#02133;
+entity("Mu") -> 16#0039C;
+entity("NJcy") -> 16#0040A;
+entity("Nacute") -> 16#00143;
+entity("Ncaron") -> 16#00147;
+entity("Ncedil") -> 16#00145;
+entity("Ncy") -> 16#0041D;
+entity("NegativeMediumSpace") -> 16#0200B;
+entity("NegativeThickSpace") -> 16#0200B;
+entity("NegativeThinSpace") -> 16#0200B;
+entity("NegativeVeryThinSpace") -> 16#0200B;
+entity("NestedGreaterGreater") -> 16#0226B;
+entity("NestedLessLess") -> 16#0226A;
+entity("NewLine") -> 16#0000A;
+entity("Nfr") -> 16#1D511;
+entity("NoBreak") -> 16#02060;
+entity("NonBreakingSpace") -> 16#000A0;
+entity("Nopf") -> 16#02115;
+entity("Not") -> 16#02AEC;
+entity("NotCongruent") -> 16#02262;
+entity("NotCupCap") -> 16#0226D;
+entity("NotDoubleVerticalBar") -> 16#02226;
+entity("NotElement") -> 16#02209;
+entity("NotEqual") -> 16#02260;
+entity("NotEqualTilde") -> [16#02242, 16#00338];
+entity("NotExists") -> 16#02204;
+entity("NotGreater") -> 16#0226F;
+entity("NotGreaterEqual") -> 16#02271;
+entity("NotGreaterFullEqual") -> [16#02267, 16#00338];
+entity("NotGreaterGreater") -> [16#0226B, 16#00338];
+entity("NotGreaterLess") -> 16#02279;
+entity("NotGreaterSlantEqual") -> [16#02A7E, 16#00338];
+entity("NotGreaterTilde") -> 16#02275;
+entity("NotHumpDownHump") -> [16#0224E, 16#00338];
+entity("NotHumpEqual") -> [16#0224F, 16#00338];
+entity("NotLeftTriangle") -> 16#022EA;
+entity("NotLeftTriangleBar") -> [16#029CF, 16#00338];
+entity("NotLeftTriangleEqual") -> 16#022EC;
+entity("NotLess") -> 16#0226E;
+entity("NotLessEqual") -> 16#02270;
+entity("NotLessGreater") -> 16#02278;
+entity("NotLessLess") -> [16#0226A, 16#00338];
+entity("NotLessSlantEqual") -> [16#02A7D, 16#00338];
+entity("NotLessTilde") -> 16#02274;
+entity("NotNestedGreaterGreater") -> [16#02AA2, 16#00338];
+entity("NotNestedLessLess") -> [16#02AA1, 16#00338];
+entity("NotPrecedes") -> 16#02280;
+entity("NotPrecedesEqual") -> [16#02AAF, 16#00338];
+entity("NotPrecedesSlantEqual") -> 16#022E0;
+entity("NotReverseElement") -> 16#0220C;
+entity("NotRightTriangle") -> 16#022EB;
+entity("NotRightTriangleBar") -> [16#029D0, 16#00338];
+entity("NotRightTriangleEqual") -> 16#022ED;
+entity("NotSquareSubset") -> [16#0228F, 16#00338];
+entity("NotSquareSubsetEqual") -> 16#022E2;
+entity("NotSquareSuperset") -> [16#02290, 16#00338];
+entity("NotSquareSupersetEqual") -> 16#022E3;
+entity("NotSubset") -> [16#02282, 16#020D2];
+entity("NotSubsetEqual") -> 16#02288;
+entity("NotSucceeds") -> 16#02281;
+entity("NotSucceedsEqual") -> [16#02AB0, 16#00338];
+entity("NotSucceedsSlantEqual") -> 16#022E1;
+entity("NotSucceedsTilde") -> [16#0227F, 16#00338];
+entity("NotSuperset") -> [16#02283, 16#020D2];
+entity("NotSupersetEqual") -> 16#02289;
+entity("NotTilde") -> 16#02241;
+entity("NotTildeEqual") -> 16#02244;
+entity("NotTildeFullEqual") -> 16#02247;
+entity("NotTildeTilde") -> 16#02249;
+entity("NotVerticalBar") -> 16#02224;
+entity("Nscr") -> 16#1D4A9;
+entity("Ntilde") -> 16#000D1;
+entity("Nu") -> 16#0039D;
+entity("OElig") -> 16#00152;
+entity("Oacute") -> 16#000D3;
+entity("Ocirc") -> 16#000D4;
+entity("Ocy") -> 16#0041E;
+entity("Odblac") -> 16#00150;
+entity("Ofr") -> 16#1D512;
+entity("Ograve") -> 16#000D2;
+entity("Omacr") -> 16#0014C;
+entity("Omega") -> 16#003A9;
+entity("Omicron") -> 16#0039F;
+entity("Oopf") -> 16#1D546;
+entity("OpenCurlyDoubleQuote") -> 16#0201C;
+entity("OpenCurlyQuote") -> 16#02018;
+entity("Or") -> 16#02A54;
+entity("Oscr") -> 16#1D4AA;
+entity("Oslash") -> 16#000D8;
+entity("Otilde") -> 16#000D5;
+entity("Otimes") -> 16#02A37;
+entity("Ouml") -> 16#000D6;
+entity("OverBar") -> 16#0203E;
+entity("OverBrace") -> 16#023DE;
+entity("OverBracket") -> 16#023B4;
+entity("OverParenthesis") -> 16#023DC;
+entity("PartialD") -> 16#02202;
+entity("Pcy") -> 16#0041F;
+entity("Pfr") -> 16#1D513;
+entity("Phi") -> 16#003A6;
+entity("Pi") -> 16#003A0;
+entity("PlusMinus") -> 16#000B1;
+entity("Poincareplane") -> 16#0210C;
+entity("Popf") -> 16#02119;
+entity("Pr") -> 16#02ABB;
+entity("Precedes") -> 16#0227A;
+entity("PrecedesEqual") -> 16#02AAF;
+entity("PrecedesSlantEqual") -> 16#0227C;
+entity("PrecedesTilde") -> 16#0227E;
+entity("Prime") -> 16#02033;
+entity("Product") -> 16#0220F;
+entity("Proportion") -> 16#02237;
+entity("Proportional") -> 16#0221D;
+entity("Pscr") -> 16#1D4AB;
+entity("Psi") -> 16#003A8;
+entity("QUOT") -> 16#00022;
+entity("Qfr") -> 16#1D514;
+entity("Qopf") -> 16#0211A;
+entity("Qscr") -> 16#1D4AC;
+entity("RBarr") -> 16#02910;
+entity("REG") -> 16#000AE;
+entity("Racute") -> 16#00154;
+entity("Rang") -> 16#027EB;
+entity("Rarr") -> 16#021A0;
+entity("Rarrtl") -> 16#02916;
+entity("Rcaron") -> 16#00158;
+entity("Rcedil") -> 16#00156;
+entity("Rcy") -> 16#00420;
+entity("Re") -> 16#0211C;
+entity("ReverseElement") -> 16#0220B;
+entity("ReverseEquilibrium") -> 16#021CB;
+entity("ReverseUpEquilibrium") -> 16#0296F;
+entity("Rfr") -> 16#0211C;
+entity("Rho") -> 16#003A1;
+entity("RightAngleBracket") -> 16#027E9;
+entity("RightArrow") -> 16#02192;
+entity("RightArrowBar") -> 16#021E5;
+entity("RightArrowLeftArrow") -> 16#021C4;
+entity("RightCeiling") -> 16#02309;
+entity("RightDoubleBracket") -> 16#027E7;
+entity("RightDownTeeVector") -> 16#0295D;
+entity("RightDownVector") -> 16#021C2;
+entity("RightDownVectorBar") -> 16#02955;
+entity("RightFloor") -> 16#0230B;
+entity("RightTee") -> 16#022A2;
+entity("RightTeeArrow") -> 16#021A6;
+entity("RightTeeVector") -> 16#0295B;
+entity("RightTriangle") -> 16#022B3;
+entity("RightTriangleBar") -> 16#029D0;
+entity("RightTriangleEqual") -> 16#022B5;
+entity("RightUpDownVector") -> 16#0294F;
+entity("RightUpTeeVector") -> 16#0295C;
+entity("RightUpVector") -> 16#021BE;
+entity("RightUpVectorBar") -> 16#02954;
+entity("RightVector") -> 16#021C0;
+entity("RightVectorBar") -> 16#02953;
+entity("Rightarrow") -> 16#021D2;
+entity("Ropf") -> 16#0211D;
+entity("RoundImplies") -> 16#02970;
+entity("Rrightarrow") -> 16#021DB;
+entity("Rscr") -> 16#0211B;
+entity("Rsh") -> 16#021B1;
+entity("RuleDelayed") -> 16#029F4;
+entity("SHCHcy") -> 16#00429;
+entity("SHcy") -> 16#00428;
+entity("SOFTcy") -> 16#0042C;
+entity("Sacute") -> 16#0015A;
+entity("Sc") -> 16#02ABC;
+entity("Scaron") -> 16#00160;
+entity("Scedil") -> 16#0015E;
+entity("Scirc") -> 16#0015C;
+entity("Scy") -> 16#00421;
+entity("Sfr") -> 16#1D516;
+entity("ShortDownArrow") -> 16#02193;
+entity("ShortLeftArrow") -> 16#02190;
+entity("ShortRightArrow") -> 16#02192;
+entity("ShortUpArrow") -> 16#02191;
+entity("Sigma") -> 16#003A3;
+entity("SmallCircle") -> 16#02218;
+entity("Sopf") -> 16#1D54A;
+entity("Sqrt") -> 16#0221A;
+entity("Square") -> 16#025A1;
+entity("SquareIntersection") -> 16#02293;
+entity("SquareSubset") -> 16#0228F;
+entity("SquareSubsetEqual") -> 16#02291;
+entity("SquareSuperset") -> 16#02290;
+entity("SquareSupersetEqual") -> 16#02292;
+entity("SquareUnion") -> 16#02294;
+entity("Sscr") -> 16#1D4AE;
+entity("Star") -> 16#022C6;
+entity("Sub") -> 16#022D0;
+entity("Subset") -> 16#022D0;
+entity("SubsetEqual") -> 16#02286;
+entity("Succeeds") -> 16#0227B;
+entity("SucceedsEqual") -> 16#02AB0;
+entity("SucceedsSlantEqual") -> 16#0227D;
+entity("SucceedsTilde") -> 16#0227F;
+entity("SuchThat") -> 16#0220B;
+entity("Sum") -> 16#02211;
+entity("Sup") -> 16#022D1;
+entity("Superset") -> 16#02283;
+entity("SupersetEqual") -> 16#02287;
+entity("Supset") -> 16#022D1;
+entity("THORN") -> 16#000DE;
+entity("TRADE") -> 16#02122;
+entity("TSHcy") -> 16#0040B;
+entity("TScy") -> 16#00426;
+entity("Tab") -> 16#00009;
+entity("Tau") -> 16#003A4;
+entity("Tcaron") -> 16#00164;
+entity("Tcedil") -> 16#00162;
+entity("Tcy") -> 16#00422;
+entity("Tfr") -> 16#1D517;
+entity("Therefore") -> 16#02234;
+entity("Theta") -> 16#00398;
+entity("ThickSpace") -> [16#0205F, 16#0200A];
+entity("ThinSpace") -> 16#02009;
+entity("Tilde") -> 16#0223C;
+entity("TildeEqual") -> 16#02243;
+entity("TildeFullEqual") -> 16#02245;
+entity("TildeTilde") -> 16#02248;
+entity("Topf") -> 16#1D54B;
+entity("TripleDot") -> 16#020DB;
+entity("Tscr") -> 16#1D4AF;
+entity("Tstrok") -> 16#00166;
+entity("Uacute") -> 16#000DA;
+entity("Uarr") -> 16#0219F;
+entity("Uarrocir") -> 16#02949;
+entity("Ubrcy") -> 16#0040E;
+entity("Ubreve") -> 16#0016C;
+entity("Ucirc") -> 16#000DB;
+entity("Ucy") -> 16#00423;
+entity("Udblac") -> 16#00170;
+entity("Ufr") -> 16#1D518;
+entity("Ugrave") -> 16#000D9;
+entity("Umacr") -> 16#0016A;
+entity("UnderBar") -> 16#0005F;
+entity("UnderBrace") -> 16#023DF;
+entity("UnderBracket") -> 16#023B5;
+entity("UnderParenthesis") -> 16#023DD;
+entity("Union") -> 16#022C3;
+entity("UnionPlus") -> 16#0228E;
+entity("Uogon") -> 16#00172;
+entity("Uopf") -> 16#1D54C;
+entity("UpArrow") -> 16#02191;
+entity("UpArrowBar") -> 16#02912;
+entity("UpArrowDownArrow") -> 16#021C5;
+entity("UpDownArrow") -> 16#02195;
+entity("UpEquilibrium") -> 16#0296E;
+entity("UpTee") -> 16#022A5;
+entity("UpTeeArrow") -> 16#021A5;
+entity("Uparrow") -> 16#021D1;
+entity("Updownarrow") -> 16#021D5;
+entity("UpperLeftArrow") -> 16#02196;
+entity("UpperRightArrow") -> 16#02197;
+entity("Upsi") -> 16#003D2;
+entity("Upsilon") -> 16#003A5;
+entity("Uring") -> 16#0016E;
+entity("Uscr") -> 16#1D4B0;
+entity("Utilde") -> 16#00168;
+entity("Uuml") -> 16#000DC;
+entity("VDash") -> 16#022AB;
+entity("Vbar") -> 16#02AEB;
+entity("Vcy") -> 16#00412;
+entity("Vdash") -> 16#022A9;
+entity("Vdashl") -> 16#02AE6;
+entity("Vee") -> 16#022C1;
+entity("Verbar") -> 16#02016;
+entity("Vert") -> 16#02016;
+entity("VerticalBar") -> 16#02223;
+entity("VerticalLine") -> 16#0007C;
+entity("VerticalSeparator") -> 16#02758;
+entity("VerticalTilde") -> 16#02240;
+entity("VeryThinSpace") -> 16#0200A;
+entity("Vfr") -> 16#1D519;
+entity("Vopf") -> 16#1D54D;
+entity("Vscr") -> 16#1D4B1;
+entity("Vvdash") -> 16#022AA;
+entity("Wcirc") -> 16#00174;
+entity("Wedge") -> 16#022C0;
+entity("Wfr") -> 16#1D51A;
+entity("Wopf") -> 16#1D54E;
+entity("Wscr") -> 16#1D4B2;
+entity("Xfr") -> 16#1D51B;
+entity("Xi") -> 16#0039E;
+entity("Xopf") -> 16#1D54F;
+entity("Xscr") -> 16#1D4B3;
+entity("YAcy") -> 16#0042F;
+entity("YIcy") -> 16#00407;
+entity("YUcy") -> 16#0042E;
+entity("Yacute") -> 16#000DD;
+entity("Ycirc") -> 16#00176;
+entity("Ycy") -> 16#0042B;
+entity("Yfr") -> 16#1D51C;
+entity("Yopf") -> 16#1D550;
+entity("Yscr") -> 16#1D4B4;
+entity("Yuml") -> 16#00178;
+entity("ZHcy") -> 16#00416;
+entity("Zacute") -> 16#00179;
+entity("Zcaron") -> 16#0017D;
+entity("Zcy") -> 16#00417;
+entity("Zdot") -> 16#0017B;
+entity("ZeroWidthSpace") -> 16#0200B;
+entity("Zeta") -> 16#00396;
+entity("Zfr") -> 16#02128;
+entity("Zopf") -> 16#02124;
+entity("Zscr") -> 16#1D4B5;
+entity("aacute") -> 16#000E1;
+entity("abreve") -> 16#00103;
+entity("ac") -> 16#0223E;
+entity("acE") -> [16#0223E, 16#00333];
+entity("acd") -> 16#0223F;
+entity("acirc") -> 16#000E2;
+entity("acute") -> 16#000B4;
+entity("acy") -> 16#00430;
+entity("aelig") -> 16#000E6;
+entity("af") -> 16#02061;
+entity("afr") -> 16#1D51E;
+entity("agrave") -> 16#000E0;
+entity("alefsym") -> 16#02135;
+entity("aleph") -> 16#02135;
+entity("alpha") -> 16#003B1;
+entity("amacr") -> 16#00101;
+entity("amalg") -> 16#02A3F;
+entity("amp") -> 16#00026;
+entity("and") -> 16#02227;
+entity("andand") -> 16#02A55;
+entity("andd") -> 16#02A5C;
+entity("andslope") -> 16#02A58;
+entity("andv") -> 16#02A5A;
+entity("ang") -> 16#02220;
+entity("ange") -> 16#029A4;
+entity("angle") -> 16#02220;
+entity("angmsd") -> 16#02221;
+entity("angmsdaa") -> 16#029A8;
+entity("angmsdab") -> 16#029A9;
+entity("angmsdac") -> 16#029AA;
+entity("angmsdad") -> 16#029AB;
+entity("angmsdae") -> 16#029AC;
+entity("angmsdaf") -> 16#029AD;
+entity("angmsdag") -> 16#029AE;
+entity("angmsdah") -> 16#029AF;
+entity("angrt") -> 16#0221F;
+entity("angrtvb") -> 16#022BE;
+entity("angrtvbd") -> 16#0299D;
+entity("angsph") -> 16#02222;
+entity("angst") -> 16#000C5;
+entity("angzarr") -> 16#0237C;
+entity("aogon") -> 16#00105;
+entity("aopf") -> 16#1D552;
+entity("ap") -> 16#02248;
+entity("apE") -> 16#02A70;
+entity("apacir") -> 16#02A6F;
+entity("ape") -> 16#0224A;
+entity("apid") -> 16#0224B;
+entity("apos") -> 16#00027;
+entity("approx") -> 16#02248;
+entity("approxeq") -> 16#0224A;
+entity("aring") -> 16#000E5;
+entity("ascr") -> 16#1D4B6;
+entity("ast") -> 16#0002A;
+entity("asymp") -> 16#02248;
+entity("asympeq") -> 16#0224D;
+entity("atilde") -> 16#000E3;
+entity("auml") -> 16#000E4;
+entity("awconint") -> 16#02233;
+entity("awint") -> 16#02A11;
+entity("bNot") -> 16#02AED;
+entity("backcong") -> 16#0224C;
+entity("backepsilon") -> 16#003F6;
+entity("backprime") -> 16#02035;
+entity("backsim") -> 16#0223D;
+entity("backsimeq") -> 16#022CD;
+entity("barvee") -> 16#022BD;
+entity("barwed") -> 16#02305;
+entity("barwedge") -> 16#02305;
+entity("bbrk") -> 16#023B5;
+entity("bbrktbrk") -> 16#023B6;
+entity("bcong") -> 16#0224C;
+entity("bcy") -> 16#00431;
+entity("bdquo") -> 16#0201E;
+entity("becaus") -> 16#02235;
+entity("because") -> 16#02235;
+entity("bemptyv") -> 16#029B0;
+entity("bepsi") -> 16#003F6;
+entity("bernou") -> 16#0212C;
+entity("beta") -> 16#003B2;
+entity("beth") -> 16#02136;
+entity("between") -> 16#0226C;
+entity("bfr") -> 16#1D51F;
+entity("bigcap") -> 16#022C2;
+entity("bigcirc") -> 16#025EF;
+entity("bigcup") -> 16#022C3;
+entity("bigodot") -> 16#02A00;
+entity("bigoplus") -> 16#02A01;
+entity("bigotimes") -> 16#02A02;
+entity("bigsqcup") -> 16#02A06;
+entity("bigstar") -> 16#02605;
+entity("bigtriangledown") -> 16#025BD;
+entity("bigtriangleup") -> 16#025B3;
+entity("biguplus") -> 16#02A04;
+entity("bigvee") -> 16#022C1;
+entity("bigwedge") -> 16#022C0;
+entity("bkarow") -> 16#0290D;
+entity("blacklozenge") -> 16#029EB;
+entity("blacksquare") -> 16#025AA;
+entity("blacktriangle") -> 16#025B4;
+entity("blacktriangledown") -> 16#025BE;
+entity("blacktriangleleft") -> 16#025C2;
+entity("blacktriangleright") -> 16#025B8;
+entity("blank") -> 16#02423;
+entity("blk12") -> 16#02592;
+entity("blk14") -> 16#02591;
+entity("blk34") -> 16#02593;
+entity("block") -> 16#02588;
+entity("bne") -> [16#0003D, 16#020E5];
+entity("bnequiv") -> [16#02261, 16#020E5];
+entity("bnot") -> 16#02310;
+entity("bopf") -> 16#1D553;
+entity("bot") -> 16#022A5;
+entity("bottom") -> 16#022A5;
+entity("bowtie") -> 16#022C8;
+entity("boxDL") -> 16#02557;
+entity("boxDR") -> 16#02554;
+entity("boxDl") -> 16#02556;
+entity("boxDr") -> 16#02553;
+entity("boxH") -> 16#02550;
+entity("boxHD") -> 16#02566;
+entity("boxHU") -> 16#02569;
+entity("boxHd") -> 16#02564;
+entity("boxHu") -> 16#02567;
+entity("boxUL") -> 16#0255D;
+entity("boxUR") -> 16#0255A;
+entity("boxUl") -> 16#0255C;
+entity("boxUr") -> 16#02559;
+entity("boxV") -> 16#02551;
+entity("boxVH") -> 16#0256C;
+entity("boxVL") -> 16#02563;
+entity("boxVR") -> 16#02560;
+entity("boxVh") -> 16#0256B;
+entity("boxVl") -> 16#02562;
+entity("boxVr") -> 16#0255F;
+entity("boxbox") -> 16#029C9;
+entity("boxdL") -> 16#02555;
+entity("boxdR") -> 16#02552;
+entity("boxdl") -> 16#02510;
+entity("boxdr") -> 16#0250C;
+entity("boxh") -> 16#02500;
+entity("boxhD") -> 16#02565;
+entity("boxhU") -> 16#02568;
+entity("boxhd") -> 16#0252C;
+entity("boxhu") -> 16#02534;
+entity("boxminus") -> 16#0229F;
+entity("boxplus") -> 16#0229E;
+entity("boxtimes") -> 16#022A0;
+entity("boxuL") -> 16#0255B;
+entity("boxuR") -> 16#02558;
+entity("boxul") -> 16#02518;
+entity("boxur") -> 16#02514;
+entity("boxv") -> 16#02502;
+entity("boxvH") -> 16#0256A;
+entity("boxvL") -> 16#02561;
+entity("boxvR") -> 16#0255E;
+entity("boxvh") -> 16#0253C;
+entity("boxvl") -> 16#02524;
+entity("boxvr") -> 16#0251C;
+entity("bprime") -> 16#02035;
+entity("breve") -> 16#002D8;
+entity("brvbar") -> 16#000A6;
+entity("bscr") -> 16#1D4B7;
+entity("bsemi") -> 16#0204F;
+entity("bsim") -> 16#0223D;
+entity("bsime") -> 16#022CD;
+entity("bsol") -> 16#0005C;
+entity("bsolb") -> 16#029C5;
+entity("bsolhsub") -> 16#027C8;
+entity("bull") -> 16#02022;
+entity("bullet") -> 16#02022;
+entity("bump") -> 16#0224E;
+entity("bumpE") -> 16#02AAE;
+entity("bumpe") -> 16#0224F;
+entity("bumpeq") -> 16#0224F;
+entity("cacute") -> 16#00107;
+entity("cap") -> 16#02229;
+entity("capand") -> 16#02A44;
+entity("capbrcup") -> 16#02A49;
+entity("capcap") -> 16#02A4B;
+entity("capcup") -> 16#02A47;
+entity("capdot") -> 16#02A40;
+entity("caps") -> [16#02229, 16#0FE00];
+entity("caret") -> 16#02041;
+entity("caron") -> 16#002C7;
+entity("ccaps") -> 16#02A4D;
+entity("ccaron") -> 16#0010D;
+entity("ccedil") -> 16#000E7;
+entity("ccirc") -> 16#00109;
+entity("ccups") -> 16#02A4C;
+entity("ccupssm") -> 16#02A50;
+entity("cdot") -> 16#0010B;
+entity("cedil") -> 16#000B8;
+entity("cemptyv") -> 16#029B2;
+entity("cent") -> 16#000A2;
+entity("centerdot") -> 16#000B7;
+entity("cfr") -> 16#1D520;
+entity("chcy") -> 16#00447;
+entity("check") -> 16#02713;
+entity("checkmark") -> 16#02713;
+entity("chi") -> 16#003C7;
+entity("cir") -> 16#025CB;
+entity("cirE") -> 16#029C3;
+entity("circ") -> 16#002C6;
+entity("circeq") -> 16#02257;
+entity("circlearrowleft") -> 16#021BA;
+entity("circlearrowright") -> 16#021BB;
+entity("circledR") -> 16#000AE;
+entity("circledS") -> 16#024C8;
+entity("circledast") -> 16#0229B;
+entity("circledcirc") -> 16#0229A;
+entity("circleddash") -> 16#0229D;
+entity("cire") -> 16#02257;
+entity("cirfnint") -> 16#02A10;
+entity("cirmid") -> 16#02AEF;
+entity("cirscir") -> 16#029C2;
+entity("clubs") -> 16#02663;
+entity("clubsuit") -> 16#02663;
+entity("colon") -> 16#0003A;
+entity("colone") -> 16#02254;
+entity("coloneq") -> 16#02254;
+entity("comma") -> 16#0002C;
+entity("commat") -> 16#00040;
+entity("comp") -> 16#02201;
+entity("compfn") -> 16#02218;
+entity("complement") -> 16#02201;
+entity("complexes") -> 16#02102;
+entity("cong") -> 16#02245;
+entity("congdot") -> 16#02A6D;
+entity("conint") -> 16#0222E;
+entity("copf") -> 16#1D554;
+entity("coprod") -> 16#02210;
+entity("copy") -> 16#000A9;
+entity("copysr") -> 16#02117;
+entity("crarr") -> 16#021B5;
+entity("cross") -> 16#02717;
+entity("cscr") -> 16#1D4B8;
+entity("csub") -> 16#02ACF;
+entity("csube") -> 16#02AD1;
+entity("csup") -> 16#02AD0;
+entity("csupe") -> 16#02AD2;
+entity("ctdot") -> 16#022EF;
+entity("cudarrl") -> 16#02938;
+entity("cudarrr") -> 16#02935;
+entity("cuepr") -> 16#022DE;
+entity("cuesc") -> 16#022DF;
+entity("cularr") -> 16#021B6;
+entity("cularrp") -> 16#0293D;
+entity("cup") -> 16#0222A;
+entity("cupbrcap") -> 16#02A48;
+entity("cupcap") -> 16#02A46;
+entity("cupcup") -> 16#02A4A;
+entity("cupdot") -> 16#0228D;
+entity("cupor") -> 16#02A45;
+entity("cups") -> [16#0222A, 16#0FE00];
+entity("curarr") -> 16#021B7;
+entity("curarrm") -> 16#0293C;
+entity("curlyeqprec") -> 16#022DE;
+entity("curlyeqsucc") -> 16#022DF;
+entity("curlyvee") -> 16#022CE;
+entity("curlywedge") -> 16#022CF;
+entity("curren") -> 16#000A4;
+entity("curvearrowleft") -> 16#021B6;
+entity("curvearrowright") -> 16#021B7;
+entity("cuvee") -> 16#022CE;
+entity("cuwed") -> 16#022CF;
+entity("cwconint") -> 16#02232;
+entity("cwint") -> 16#02231;
+entity("cylcty") -> 16#0232D;
+entity("dArr") -> 16#021D3;
+entity("dHar") -> 16#02965;
+entity("dagger") -> 16#02020;
+entity("daleth") -> 16#02138;
+entity("darr") -> 16#02193;
+entity("dash") -> 16#02010;
+entity("dashv") -> 16#022A3;
+entity("dbkarow") -> 16#0290F;
+entity("dblac") -> 16#002DD;
+entity("dcaron") -> 16#0010F;
+entity("dcy") -> 16#00434;
+entity("dd") -> 16#02146;
+entity("ddagger") -> 16#02021;
+entity("ddarr") -> 16#021CA;
+entity("ddotseq") -> 16#02A77;
+entity("deg") -> 16#000B0;
+entity("delta") -> 16#003B4;
+entity("demptyv") -> 16#029B1;
+entity("dfisht") -> 16#0297F;
+entity("dfr") -> 16#1D521;
+entity("dharl") -> 16#021C3;
+entity("dharr") -> 16#021C2;
+entity("diam") -> 16#022C4;
+entity("diamond") -> 16#022C4;
+entity("diamondsuit") -> 16#02666;
+entity("diams") -> 16#02666;
+entity("die") -> 16#000A8;
+entity("digamma") -> 16#003DD;
+entity("disin") -> 16#022F2;
+entity("div") -> 16#000F7;
+entity("divide") -> 16#000F7;
+entity("divideontimes") -> 16#022C7;
+entity("divonx") -> 16#022C7;
+entity("djcy") -> 16#00452;
+entity("dlcorn") -> 16#0231E;
+entity("dlcrop") -> 16#0230D;
+entity("dollar") -> 16#00024;
+entity("dopf") -> 16#1D555;
+entity("dot") -> 16#002D9;
+entity("doteq") -> 16#02250;
+entity("doteqdot") -> 16#02251;
+entity("dotminus") -> 16#02238;
+entity("dotplus") -> 16#02214;
+entity("dotsquare") -> 16#022A1;
+entity("doublebarwedge") -> 16#02306;
+entity("downarrow") -> 16#02193;
+entity("downdownarrows") -> 16#021CA;
+entity("downharpoonleft") -> 16#021C3;
+entity("downharpoonright") -> 16#021C2;
+entity("drbkarow") -> 16#02910;
+entity("drcorn") -> 16#0231F;
+entity("drcrop") -> 16#0230C;
+entity("dscr") -> 16#1D4B9;
+entity("dscy") -> 16#00455;
+entity("dsol") -> 16#029F6;
+entity("dstrok") -> 16#00111;
+entity("dtdot") -> 16#022F1;
+entity("dtri") -> 16#025BF;
+entity("dtrif") -> 16#025BE;
+entity("duarr") -> 16#021F5;
+entity("duhar") -> 16#0296F;
+entity("dwangle") -> 16#029A6;
+entity("dzcy") -> 16#0045F;
+entity("dzigrarr") -> 16#027FF;
+entity("eDDot") -> 16#02A77;
+entity("eDot") -> 16#02251;
+entity("eacute") -> 16#000E9;
+entity("easter") -> 16#02A6E;
+entity("ecaron") -> 16#0011B;
+entity("ecir") -> 16#02256;
+entity("ecirc") -> 16#000EA;
+entity("ecolon") -> 16#02255;
+entity("ecy") -> 16#0044D;
+entity("edot") -> 16#00117;
+entity("ee") -> 16#02147;
+entity("efDot") -> 16#02252;
+entity("efr") -> 16#1D522;
+entity("eg") -> 16#02A9A;
+entity("egrave") -> 16#000E8;
+entity("egs") -> 16#02A96;
+entity("egsdot") -> 16#02A98;
+entity("el") -> 16#02A99;
+entity("elinters") -> 16#023E7;
+entity("ell") -> 16#02113;
+entity("els") -> 16#02A95;
+entity("elsdot") -> 16#02A97;
+entity("emacr") -> 16#00113;
+entity("empty") -> 16#02205;
+entity("emptyset") -> 16#02205;
+entity("emptyv") -> 16#02205;
+entity("emsp") -> 16#02003;
+entity("emsp13") -> 16#02004;
+entity("emsp14") -> 16#02005;
+entity("eng") -> 16#0014B;
+entity("ensp") -> 16#02002;
+entity("eogon") -> 16#00119;
+entity("eopf") -> 16#1D556;
+entity("epar") -> 16#022D5;
+entity("eparsl") -> 16#029E3;
+entity("eplus") -> 16#02A71;
+entity("epsi") -> 16#003B5;
+entity("epsilon") -> 16#003B5;
+entity("epsiv") -> 16#003F5;
+entity("eqcirc") -> 16#02256;
+entity("eqcolon") -> 16#02255;
+entity("eqsim") -> 16#02242;
+entity("eqslantgtr") -> 16#02A96;
+entity("eqslantless") -> 16#02A95;
+entity("equals") -> 16#0003D;
+entity("equest") -> 16#0225F;
+entity("equiv") -> 16#02261;
+entity("equivDD") -> 16#02A78;
+entity("eqvparsl") -> 16#029E5;
+entity("erDot") -> 16#02253;
+entity("erarr") -> 16#02971;
+entity("escr") -> 16#0212F;
+entity("esdot") -> 16#02250;
+entity("esim") -> 16#02242;
+entity("eta") -> 16#003B7;
+entity("eth") -> 16#000F0;
+entity("euml") -> 16#000EB;
+entity("euro") -> 16#020AC;
+entity("excl") -> 16#00021;
+entity("exist") -> 16#02203;
+entity("expectation") -> 16#02130;
+entity("exponentiale") -> 16#02147;
+entity("fallingdotseq") -> 16#02252;
+entity("fcy") -> 16#00444;
+entity("female") -> 16#02640;
+entity("ffilig") -> 16#0FB03;
+entity("fflig") -> 16#0FB00;
+entity("ffllig") -> 16#0FB04;
+entity("ffr") -> 16#1D523;
+entity("filig") -> 16#0FB01;
+entity("fjlig") -> [16#00066, 16#0006A];
+entity("flat") -> 16#0266D;
+entity("fllig") -> 16#0FB02;
+entity("fltns") -> 16#025B1;
+entity("fnof") -> 16#00192;
+entity("fopf") -> 16#1D557;
+entity("forall") -> 16#02200;
+entity("fork") -> 16#022D4;
+entity("forkv") -> 16#02AD9;
+entity("fpartint") -> 16#02A0D;
+entity("frac12") -> 16#000BD;
+entity("frac13") -> 16#02153;
+entity("frac14") -> 16#000BC;
+entity("frac15") -> 16#02155;
+entity("frac16") -> 16#02159;
+entity("frac18") -> 16#0215B;
+entity("frac23") -> 16#02154;
+entity("frac25") -> 16#02156;
+entity("frac34") -> 16#000BE;
+entity("frac35") -> 16#02157;
+entity("frac38") -> 16#0215C;
+entity("frac45") -> 16#02158;
+entity("frac56") -> 16#0215A;
+entity("frac58") -> 16#0215D;
+entity("frac78") -> 16#0215E;
+entity("frasl") -> 16#02044;
+entity("frown") -> 16#02322;
+entity("fscr") -> 16#1D4BB;
+entity("gE") -> 16#02267;
+entity("gEl") -> 16#02A8C;
+entity("gacute") -> 16#001F5;
+entity("gamma") -> 16#003B3;
+entity("gammad") -> 16#003DD;
+entity("gap") -> 16#02A86;
+entity("gbreve") -> 16#0011F;
+entity("gcirc") -> 16#0011D;
+entity("gcy") -> 16#00433;
+entity("gdot") -> 16#00121;
+entity("ge") -> 16#02265;
+entity("gel") -> 16#022DB;
+entity("geq") -> 16#02265;
+entity("geqq") -> 16#02267;
+entity("geqslant") -> 16#02A7E;
+entity("ges") -> 16#02A7E;
+entity("gescc") -> 16#02AA9;
+entity("gesdot") -> 16#02A80;
+entity("gesdoto") -> 16#02A82;
+entity("gesdotol") -> 16#02A84;
+entity("gesl") -> [16#022DB, 16#0FE00];
+entity("gesles") -> 16#02A94;
+entity("gfr") -> 16#1D524;
+entity("gg") -> 16#0226B;
+entity("ggg") -> 16#022D9;
+entity("gimel") -> 16#02137;
+entity("gjcy") -> 16#00453;
+entity("gl") -> 16#02277;
+entity("glE") -> 16#02A92;
+entity("gla") -> 16#02AA5;
+entity("glj") -> 16#02AA4;
+entity("gnE") -> 16#02269;
+entity("gnap") -> 16#02A8A;
+entity("gnapprox") -> 16#02A8A;
+entity("gne") -> 16#02A88;
+entity("gneq") -> 16#02A88;
+entity("gneqq") -> 16#02269;
+entity("gnsim") -> 16#022E7;
+entity("gopf") -> 16#1D558;
+entity("grave") -> 16#00060;
+entity("gscr") -> 16#0210A;
+entity("gsim") -> 16#02273;
+entity("gsime") -> 16#02A8E;
+entity("gsiml") -> 16#02A90;
+entity("gt") -> 16#0003E;
+entity("gtcc") -> 16#02AA7;
+entity("gtcir") -> 16#02A7A;
+entity("gtdot") -> 16#022D7;
+entity("gtlPar") -> 16#02995;
+entity("gtquest") -> 16#02A7C;
+entity("gtrapprox") -> 16#02A86;
+entity("gtrarr") -> 16#02978;
+entity("gtrdot") -> 16#022D7;
+entity("gtreqless") -> 16#022DB;
+entity("gtreqqless") -> 16#02A8C;
+entity("gtrless") -> 16#02277;
+entity("gtrsim") -> 16#02273;
+entity("gvertneqq") -> [16#02269, 16#0FE00];
+entity("gvnE") -> [16#02269, 16#0FE00];
+entity("hArr") -> 16#021D4;
+entity("hairsp") -> 16#0200A;
+entity("half") -> 16#000BD;
+entity("hamilt") -> 16#0210B;
+entity("hardcy") -> 16#0044A;
+entity("harr") -> 16#02194;
+entity("harrcir") -> 16#02948;
+entity("harrw") -> 16#021AD;
+entity("hbar") -> 16#0210F;
+entity("hcirc") -> 16#00125;
+entity("hearts") -> 16#02665;
+entity("heartsuit") -> 16#02665;
+entity("hellip") -> 16#02026;
+entity("hercon") -> 16#022B9;
+entity("hfr") -> 16#1D525;
+entity("hksearow") -> 16#02925;
+entity("hkswarow") -> 16#02926;
+entity("hoarr") -> 16#021FF;
+entity("homtht") -> 16#0223B;
+entity("hookleftarrow") -> 16#021A9;
+entity("hookrightarrow") -> 16#021AA;
+entity("hopf") -> 16#1D559;
+entity("horbar") -> 16#02015;
+entity("hscr") -> 16#1D4BD;
+entity("hslash") -> 16#0210F;
+entity("hstrok") -> 16#00127;
+entity("hybull") -> 16#02043;
+entity("hyphen") -> 16#02010;
+entity("iacute") -> 16#000ED;
+entity("ic") -> 16#02063;
+entity("icirc") -> 16#000EE;
+entity("icy") -> 16#00438;
+entity("iecy") -> 16#00435;
+entity("iexcl") -> 16#000A1;
+entity("iff") -> 16#021D4;
+entity("ifr") -> 16#1D526;
+entity("igrave") -> 16#000EC;
+entity("ii") -> 16#02148;
+entity("iiiint") -> 16#02A0C;
+entity("iiint") -> 16#0222D;
+entity("iinfin") -> 16#029DC;
+entity("iiota") -> 16#02129;
+entity("ijlig") -> 16#00133;
+entity("imacr") -> 16#0012B;
+entity("image") -> 16#02111;
+entity("imagline") -> 16#02110;
+entity("imagpart") -> 16#02111;
+entity("imath") -> 16#00131;
+entity("imof") -> 16#022B7;
+entity("imped") -> 16#001B5;
+entity("in") -> 16#02208;
+entity("incare") -> 16#02105;
+entity("infin") -> 16#0221E;
+entity("infintie") -> 16#029DD;
+entity("inodot") -> 16#00131;
+entity("int") -> 16#0222B;
+entity("intcal") -> 16#022BA;
+entity("integers") -> 16#02124;
+entity("intercal") -> 16#022BA;
+entity("intlarhk") -> 16#02A17;
+entity("intprod") -> 16#02A3C;
+entity("iocy") -> 16#00451;
+entity("iogon") -> 16#0012F;
+entity("iopf") -> 16#1D55A;
+entity("iota") -> 16#003B9;
+entity("iprod") -> 16#02A3C;
+entity("iquest") -> 16#000BF;
+entity("iscr") -> 16#1D4BE;
+entity("isin") -> 16#02208;
+entity("isinE") -> 16#022F9;
+entity("isindot") -> 16#022F5;
+entity("isins") -> 16#022F4;
+entity("isinsv") -> 16#022F3;
+entity("isinv") -> 16#02208;
+entity("it") -> 16#02062;
+entity("itilde") -> 16#00129;
+entity("iukcy") -> 16#00456;
+entity("iuml") -> 16#000EF;
+entity("jcirc") -> 16#00135;
+entity("jcy") -> 16#00439;
+entity("jfr") -> 16#1D527;
+entity("jmath") -> 16#00237;
+entity("jopf") -> 16#1D55B;
+entity("jscr") -> 16#1D4BF;
+entity("jsercy") -> 16#00458;
+entity("jukcy") -> 16#00454;
+entity("kappa") -> 16#003BA;
+entity("kappav") -> 16#003F0;
+entity("kcedil") -> 16#00137;
+entity("kcy") -> 16#0043A;
+entity("kfr") -> 16#1D528;
+entity("kgreen") -> 16#00138;
+entity("khcy") -> 16#00445;
+entity("kjcy") -> 16#0045C;
+entity("kopf") -> 16#1D55C;
+entity("kscr") -> 16#1D4C0;
+entity("lAarr") -> 16#021DA;
+entity("lArr") -> 16#021D0;
+entity("lAtail") -> 16#0291B;
+entity("lBarr") -> 16#0290E;
+entity("lE") -> 16#02266;
+entity("lEg") -> 16#02A8B;
+entity("lHar") -> 16#02962;
+entity("lacute") -> 16#0013A;
+entity("laemptyv") -> 16#029B4;
+entity("lagran") -> 16#02112;
+entity("lambda") -> 16#003BB;
+entity("lang") -> 16#027E8;
+entity("langd") -> 16#02991;
+entity("langle") -> 16#027E8;
+entity("lap") -> 16#02A85;
+entity("laquo") -> 16#000AB;
+entity("larr") -> 16#02190;
+entity("larrb") -> 16#021E4;
+entity("larrbfs") -> 16#0291F;
+entity("larrfs") -> 16#0291D;
+entity("larrhk") -> 16#021A9;
+entity("larrlp") -> 16#021AB;
+entity("larrpl") -> 16#02939;
+entity("larrsim") -> 16#02973;
+entity("larrtl") -> 16#021A2;
+entity("lat") -> 16#02AAB;
+entity("latail") -> 16#02919;
+entity("late") -> 16#02AAD;
+entity("lates") -> [16#02AAD, 16#0FE00];
+entity("lbarr") -> 16#0290C;
+entity("lbbrk") -> 16#02772;
+entity("lbrace") -> 16#0007B;
+entity("lbrack") -> 16#0005B;
+entity("lbrke") -> 16#0298B;
+entity("lbrksld") -> 16#0298F;
+entity("lbrkslu") -> 16#0298D;
+entity("lcaron") -> 16#0013E;
+entity("lcedil") -> 16#0013C;
+entity("lceil") -> 16#02308;
+entity("lcub") -> 16#0007B;
+entity("lcy") -> 16#0043B;
+entity("ldca") -> 16#02936;
+entity("ldquo") -> 16#0201C;
+entity("ldquor") -> 16#0201E;
+entity("ldrdhar") -> 16#02967;
+entity("ldrushar") -> 16#0294B;
+entity("ldsh") -> 16#021B2;
+entity("le") -> 16#02264;
+entity("leftarrow") -> 16#02190;
+entity("leftarrowtail") -> 16#021A2;
+entity("leftharpoondown") -> 16#021BD;
+entity("leftharpoonup") -> 16#021BC;
+entity("leftleftarrows") -> 16#021C7;
+entity("leftrightarrow") -> 16#02194;
+entity("leftrightarrows") -> 16#021C6;
+entity("leftrightharpoons") -> 16#021CB;
+entity("leftrightsquigarrow") -> 16#021AD;
+entity("leftthreetimes") -> 16#022CB;
+entity("leg") -> 16#022DA;
+entity("leq") -> 16#02264;
+entity("leqq") -> 16#02266;
+entity("leqslant") -> 16#02A7D;
+entity("les") -> 16#02A7D;
+entity("lescc") -> 16#02AA8;
+entity("lesdot") -> 16#02A7F;
+entity("lesdoto") -> 16#02A81;
+entity("lesdotor") -> 16#02A83;
+entity("lesg") -> [16#022DA, 16#0FE00];
+entity("lesges") -> 16#02A93;
+entity("lessapprox") -> 16#02A85;
+entity("lessdot") -> 16#022D6;
+entity("lesseqgtr") -> 16#022DA;
+entity("lesseqqgtr") -> 16#02A8B;
+entity("lessgtr") -> 16#02276;
+entity("lesssim") -> 16#02272;
+entity("lfisht") -> 16#0297C;
+entity("lfloor") -> 16#0230A;
+entity("lfr") -> 16#1D529;
+entity("lg") -> 16#02276;
+entity("lgE") -> 16#02A91;
+entity("lhard") -> 16#021BD;
+entity("lharu") -> 16#021BC;
+entity("lharul") -> 16#0296A;
+entity("lhblk") -> 16#02584;
+entity("ljcy") -> 16#00459;
+entity("ll") -> 16#0226A;
+entity("llarr") -> 16#021C7;
+entity("llcorner") -> 16#0231E;
+entity("llhard") -> 16#0296B;
+entity("lltri") -> 16#025FA;
+entity("lmidot") -> 16#00140;
+entity("lmoust") -> 16#023B0;
+entity("lmoustache") -> 16#023B0;
+entity("lnE") -> 16#02268;
+entity("lnap") -> 16#02A89;
+entity("lnapprox") -> 16#02A89;
+entity("lne") -> 16#02A87;
+entity("lneq") -> 16#02A87;
+entity("lneqq") -> 16#02268;
+entity("lnsim") -> 16#022E6;
+entity("loang") -> 16#027EC;
+entity("loarr") -> 16#021FD;
+entity("lobrk") -> 16#027E6;
+entity("longleftarrow") -> 16#027F5;
+entity("longleftrightarrow") -> 16#027F7;
+entity("longmapsto") -> 16#027FC;
+entity("longrightarrow") -> 16#027F6;
+entity("looparrowleft") -> 16#021AB;
+entity("looparrowright") -> 16#021AC;
+entity("lopar") -> 16#02985;
+entity("lopf") -> 16#1D55D;
+entity("loplus") -> 16#02A2D;
+entity("lotimes") -> 16#02A34;
+entity("lowast") -> 16#02217;
+entity("lowbar") -> 16#0005F;
+entity("loz") -> 16#025CA;
+entity("lozenge") -> 16#025CA;
+entity("lozf") -> 16#029EB;
+entity("lpar") -> 16#00028;
+entity("lparlt") -> 16#02993;
+entity("lrarr") -> 16#021C6;
+entity("lrcorner") -> 16#0231F;
+entity("lrhar") -> 16#021CB;
+entity("lrhard") -> 16#0296D;
+entity("lrm") -> 16#0200E;
+entity("lrtri") -> 16#022BF;
+entity("lsaquo") -> 16#02039;
+entity("lscr") -> 16#1D4C1;
+entity("lsh") -> 16#021B0;
+entity("lsim") -> 16#02272;
+entity("lsime") -> 16#02A8D;
+entity("lsimg") -> 16#02A8F;
+entity("lsqb") -> 16#0005B;
+entity("lsquo") -> 16#02018;
+entity("lsquor") -> 16#0201A;
+entity("lstrok") -> 16#00142;
+entity("lt") -> 16#0003C;
+entity("ltcc") -> 16#02AA6;
+entity("ltcir") -> 16#02A79;
+entity("ltdot") -> 16#022D6;
+entity("lthree") -> 16#022CB;
+entity("ltimes") -> 16#022C9;
+entity("ltlarr") -> 16#02976;
+entity("ltquest") -> 16#02A7B;
+entity("ltrPar") -> 16#02996;
+entity("ltri") -> 16#025C3;
+entity("ltrie") -> 16#022B4;
+entity("ltrif") -> 16#025C2;
+entity("lurdshar") -> 16#0294A;
+entity("luruhar") -> 16#02966;
+entity("lvertneqq") -> [16#02268, 16#0FE00];
+entity("lvnE") -> [16#02268, 16#0FE00];
+entity("mDDot") -> 16#0223A;
+entity("macr") -> 16#000AF;
+entity("male") -> 16#02642;
+entity("malt") -> 16#02720;
+entity("maltese") -> 16#02720;
+entity("map") -> 16#021A6;
+entity("mapsto") -> 16#021A6;
+entity("mapstodown") -> 16#021A7;
+entity("mapstoleft") -> 16#021A4;
+entity("mapstoup") -> 16#021A5;
+entity("marker") -> 16#025AE;
+entity("mcomma") -> 16#02A29;
+entity("mcy") -> 16#0043C;
+entity("mdash") -> 16#02014;
+entity("measuredangle") -> 16#02221;
+entity("mfr") -> 16#1D52A;
+entity("mho") -> 16#02127;
+entity("micro") -> 16#000B5;
+entity("mid") -> 16#02223;
+entity("midast") -> 16#0002A;
+entity("midcir") -> 16#02AF0;
+entity("middot") -> 16#000B7;
+entity("minus") -> 16#02212;
+entity("minusb") -> 16#0229F;
+entity("minusd") -> 16#02238;
+entity("minusdu") -> 16#02A2A;
+entity("mlcp") -> 16#02ADB;
+entity("mldr") -> 16#02026;
+entity("mnplus") -> 16#02213;
+entity("models") -> 16#022A7;
+entity("mopf") -> 16#1D55E;
+entity("mp") -> 16#02213;
+entity("mscr") -> 16#1D4C2;
+entity("mstpos") -> 16#0223E;
+entity("mu") -> 16#003BC;
+entity("multimap") -> 16#022B8;
+entity("mumap") -> 16#022B8;
+entity("nGg") -> [16#022D9, 16#00338];
+entity("nGt") -> [16#0226B, 16#020D2];
+entity("nGtv") -> [16#0226B, 16#00338];
+entity("nLeftarrow") -> 16#021CD;
+entity("nLeftrightarrow") -> 16#021CE;
+entity("nLl") -> [16#022D8, 16#00338];
+entity("nLt") -> [16#0226A, 16#020D2];
+entity("nLtv") -> [16#0226A, 16#00338];
+entity("nRightarrow") -> 16#021CF;
+entity("nVDash") -> 16#022AF;
+entity("nVdash") -> 16#022AE;
+entity("nabla") -> 16#02207;
+entity("nacute") -> 16#00144;
+entity("nang") -> [16#02220, 16#020D2];
+entity("nap") -> 16#02249;
+entity("napE") -> [16#02A70, 16#00338];
+entity("napid") -> [16#0224B, 16#00338];
+entity("napos") -> 16#00149;
+entity("napprox") -> 16#02249;
+entity("natur") -> 16#0266E;
+entity("natural") -> 16#0266E;
+entity("naturals") -> 16#02115;
+entity("nbsp") -> 16#000A0;
+entity("nbump") -> [16#0224E, 16#00338];
+entity("nbumpe") -> [16#0224F, 16#00338];
+entity("ncap") -> 16#02A43;
+entity("ncaron") -> 16#00148;
+entity("ncedil") -> 16#00146;
+entity("ncong") -> 16#02247;
+entity("ncongdot") -> [16#02A6D, 16#00338];
+entity("ncup") -> 16#02A42;
+entity("ncy") -> 16#0043D;
+entity("ndash") -> 16#02013;
+entity("ne") -> 16#02260;
+entity("neArr") -> 16#021D7;
+entity("nearhk") -> 16#02924;
+entity("nearr") -> 16#02197;
+entity("nearrow") -> 16#02197;
+entity("nedot") -> [16#02250, 16#00338];
+entity("nequiv") -> 16#02262;
+entity("nesear") -> 16#02928;
+entity("nesim") -> [16#02242, 16#00338];
+entity("nexist") -> 16#02204;
+entity("nexists") -> 16#02204;
+entity("nfr") -> 16#1D52B;
+entity("ngE") -> [16#02267, 16#00338];
+entity("nge") -> 16#02271;
+entity("ngeq") -> 16#02271;
+entity("ngeqq") -> [16#02267, 16#00338];
+entity("ngeqslant") -> [16#02A7E, 16#00338];
+entity("nges") -> [16#02A7E, 16#00338];
+entity("ngsim") -> 16#02275;
+entity("ngt") -> 16#0226F;
+entity("ngtr") -> 16#0226F;
+entity("nhArr") -> 16#021CE;
+entity("nharr") -> 16#021AE;
+entity("nhpar") -> 16#02AF2;
+entity("ni") -> 16#0220B;
+entity("nis") -> 16#022FC;
+entity("nisd") -> 16#022FA;
+entity("niv") -> 16#0220B;
+entity("njcy") -> 16#0045A;
+entity("nlArr") -> 16#021CD;
+entity("nlE") -> [16#02266, 16#00338];
+entity("nlarr") -> 16#0219A;
+entity("nldr") -> 16#02025;
+entity("nle") -> 16#02270;
+entity("nleftarrow") -> 16#0219A;
+entity("nleftrightarrow") -> 16#021AE;
+entity("nleq") -> 16#02270;
+entity("nleqq") -> [16#02266, 16#00338];
+entity("nleqslant") -> [16#02A7D, 16#00338];
+entity("nles") -> [16#02A7D, 16#00338];
+entity("nless") -> 16#0226E;
+entity("nlsim") -> 16#02274;
+entity("nlt") -> 16#0226E;
+entity("nltri") -> 16#022EA;
+entity("nltrie") -> 16#022EC;
+entity("nmid") -> 16#02224;
+entity("nopf") -> 16#1D55F;
+entity("not") -> 16#000AC;
+entity("notin") -> 16#02209;
+entity("notinE") -> [16#022F9, 16#00338];
+entity("notindot") -> [16#022F5, 16#00338];
+entity("notinva") -> 16#02209;
+entity("notinvb") -> 16#022F7;
+entity("notinvc") -> 16#022F6;
+entity("notni") -> 16#0220C;
+entity("notniva") -> 16#0220C;
+entity("notnivb") -> 16#022FE;
+entity("notnivc") -> 16#022FD;
+entity("npar") -> 16#02226;
+entity("nparallel") -> 16#02226;
+entity("nparsl") -> [16#02AFD, 16#020E5];
+entity("npart") -> [16#02202, 16#00338];
+entity("npolint") -> 16#02A14;
+entity("npr") -> 16#02280;
+entity("nprcue") -> 16#022E0;
+entity("npre") -> [16#02AAF, 16#00338];
+entity("nprec") -> 16#02280;
+entity("npreceq") -> [16#02AAF, 16#00338];
+entity("nrArr") -> 16#021CF;
+entity("nrarr") -> 16#0219B;
+entity("nrarrc") -> [16#02933, 16#00338];
+entity("nrarrw") -> [16#0219D, 16#00338];
+entity("nrightarrow") -> 16#0219B;
+entity("nrtri") -> 16#022EB;
+entity("nrtrie") -> 16#022ED;
+entity("nsc") -> 16#02281;
+entity("nsccue") -> 16#022E1;
+entity("nsce") -> [16#02AB0, 16#00338];
+entity("nscr") -> 16#1D4C3;
+entity("nshortmid") -> 16#02224;
+entity("nshortparallel") -> 16#02226;
+entity("nsim") -> 16#02241;
+entity("nsime") -> 16#02244;
+entity("nsimeq") -> 16#02244;
+entity("nsmid") -> 16#02224;
+entity("nspar") -> 16#02226;
+entity("nsqsube") -> 16#022E2;
+entity("nsqsupe") -> 16#022E3;
+entity("nsub") -> 16#02284;
+entity("nsubE") -> [16#02AC5, 16#00338];
+entity("nsube") -> 16#02288;
+entity("nsubset") -> [16#02282, 16#020D2];
+entity("nsubseteq") -> 16#02288;
+entity("nsubseteqq") -> [16#02AC5, 16#00338];
+entity("nsucc") -> 16#02281;
+entity("nsucceq") -> [16#02AB0, 16#00338];
+entity("nsup") -> 16#02285;
+entity("nsupE") -> [16#02AC6, 16#00338];
+entity("nsupe") -> 16#02289;
+entity("nsupset") -> [16#02283, 16#020D2];
+entity("nsupseteq") -> 16#02289;
+entity("nsupseteqq") -> [16#02AC6, 16#00338];
+entity("ntgl") -> 16#02279;
+entity("ntilde") -> 16#000F1;
+entity("ntlg") -> 16#02278;
+entity("ntriangleleft") -> 16#022EA;
+entity("ntrianglelefteq") -> 16#022EC;
+entity("ntriangleright") -> 16#022EB;
+entity("ntrianglerighteq") -> 16#022ED;
+entity("nu") -> 16#003BD;
+entity("num") -> 16#00023;
+entity("numero") -> 16#02116;
+entity("numsp") -> 16#02007;
+entity("nvDash") -> 16#022AD;
+entity("nvHarr") -> 16#02904;
+entity("nvap") -> [16#0224D, 16#020D2];
+entity("nvdash") -> 16#022AC;
+entity("nvge") -> [16#02265, 16#020D2];
+entity("nvgt") -> [16#0003E, 16#020D2];
+entity("nvinfin") -> 16#029DE;
+entity("nvlArr") -> 16#02902;
+entity("nvle") -> [16#02264, 16#020D2];
+entity("nvlt") -> [16#0003C, 16#020D2];
+entity("nvltrie") -> [16#022B4, 16#020D2];
+entity("nvrArr") -> 16#02903;
+entity("nvrtrie") -> [16#022B5, 16#020D2];
+entity("nvsim") -> [16#0223C, 16#020D2];
+entity("nwArr") -> 16#021D6;
+entity("nwarhk") -> 16#02923;
+entity("nwarr") -> 16#02196;
+entity("nwarrow") -> 16#02196;
+entity("nwnear") -> 16#02927;
+entity("oS") -> 16#024C8;
+entity("oacute") -> 16#000F3;
+entity("oast") -> 16#0229B;
+entity("ocir") -> 16#0229A;
+entity("ocirc") -> 16#000F4;
+entity("ocy") -> 16#0043E;
+entity("odash") -> 16#0229D;
+entity("odblac") -> 16#00151;
+entity("odiv") -> 16#02A38;
+entity("odot") -> 16#02299;
+entity("odsold") -> 16#029BC;
+entity("oelig") -> 16#00153;
+entity("ofcir") -> 16#029BF;
+entity("ofr") -> 16#1D52C;
+entity("ogon") -> 16#002DB;
+entity("ograve") -> 16#000F2;
+entity("ogt") -> 16#029C1;
+entity("ohbar") -> 16#029B5;
+entity("ohm") -> 16#003A9;
+entity("oint") -> 16#0222E;
+entity("olarr") -> 16#021BA;
+entity("olcir") -> 16#029BE;
+entity("olcross") -> 16#029BB;
+entity("oline") -> 16#0203E;
+entity("olt") -> 16#029C0;
+entity("omacr") -> 16#0014D;
+entity("omega") -> 16#003C9;
+entity("omicron") -> 16#003BF;
+entity("omid") -> 16#029B6;
+entity("ominus") -> 16#02296;
+entity("oopf") -> 16#1D560;
+entity("opar") -> 16#029B7;
+entity("operp") -> 16#029B9;
+entity("oplus") -> 16#02295;
+entity("or") -> 16#02228;
+entity("orarr") -> 16#021BB;
+entity("ord") -> 16#02A5D;
+entity("order") -> 16#02134;
+entity("orderof") -> 16#02134;
+entity("ordf") -> 16#000AA;
+entity("ordm") -> 16#000BA;
+entity("origof") -> 16#022B6;
+entity("oror") -> 16#02A56;
+entity("orslope") -> 16#02A57;
+entity("orv") -> 16#02A5B;
+entity("oscr") -> 16#02134;
+entity("oslash") -> 16#000F8;
+entity("osol") -> 16#02298;
+entity("otilde") -> 16#000F5;
+entity("otimes") -> 16#02297;
+entity("otimesas") -> 16#02A36;
+entity("ouml") -> 16#000F6;
+entity("ovbar") -> 16#0233D;
+entity("par") -> 16#02225;
+entity("para") -> 16#000B6;
+entity("parallel") -> 16#02225;
+entity("parsim") -> 16#02AF3;
+entity("parsl") -> 16#02AFD;
+entity("part") -> 16#02202;
+entity("pcy") -> 16#0043F;
+entity("percnt") -> 16#00025;
+entity("period") -> 16#0002E;
+entity("permil") -> 16#02030;
+entity("perp") -> 16#022A5;
+entity("pertenk") -> 16#02031;
+entity("pfr") -> 16#1D52D;
+entity("phi") -> 16#003C6;
+entity("phiv") -> 16#003D5;
+entity("phmmat") -> 16#02133;
+entity("phone") -> 16#0260E;
+entity("pi") -> 16#003C0;
+entity("pitchfork") -> 16#022D4;
+entity("piv") -> 16#003D6;
+entity("planck") -> 16#0210F;
+entity("planckh") -> 16#0210E;
+entity("plankv") -> 16#0210F;
+entity("plus") -> 16#0002B;
+entity("plusacir") -> 16#02A23;
+entity("plusb") -> 16#0229E;
+entity("pluscir") -> 16#02A22;
+entity("plusdo") -> 16#02214;
+entity("plusdu") -> 16#02A25;
+entity("pluse") -> 16#02A72;
+entity("plusmn") -> 16#000B1;
+entity("plussim") -> 16#02A26;
+entity("plustwo") -> 16#02A27;
+entity("pm") -> 16#000B1;
+entity("pointint") -> 16#02A15;
+entity("popf") -> 16#1D561;
+entity("pound") -> 16#000A3;
+entity("pr") -> 16#0227A;
+entity("prE") -> 16#02AB3;
+entity("prap") -> 16#02AB7;
+entity("prcue") -> 16#0227C;
+entity("pre") -> 16#02AAF;
+entity("prec") -> 16#0227A;
+entity("precapprox") -> 16#02AB7;
+entity("preccurlyeq") -> 16#0227C;
+entity("preceq") -> 16#02AAF;
+entity("precnapprox") -> 16#02AB9;
+entity("precneqq") -> 16#02AB5;
+entity("precnsim") -> 16#022E8;
+entity("precsim") -> 16#0227E;
+entity("prime") -> 16#02032;
+entity("primes") -> 16#02119;
+entity("prnE") -> 16#02AB5;
+entity("prnap") -> 16#02AB9;
+entity("prnsim") -> 16#022E8;
+entity("prod") -> 16#0220F;
+entity("profalar") -> 16#0232E;
+entity("profline") -> 16#02312;
+entity("profsurf") -> 16#02313;
+entity("prop") -> 16#0221D;
+entity("propto") -> 16#0221D;
+entity("prsim") -> 16#0227E;
+entity("prurel") -> 16#022B0;
+entity("pscr") -> 16#1D4C5;
+entity("psi") -> 16#003C8;
+entity("puncsp") -> 16#02008;
+entity("qfr") -> 16#1D52E;
+entity("qint") -> 16#02A0C;
+entity("qopf") -> 16#1D562;
+entity("qprime") -> 16#02057;
+entity("qscr") -> 16#1D4C6;
+entity("quaternions") -> 16#0210D;
+entity("quatint") -> 16#02A16;
+entity("quest") -> 16#0003F;
+entity("questeq") -> 16#0225F;
+entity("quot") -> 16#00022;
+entity("rAarr") -> 16#021DB;
+entity("rArr") -> 16#021D2;
+entity("rAtail") -> 16#0291C;
+entity("rBarr") -> 16#0290F;
+entity("rHar") -> 16#02964;
+entity("race") -> [16#0223D, 16#00331];
+entity("racute") -> 16#00155;
+entity("radic") -> 16#0221A;
+entity("raemptyv") -> 16#029B3;
+entity("rang") -> 16#027E9;
+entity("rangd") -> 16#02992;
+entity("range") -> 16#029A5;
+entity("rangle") -> 16#027E9;
+entity("raquo") -> 16#000BB;
+entity("rarr") -> 16#02192;
+entity("rarrap") -> 16#02975;
+entity("rarrb") -> 16#021E5;
+entity("rarrbfs") -> 16#02920;
+entity("rarrc") -> 16#02933;
+entity("rarrfs") -> 16#0291E;
+entity("rarrhk") -> 16#021AA;
+entity("rarrlp") -> 16#021AC;
+entity("rarrpl") -> 16#02945;
+entity("rarrsim") -> 16#02974;
+entity("rarrtl") -> 16#021A3;
+entity("rarrw") -> 16#0219D;
+entity("ratail") -> 16#0291A;
+entity("ratio") -> 16#02236;
+entity("rationals") -> 16#0211A;
+entity("rbarr") -> 16#0290D;
+entity("rbbrk") -> 16#02773;
+entity("rbrace") -> 16#0007D;
+entity("rbrack") -> 16#0005D;
+entity("rbrke") -> 16#0298C;
+entity("rbrksld") -> 16#0298E;
+entity("rbrkslu") -> 16#02990;
+entity("rcaron") -> 16#00159;
+entity("rcedil") -> 16#00157;
+entity("rceil") -> 16#02309;
+entity("rcub") -> 16#0007D;
+entity("rcy") -> 16#00440;
+entity("rdca") -> 16#02937;
+entity("rdldhar") -> 16#02969;
+entity("rdquo") -> 16#0201D;
+entity("rdquor") -> 16#0201D;
+entity("rdsh") -> 16#021B3;
+entity("real") -> 16#0211C;
+entity("realine") -> 16#0211B;
+entity("realpart") -> 16#0211C;
+entity("reals") -> 16#0211D;
+entity("rect") -> 16#025AD;
+entity("reg") -> 16#000AE;
+entity("rfisht") -> 16#0297D;
+entity("rfloor") -> 16#0230B;
+entity("rfr") -> 16#1D52F;
+entity("rhard") -> 16#021C1;
+entity("rharu") -> 16#021C0;
+entity("rharul") -> 16#0296C;
+entity("rho") -> 16#003C1;
+entity("rhov") -> 16#003F1;
+entity("rightarrow") -> 16#02192;
+entity("rightarrowtail") -> 16#021A3;
+entity("rightharpoondown") -> 16#021C1;
+entity("rightharpoonup") -> 16#021C0;
+entity("rightleftarrows") -> 16#021C4;
+entity("rightleftharpoons") -> 16#021CC;
+entity("rightrightarrows") -> 16#021C9;
+entity("rightsquigarrow") -> 16#0219D;
+entity("rightthreetimes") -> 16#022CC;
+entity("ring") -> 16#002DA;
+entity("risingdotseq") -> 16#02253;
+entity("rlarr") -> 16#021C4;
+entity("rlhar") -> 16#021CC;
+entity("rlm") -> 16#0200F;
+entity("rmoust") -> 16#023B1;
+entity("rmoustache") -> 16#023B1;
+entity("rnmid") -> 16#02AEE;
+entity("roang") -> 16#027ED;
+entity("roarr") -> 16#021FE;
+entity("robrk") -> 16#027E7;
+entity("ropar") -> 16#02986;
+entity("ropf") -> 16#1D563;
+entity("roplus") -> 16#02A2E;
+entity("rotimes") -> 16#02A35;
+entity("rpar") -> 16#00029;
+entity("rpargt") -> 16#02994;
+entity("rppolint") -> 16#02A12;
+entity("rrarr") -> 16#021C9;
+entity("rsaquo") -> 16#0203A;
+entity("rscr") -> 16#1D4C7;
+entity("rsh") -> 16#021B1;
+entity("rsqb") -> 16#0005D;
+entity("rsquo") -> 16#02019;
+entity("rsquor") -> 16#02019;
+entity("rthree") -> 16#022CC;
+entity("rtimes") -> 16#022CA;
+entity("rtri") -> 16#025B9;
+entity("rtrie") -> 16#022B5;
+entity("rtrif") -> 16#025B8;
+entity("rtriltri") -> 16#029CE;
+entity("ruluhar") -> 16#02968;
+entity("rx") -> 16#0211E;
+entity("sacute") -> 16#0015B;
+entity("sbquo") -> 16#0201A;
+entity("sc") -> 16#0227B;
+entity("scE") -> 16#02AB4;
+entity("scap") -> 16#02AB8;
+entity("scaron") -> 16#00161;
+entity("sccue") -> 16#0227D;
+entity("sce") -> 16#02AB0;
+entity("scedil") -> 16#0015F;
+entity("scirc") -> 16#0015D;
+entity("scnE") -> 16#02AB6;
+entity("scnap") -> 16#02ABA;
+entity("scnsim") -> 16#022E9;
+entity("scpolint") -> 16#02A13;
+entity("scsim") -> 16#0227F;
+entity("scy") -> 16#00441;
+entity("sdot") -> 16#022C5;
+entity("sdotb") -> 16#022A1;
+entity("sdote") -> 16#02A66;
+entity("seArr") -> 16#021D8;
+entity("searhk") -> 16#02925;
+entity("searr") -> 16#02198;
+entity("searrow") -> 16#02198;
+entity("sect") -> 16#000A7;
+entity("semi") -> 16#0003B;
+entity("seswar") -> 16#02929;
+entity("setminus") -> 16#02216;
+entity("setmn") -> 16#02216;
+entity("sext") -> 16#02736;
+entity("sfr") -> 16#1D530;
+entity("sfrown") -> 16#02322;
+entity("sharp") -> 16#0266F;
+entity("shchcy") -> 16#00449;
+entity("shcy") -> 16#00448;
+entity("shortmid") -> 16#02223;
+entity("shortparallel") -> 16#02225;
+entity("shy") -> 16#000AD;
+entity("sigma") -> 16#003C3;
+entity("sigmaf") -> 16#003C2;
+entity("sigmav") -> 16#003C2;
+entity("sim") -> 16#0223C;
+entity("simdot") -> 16#02A6A;
+entity("sime") -> 16#02243;
+entity("simeq") -> 16#02243;
+entity("simg") -> 16#02A9E;
+entity("simgE") -> 16#02AA0;
+entity("siml") -> 16#02A9D;
+entity("simlE") -> 16#02A9F;
+entity("simne") -> 16#02246;
+entity("simplus") -> 16#02A24;
+entity("simrarr") -> 16#02972;
+entity("slarr") -> 16#02190;
+entity("smallsetminus") -> 16#02216;
+entity("smashp") -> 16#02A33;
+entity("smeparsl") -> 16#029E4;
+entity("smid") -> 16#02223;
+entity("smile") -> 16#02323;
+entity("smt") -> 16#02AAA;
+entity("smte") -> 16#02AAC;
+entity("smtes") -> [16#02AAC, 16#0FE00];
+entity("softcy") -> 16#0044C;
+entity("sol") -> 16#0002F;
+entity("solb") -> 16#029C4;
+entity("solbar") -> 16#0233F;
+entity("sopf") -> 16#1D564;
+entity("spades") -> 16#02660;
+entity("spadesuit") -> 16#02660;
+entity("spar") -> 16#02225;
+entity("sqcap") -> 16#02293;
+entity("sqcaps") -> [16#02293, 16#0FE00];
+entity("sqcup") -> 16#02294;
+entity("sqcups") -> [16#02294, 16#0FE00];
+entity("sqsub") -> 16#0228F;
+entity("sqsube") -> 16#02291;
+entity("sqsubset") -> 16#0228F;
+entity("sqsubseteq") -> 16#02291;
+entity("sqsup") -> 16#02290;
+entity("sqsupe") -> 16#02292;
+entity("sqsupset") -> 16#02290;
+entity("sqsupseteq") -> 16#02292;
+entity("squ") -> 16#025A1;
+entity("square") -> 16#025A1;
+entity("squarf") -> 16#025AA;
+entity("squf") -> 16#025AA;
+entity("srarr") -> 16#02192;
+entity("sscr") -> 16#1D4C8;
+entity("ssetmn") -> 16#02216;
+entity("ssmile") -> 16#02323;
+entity("sstarf") -> 16#022C6;
+entity("star") -> 16#02606;
+entity("starf") -> 16#02605;
+entity("straightepsilon") -> 16#003F5;
+entity("straightphi") -> 16#003D5;
+entity("strns") -> 16#000AF;
+entity("sub") -> 16#02282;
+entity("subE") -> 16#02AC5;
+entity("subdot") -> 16#02ABD;
+entity("sube") -> 16#02286;
+entity("subedot") -> 16#02AC3;
+entity("submult") -> 16#02AC1;
+entity("subnE") -> 16#02ACB;
+entity("subne") -> 16#0228A;
+entity("subplus") -> 16#02ABF;
+entity("subrarr") -> 16#02979;
+entity("subset") -> 16#02282;
+entity("subseteq") -> 16#02286;
+entity("subseteqq") -> 16#02AC5;
+entity("subsetneq") -> 16#0228A;
+entity("subsetneqq") -> 16#02ACB;
+entity("subsim") -> 16#02AC7;
+entity("subsub") -> 16#02AD5;
+entity("subsup") -> 16#02AD3;
+entity("succ") -> 16#0227B;
+entity("succapprox") -> 16#02AB8;
+entity("succcurlyeq") -> 16#0227D;
+entity("succeq") -> 16#02AB0;
+entity("succnapprox") -> 16#02ABA;
+entity("succneqq") -> 16#02AB6;
+entity("succnsim") -> 16#022E9;
+entity("succsim") -> 16#0227F;
+entity("sum") -> 16#02211;
+entity("sung") -> 16#0266A;
+entity("sup") -> 16#02283;
+entity("sup1") -> 16#000B9;
+entity("sup2") -> 16#000B2;
+entity("sup3") -> 16#000B3;
+entity("supE") -> 16#02AC6;
+entity("supdot") -> 16#02ABE;
+entity("supdsub") -> 16#02AD8;
+entity("supe") -> 16#02287;
+entity("supedot") -> 16#02AC4;
+entity("suphsol") -> 16#027C9;
+entity("suphsub") -> 16#02AD7;
+entity("suplarr") -> 16#0297B;
+entity("supmult") -> 16#02AC2;
+entity("supnE") -> 16#02ACC;
+entity("supne") -> 16#0228B;
+entity("supplus") -> 16#02AC0;
+entity("supset") -> 16#02283;
+entity("supseteq") -> 16#02287;
+entity("supseteqq") -> 16#02AC6;
+entity("supsetneq") -> 16#0228B;
+entity("supsetneqq") -> 16#02ACC;
+entity("supsim") -> 16#02AC8;
+entity("supsub") -> 16#02AD4;
+entity("supsup") -> 16#02AD6;
+entity("swArr") -> 16#021D9;
+entity("swarhk") -> 16#02926;
+entity("swarr") -> 16#02199;
+entity("swarrow") -> 16#02199;
+entity("swnwar") -> 16#0292A;
+entity("szlig") -> 16#000DF;
+entity("target") -> 16#02316;
+entity("tau") -> 16#003C4;
+entity("tbrk") -> 16#023B4;
+entity("tcaron") -> 16#00165;
+entity("tcedil") -> 16#00163;
+entity("tcy") -> 16#00442;
+entity("tdot") -> 16#020DB;
+entity("telrec") -> 16#02315;
+entity("tfr") -> 16#1D531;
+entity("there4") -> 16#02234;
+entity("therefore") -> 16#02234;
+entity("theta") -> 16#003B8;
+entity("thetasym") -> 16#003D1;
+entity("thetav") -> 16#003D1;
+entity("thickapprox") -> 16#02248;
+entity("thicksim") -> 16#0223C;
+entity("thinsp") -> 16#02009;
+entity("thkap") -> 16#02248;
+entity("thksim") -> 16#0223C;
+entity("thorn") -> 16#000FE;
+entity("tilde") -> 16#002DC;
+entity("times") -> 16#000D7;
+entity("timesb") -> 16#022A0;
+entity("timesbar") -> 16#02A31;
+entity("timesd") -> 16#02A30;
+entity("tint") -> 16#0222D;
+entity("toea") -> 16#02928;
+entity("top") -> 16#022A4;
+entity("topbot") -> 16#02336;
+entity("topcir") -> 16#02AF1;
+entity("topf") -> 16#1D565;
+entity("topfork") -> 16#02ADA;
+entity("tosa") -> 16#02929;
+entity("tprime") -> 16#02034;
+entity("trade") -> 16#02122;
+entity("triangle") -> 16#025B5;
+entity("triangledown") -> 16#025BF;
+entity("triangleleft") -> 16#025C3;
+entity("trianglelefteq") -> 16#022B4;
+entity("triangleq") -> 16#0225C;
+entity("triangleright") -> 16#025B9;
+entity("trianglerighteq") -> 16#022B5;
+entity("tridot") -> 16#025EC;
+entity("trie") -> 16#0225C;
+entity("triminus") -> 16#02A3A;
+entity("triplus") -> 16#02A39;
+entity("trisb") -> 16#029CD;
+entity("tritime") -> 16#02A3B;
+entity("trpezium") -> 16#023E2;
+entity("tscr") -> 16#1D4C9;
+entity("tscy") -> 16#00446;
+entity("tshcy") -> 16#0045B;
+entity("tstrok") -> 16#00167;
+entity("twixt") -> 16#0226C;
+entity("twoheadleftarrow") -> 16#0219E;
+entity("twoheadrightarrow") -> 16#021A0;
+entity("uArr") -> 16#021D1;
+entity("uHar") -> 16#02963;
+entity("uacute") -> 16#000FA;
+entity("uarr") -> 16#02191;
+entity("ubrcy") -> 16#0045E;
+entity("ubreve") -> 16#0016D;
+entity("ucirc") -> 16#000FB;
+entity("ucy") -> 16#00443;
+entity("udarr") -> 16#021C5;
+entity("udblac") -> 16#00171;
+entity("udhar") -> 16#0296E;
+entity("ufisht") -> 16#0297E;
+entity("ufr") -> 16#1D532;
+entity("ugrave") -> 16#000F9;
+entity("uharl") -> 16#021BF;
+entity("uharr") -> 16#021BE;
+entity("uhblk") -> 16#02580;
+entity("ulcorn") -> 16#0231C;
+entity("ulcorner") -> 16#0231C;
+entity("ulcrop") -> 16#0230F;
+entity("ultri") -> 16#025F8;
+entity("umacr") -> 16#0016B;
+entity("uml") -> 16#000A8;
+entity("uogon") -> 16#00173;
+entity("uopf") -> 16#1D566;
+entity("uparrow") -> 16#02191;
+entity("updownarrow") -> 16#02195;
+entity("upharpoonleft") -> 16#021BF;
+entity("upharpoonright") -> 16#021BE;
+entity("uplus") -> 16#0228E;
+entity("upsi") -> 16#003C5;
+entity("upsih") -> 16#003D2;
+entity("upsilon") -> 16#003C5;
+entity("upuparrows") -> 16#021C8;
+entity("urcorn") -> 16#0231D;
+entity("urcorner") -> 16#0231D;
+entity("urcrop") -> 16#0230E;
+entity("uring") -> 16#0016F;
+entity("urtri") -> 16#025F9;
+entity("uscr") -> 16#1D4CA;
+entity("utdot") -> 16#022F0;
+entity("utilde") -> 16#00169;
+entity("utri") -> 16#025B5;
+entity("utrif") -> 16#025B4;
+entity("uuarr") -> 16#021C8;
+entity("uuml") -> 16#000FC;
+entity("uwangle") -> 16#029A7;
+entity("vArr") -> 16#021D5;
+entity("vBar") -> 16#02AE8;
+entity("vBarv") -> 16#02AE9;
+entity("vDash") -> 16#022A8;
+entity("vangrt") -> 16#0299C;
+entity("varepsilon") -> 16#003F5;
+entity("varkappa") -> 16#003F0;
+entity("varnothing") -> 16#02205;
+entity("varphi") -> 16#003D5;
+entity("varpi") -> 16#003D6;
+entity("varpropto") -> 16#0221D;
+entity("varr") -> 16#02195;
+entity("varrho") -> 16#003F1;
+entity("varsigma") -> 16#003C2;
+entity("varsubsetneq") -> [16#0228A, 16#0FE00];
+entity("varsubsetneqq") -> [16#02ACB, 16#0FE00];
+entity("varsupsetneq") -> [16#0228B, 16#0FE00];
+entity("varsupsetneqq") -> [16#02ACC, 16#0FE00];
+entity("vartheta") -> 16#003D1;
+entity("vartriangleleft") -> 16#022B2;
+entity("vartriangleright") -> 16#022B3;
+entity("vcy") -> 16#00432;
+entity("vdash") -> 16#022A2;
+entity("vee") -> 16#02228;
+entity("veebar") -> 16#022BB;
+entity("veeeq") -> 16#0225A;
+entity("vellip") -> 16#022EE;
+entity("verbar") -> 16#0007C;
+entity("vert") -> 16#0007C;
+entity("vfr") -> 16#1D533;
+entity("vltri") -> 16#022B2;
+entity("vnsub") -> [16#02282, 16#020D2];
+entity("vnsup") -> [16#02283, 16#020D2];
+entity("vopf") -> 16#1D567;
+entity("vprop") -> 16#0221D;
+entity("vrtri") -> 16#022B3;
+entity("vscr") -> 16#1D4CB;
+entity("vsubnE") -> [16#02ACB, 16#0FE00];
+entity("vsubne") -> [16#0228A, 16#0FE00];
+entity("vsupnE") -> [16#02ACC, 16#0FE00];
+entity("vsupne") -> [16#0228B, 16#0FE00];
+entity("vzigzag") -> 16#0299A;
+entity("wcirc") -> 16#00175;
+entity("wedbar") -> 16#02A5F;
+entity("wedge") -> 16#02227;
+entity("wedgeq") -> 16#02259;
+entity("weierp") -> 16#02118;
+entity("wfr") -> 16#1D534;
+entity("wopf") -> 16#1D568;
+entity("wp") -> 16#02118;
+entity("wr") -> 16#02240;
+entity("wreath") -> 16#02240;
+entity("wscr") -> 16#1D4CC;
+entity("xcap") -> 16#022C2;
+entity("xcirc") -> 16#025EF;
+entity("xcup") -> 16#022C3;
+entity("xdtri") -> 16#025BD;
+entity("xfr") -> 16#1D535;
+entity("xhArr") -> 16#027FA;
+entity("xharr") -> 16#027F7;
+entity("xi") -> 16#003BE;
+entity("xlArr") -> 16#027F8;
+entity("xlarr") -> 16#027F5;
+entity("xmap") -> 16#027FC;
+entity("xnis") -> 16#022FB;
+entity("xodot") -> 16#02A00;
+entity("xopf") -> 16#1D569;
+entity("xoplus") -> 16#02A01;
+entity("xotime") -> 16#02A02;
+entity("xrArr") -> 16#027F9;
+entity("xrarr") -> 16#027F6;
+entity("xscr") -> 16#1D4CD;
+entity("xsqcup") -> 16#02A06;
+entity("xuplus") -> 16#02A04;
+entity("xutri") -> 16#025B3;
+entity("xvee") -> 16#022C1;
+entity("xwedge") -> 16#022C0;
+entity("yacute") -> 16#000FD;
+entity("yacy") -> 16#0044F;
+entity("ycirc") -> 16#00177;
+entity("ycy") -> 16#0044B;
+entity("yen") -> 16#000A5;
+entity("yfr") -> 16#1D536;
+entity("yicy") -> 16#00457;
+entity("yopf") -> 16#1D56A;
+entity("yscr") -> 16#1D4CE;
+entity("yucy") -> 16#0044E;
+entity("yuml") -> 16#000FF;
+entity("zacute") -> 16#0017A;
+entity("zcaron") -> 16#0017E;
+entity("zcy") -> 16#00437;
+entity("zdot") -> 16#0017C;
+entity("zeetrf") -> 16#02128;
+entity("zeta") -> 16#003B6;
+entity("zfr") -> 16#1D537;
+entity("zhcy") -> 16#00436;
+entity("zigrarr") -> 16#021DD;
+entity("zopf") -> 16#1D56B;
+entity("zscr") -> 16#1D4CF;
+entity("zwj") -> 16#0200D;
+entity("zwnj") -> 16#0200C;
+entity(_) -> undefined.
 
 %%
 %% Tests
 %%
--include_lib("eunit/include/eunit.hrl").
 -ifdef(TEST).
+-include_lib("eunit/include/eunit.hrl").
 
 exhaustive_entity_test() ->
     T = mochiweb_cover:clause_lookup_table(?MODULE, entity),

http://git-wip-us.apache.org/repos/asf/couchdb/blob/cbb8a550/src/mochiweb/mochiweb_cookies.erl
----------------------------------------------------------------------
diff --git a/src/mochiweb/mochiweb_cookies.erl b/src/mochiweb/mochiweb_cookies.erl
index ee91d0c..1cc4e91 100644
--- a/src/mochiweb/mochiweb_cookies.erl
+++ b/src/mochiweb/mochiweb_cookies.erl
@@ -23,6 +23,7 @@
 
 %% @type proplist() = [{Key::string(), Value::string()}].
 %% @type header() = {Name::string(), Value::string()}.
+%% @type int_seconds() = integer().
 
 %% @spec cookie(Key::string(), Value::string()) -> header()
 %% @doc Short-hand for <code>cookie(Key, Value, [])</code>.
@@ -30,7 +31,7 @@ cookie(Key, Value) ->
     cookie(Key, Value, []).
 
 %% @spec cookie(Key::string(), Value::string(), Options::[Option]) -> header()
-%% where Option = {max_age, integer()} | {local_time, {date(), time()}}
+%% where Option = {max_age, int_seconds()} | {local_time, {date(), time()}}
 %%                | {domain, string()} | {path, string()}
 %%                | {secure, true | false} | {http_only, true | false}
 %%
@@ -49,9 +50,9 @@ cookie(Key, Value, Options) ->
             RawAge ->
                 When = case proplists:get_value(local_time, Options) of
                            undefined ->
-                               calendar:universal_time();
+                               calendar:local_time();
                            LocalTime ->
-                               erlang:localtime_to_universaltime(LocalTime)
+                               LocalTime
                        end,
                 Age = case RawAge < 0 of
                           true ->
@@ -115,12 +116,33 @@ quote(V0) ->
         orelse erlang:error({cookie_quoting_required, V}),
     V.
 
-add_seconds(Secs, UniversalTime) ->
-    Greg = calendar:datetime_to_gregorian_seconds(UniversalTime),
+
+%% Return a date in the form of: Wdy, DD-Mon-YYYY HH:MM:SS GMT
+%% See also: rfc2109: 10.1.2
+rfc2109_cookie_expires_date(LocalTime) ->
+    {{YYYY,MM,DD},{Hour,Min,Sec}} =
+        case calendar:local_time_to_universal_time_dst(LocalTime) of
+            [] ->
+                {Date, {Hour1, Min1, Sec1}} = LocalTime,
+                LocalTime2 = {Date, {Hour1 + 1, Min1, Sec1}},
+                case calendar:local_time_to_universal_time_dst(LocalTime2) of
+                    [Gmt]   -> Gmt;
+                    [_,Gmt] -> Gmt
+                end;
+            [Gmt]   -> Gmt;
+            [_,Gmt] -> Gmt
+        end,
+    DayNumber = calendar:day_of_the_week({YYYY,MM,DD}),
+    lists:flatten(
+      io_lib:format("~s, ~2.2.0w-~3.s-~4.4.0w ~2.2.0w:~2.2.0w:~2.2.0w GMT",
+                    [httpd_util:day(DayNumber),DD,httpd_util:month(MM),YYYY,Hour,Min,Sec])).
+
+add_seconds(Secs, LocalTime) ->
+    Greg = calendar:datetime_to_gregorian_seconds(LocalTime),
     calendar:gregorian_seconds_to_datetime(Greg + Secs).
 
-age_to_cookie_date(Age, UniversalTime) ->
-    couch_util:rfc1123_date(add_seconds(Age, UniversalTime)).
+age_to_cookie_date(Age, LocalTime) ->
+    rfc2109_cookie_expires_date(add_seconds(Age, LocalTime)).
 
 %% @spec parse_cookie(string()) -> [{K::string(), V::string()}]
 %% @doc Parse the contents of a Cookie header field, ignoring cookie
@@ -203,8 +225,8 @@ any_to_list(V) when is_integer(V) ->
 %%
 %% Tests
 %%
--include_lib("eunit/include/eunit.hrl").
 -ifdef(TEST).
+-include_lib("eunit/include/eunit.hrl").
 
 quote_test() ->
     %% ?assertError eunit macro is not compatible with coverage module
@@ -293,14 +315,14 @@ cookie_test() ->
     C2 = {"Set-Cookie",
           "Customer=WILE_E_COYOTE; "
           "Version=1; "
-          "Expires=Tue, 15 May 2007 13:45:33 GMT; "
+          "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; "
+          "Expires=Wed, 16-May-2007 13:45:50 GMT; "
           "Max-Age=86417"},
     C3 = cookie("Customer", "WILE_E_COYOTE",
                 [{max_age, 86417}, {local_time, LocalTime}]),

http://git-wip-us.apache.org/repos/asf/couchdb/blob/cbb8a550/src/mochiweb/mochiweb_cover.erl
----------------------------------------------------------------------
diff --git a/src/mochiweb/mochiweb_cover.erl b/src/mochiweb/mochiweb_cover.erl
index 6a14ef5..aa075d5 100644
--- a/src/mochiweb/mochiweb_cover.erl
+++ b/src/mochiweb/mochiweb_cover.erl
@@ -46,8 +46,8 @@ clause_fold(_, Acc) ->
 %%
 %% Tests
 %%
--include_lib("eunit/include/eunit.hrl").
 -ifdef(TEST).
+-include_lib("eunit/include/eunit.hrl").
 foo_table(a) -> b;
 foo_table("a") -> <<"b">>;
 foo_table(123) -> {4, 3, 2};

http://git-wip-us.apache.org/repos/asf/couchdb/blob/cbb8a550/src/mochiweb/mochiweb_echo.erl
----------------------------------------------------------------------
diff --git a/src/mochiweb/mochiweb_echo.erl b/src/mochiweb/mochiweb_echo.erl
index 6f7872b..e145840 100644
--- a/src/mochiweb/mochiweb_echo.erl
+++ b/src/mochiweb/mochiweb_echo.erl
@@ -11,11 +11,14 @@ 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}}]).
+    mochiweb_socket_server:start([{link, false} | options()]).
+
+options() ->
+    [{name, ?MODULE},
+     {port, 6789},
+     {ip, "127.0.0.1"},
+     {max, 1},
+     {loop, {?MODULE, loop}}].
 
 loop(Socket) ->
     case mochiweb_socket:recv(Socket, 0, 30000) of
@@ -33,6 +36,6 @@ loop(Socket) ->
 %%
 %% Tests
 %%
--include_lib("eunit/include/eunit.hrl").
 -ifdef(TEST).
+-include_lib("eunit/include/eunit.hrl").
 -endif.

http://git-wip-us.apache.org/repos/asf/couchdb/blob/cbb8a550/src/mochiweb/mochiweb_headers.erl
----------------------------------------------------------------------
diff --git a/src/mochiweb/mochiweb_headers.erl b/src/mochiweb/mochiweb_headers.erl
index 4fce983..b49cf9e 100644
--- a/src/mochiweb/mochiweb_headers.erl
+++ b/src/mochiweb/mochiweb_headers.erl
@@ -6,7 +6,7 @@
 -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([delete_any/2, get_primary_value/2, get_combined_value/2]).
 -export([default/3, enter_from_list/2, default_from_list/2]).
 -export([to_list/1, make/1]).
 -export([from_binary/1]).
@@ -24,8 +24,8 @@ empty() ->
 %% @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) ->
+%% assume a non-list is already mochiweb_headers.
+make(T) ->
     T.
 
 %% @spec from_binary(iolist()) -> headers()
@@ -112,6 +112,34 @@ get_primary_value(K, T) ->
             lists:takewhile(fun (C) -> C =/= $; end, V)
     end.
 
+%% @spec get_combined_value(key(), headers()) -> string() | undefined
+%% @doc Return the value from the given header using a case insensitive search.
+%%      If the value of the header is a comma-separated list where holds values
+%%      are all identical, the identical value will be returned.
+%%      undefined will be returned for keys that are not present or the
+%%      values in the list are not the same.
+%%
+%%      NOTE: The process isn't designed for a general purpose. If you need
+%%            to access all values in the combined header, please refer to
+%%            '''tokenize_header_value/1'''.
+%%
+%%      Section 4.2 of the RFC 2616 (HTTP 1.1) describes multiple message-header
+%%      fields with the same field-name may be present in a message if and only
+%%      if the entire field-value for that header field is defined as a
+%%      comma-separated list [i.e., #(values)].
+get_combined_value(K, T) ->
+    case get_value(K, T) of
+        undefined ->
+            undefined;
+        V ->
+            case sets:to_list(sets:from_list(tokenize_header_value(V))) of
+                [Val] ->
+                    Val;
+                _ ->
+                    undefined
+            end
+    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
@@ -164,6 +192,49 @@ delete_any(K, T) ->
 
 %% Internal API
 
+tokenize_header_value(undefined) ->
+    undefined;
+tokenize_header_value(V) ->
+    reversed_tokens(trim_and_reverse(V, false), [], []).
+
+trim_and_reverse([S | Rest], Reversed) when S=:=$ ; S=:=$\n; S=:=$\t ->
+    trim_and_reverse(Rest, Reversed);
+trim_and_reverse(V, false) ->
+    trim_and_reverse(lists:reverse(V), true);
+trim_and_reverse(V, true) ->
+    V.
+
+reversed_tokens([], [], Acc) ->
+    Acc;
+reversed_tokens([], Token, Acc) ->
+    [Token | Acc];
+reversed_tokens("\"" ++ Rest, [], Acc) ->
+    case extract_quoted_string(Rest, []) of
+        {String, NewRest} ->
+            reversed_tokens(NewRest, [], [String | Acc]);
+        undefined ->
+            undefined
+    end;
+reversed_tokens("\"" ++ _Rest, _Token, _Acc) ->
+    undefined;
+reversed_tokens([C | Rest], [], Acc) when C=:=$ ;C=:=$\n;C=:=$\t;C=:=$, ->
+    reversed_tokens(Rest, [], Acc);
+reversed_tokens([C | Rest], Token, Acc) when C=:=$ ;C=:=$\n;C=:=$\t;C=:=$, ->
+    reversed_tokens(Rest, [], [Token | Acc]);
+reversed_tokens([C | Rest], Token, Acc) ->
+    reversed_tokens(Rest, [C | Token], Acc);
+reversed_tokens(_, _, _) ->
+    undefeined.
+
+extract_quoted_string([], _Acc) ->
+    undefined;
+extract_quoted_string("\"\\" ++ Rest, Acc) ->
+    extract_quoted_string(Rest, "\"" ++ Acc);
+extract_quoted_string("\"" ++ Rest, Acc) ->
+    {Acc, Rest};
+extract_quoted_string([C | Rest], Acc) ->
+    extract_quoted_string(Rest, [C | Acc]).
+
 expand({array, L}) ->
     mochiweb_util:join(lists:reverse(L), ", ");
 expand(V) ->
@@ -195,8 +266,8 @@ any_to_list(V) when is_integer(V) ->
 %%
 %% Tests.
 %%
--include_lib("eunit/include/eunit.hrl").
 -ifdef(TEST).
+-include_lib("eunit/include/eunit.hrl").
 
 make_test() ->
     Identity = make([{hdr, foo}]),
@@ -237,6 +308,37 @@ get_primary_value_test() ->
        get_primary_value(<<"baz">>, H)),
     ok.
 
+get_combined_value_test() ->
+    H = make([{hdr, foo}, {baz, <<"wibble,taco">>}, {content_length, "123, 123"},
+              {test, " 123,  123,     123  , 123,123 "},
+              {test2, "456,  123,     123  , 123"},
+              {test3, "123"}, {test4, " 123, "}]),
+    ?assertEqual(
+       "foo",
+       get_combined_value(hdr, H)),
+    ?assertEqual(
+       undefined,
+       get_combined_value(bar, H)),
+    ?assertEqual(
+       undefined,
+       get_combined_value(<<"baz">>, H)),
+    ?assertEqual(
+       "123",
+       get_combined_value(<<"content_length">>, H)),
+    ?assertEqual(
+       "123",
+       get_combined_value(<<"test">>, H)),
+    ?assertEqual(
+       undefined,
+       get_combined_value(<<"test2">>, H)),
+    ?assertEqual(
+       "123",
+       get_combined_value(<<"test3">>, H)),
+    ?assertEqual(
+       "123",
+       get_combined_value(<<"test4">>, H)),
+    ok.
+
 set_cookie_test() ->
     H = make([{"set-cookie", foo}, {"set-cookie", bar}, {"set-cookie", baz}]),
     ?assertEqual(
@@ -296,4 +398,23 @@ headers_test() ->
     [] = ?MODULE:to_list(?MODULE:from_binary([<<"\r\n\r\n">>])),
     ok.
 
+tokenize_header_value_test() ->
+    ?assertEqual(["a quote in a \"quote\"."],
+                 tokenize_header_value("\"a quote in a \\\"quote\\\".\"")),
+    ?assertEqual(["abc"], tokenize_header_value("abc")),
+    ?assertEqual(["abc", "def"], tokenize_header_value("abc def")),
+    ?assertEqual(["abc", "def"], tokenize_header_value("abc , def")),
+    ?assertEqual(["abc", "def"], tokenize_header_value(",abc ,, def,,")),
+    ?assertEqual(["abc def"], tokenize_header_value("\"abc def\"      ")),
+    ?assertEqual(["abc, def"], tokenize_header_value("\"abc, def\"")),
+    ?assertEqual(["\\a\\$"], tokenize_header_value("\"\\a\\$\"")),
+    ?assertEqual(["abc def", "foo, bar", "12345", ""],
+                 tokenize_header_value("\"abc def\" \"foo, bar\" , 12345, \"\"")),
+    ?assertEqual(undefined,
+                 tokenize_header_value(undefined)),
+    ?assertEqual(undefined,
+                 tokenize_header_value("umatched quote\"")),
+    ?assertEqual(undefined,
+                 tokenize_header_value("\"unmatched quote")).
+
 -endif.


[6/8] git commit: updated refs/heads/master to d751a39

Posted by dc...@apache.org.
COUCHDB-627 - Support all timezones

Forward-ported over top of MochiWeb 2.4.2, comprising specific chunks
of b1a049bb, 445e919, 010522e, 317c97f lost during MochiWeb upgrade.

Some timezones are incorrectly handled by OTP's calendar module. The
ironic thing is that we only ever need the time in GMT (for HTTP
response headers and the log file).

This patch duplicates httpd_util:rfc1123_date/0 and /1 but uses
universal time everywhere, avoiding the broken conversion code.

- Support all timezones for R14 series.
- the _dst variant does exist in R14B04.
- Fix local to universal handling.

Also relates to COUCHDB-1513, a duplicate of COUCHDB-627.


Project: http://git-wip-us.apache.org/repos/asf/couchdb/repo
Commit: http://git-wip-us.apache.org/repos/asf/couchdb/commit/cdf8949d
Tree: http://git-wip-us.apache.org/repos/asf/couchdb/tree/cdf8949d
Diff: http://git-wip-us.apache.org/repos/asf/couchdb/diff/cdf8949d

Branch: refs/heads/master
Commit: cdf8949d341722423828471ca45d71f2fcb3a36d
Parents: b67b03e
Author: Robert Newson <rn...@apache.org>
Authored: Tue Mar 19 23:16:07 2013 +0100
Committer: Dave Cottlehuber <dc...@apache.org>
Committed: Thu Apr 25 00:13:57 2013 +0200

----------------------------------------------------------------------
 src/mochiweb/mochiweb_cookies.erl |    4 ++--
 src/mochiweb/mochiweb_request.erl |    6 +++---
 2 files changed, 5 insertions(+), 5 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/couchdb/blob/cdf8949d/src/mochiweb/mochiweb_cookies.erl
----------------------------------------------------------------------
diff --git a/src/mochiweb/mochiweb_cookies.erl b/src/mochiweb/mochiweb_cookies.erl
index 1cc4e91..3b71f4e 100644
--- a/src/mochiweb/mochiweb_cookies.erl
+++ b/src/mochiweb/mochiweb_cookies.erl
@@ -50,9 +50,9 @@ cookie(Key, Value, Options) ->
             RawAge ->
                 When = case proplists:get_value(local_time, Options) of
                            undefined ->
-                               calendar:local_time();
+                               calendar:universal_time();
                            LocalTime ->
-                               LocalTime
+                               calendar:local_time_to_universal_time_dst(LocalTime)
                        end,
                 Age = case RawAge < 0 of
                           true ->

http://git-wip-us.apache.org/repos/asf/couchdb/blob/cdf8949d/src/mochiweb/mochiweb_request.erl
----------------------------------------------------------------------
diff --git a/src/mochiweb/mochiweb_request.erl b/src/mochiweb/mochiweb_request.erl
index 1b431d3..2727fc7 100644
--- a/src/mochiweb/mochiweb_request.erl
+++ b/src/mochiweb/mochiweb_request.erl
@@ -621,9 +621,9 @@ maybe_redirect(RelPath, FullPath, ExtraHeaders,
     end.
 
 maybe_serve_file(File, ExtraHeaders, {?MODULE, [_Socket, _Method, _RawPath, _Version, _Headers]}=THIS) ->
-    case file:read_file_info(File) of
+    case file:read_file_info(File, [{time, universal}]) of
         {ok, FileInfo} ->
-            LastModified = httpd_util:rfc1123_date(FileInfo#file_info.mtime),
+            LastModified = couch_util:rfc1123_date(FileInfo#file_info.mtime),
             case get_header_value("if-modified-since", THIS) of
                 LastModified ->
                     respond({304, ExtraHeaders, ""}, THIS);
@@ -647,7 +647,7 @@ maybe_serve_file(File, ExtraHeaders, {?MODULE, [_Socket, _Method, _RawPath, _Ver
 
 server_headers() ->
     [{"Server", "MochiWeb/1.0 (" ++ ?QUIP ++ ")"},
-     {"Date", httpd_util:rfc1123_date()}].
+     {"Date", couch_util:rfc1123_date()}].
 
 make_code(X) when is_integer(X) ->
     [integer_to_list(X), [" " | httpd_util:reason_phrase(X)]];