You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@couchdb.apache.org by da...@apache.org on 2014/02/12 07:22:08 UTC

[36/50] [abbrv] mochiweb commit: updated refs/heads/import-master to 3a54dbf

Upgrade to Mochiweb 1.4.1.

Modifications from upstream

1) Makefile is replaced by Makefile.am
2) mochiweb.app.in is custom to us.

git-svn-id: https://svn.apache.org/repos/asf/couchdb/trunk@1040473 13f79535-47bb-0310-9956-ffa450edef68


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

Branch: refs/heads/import-master
Commit: 964f4656d8270bd9ec327089b65f6f35600b37b3
Parents: a89afc6
Author: Robert Newson <rn...@apache.org>
Authored: Tue Nov 30 11:57:08 2010 +0000
Committer: Robert Newson <rn...@apache.org>
Committed: Tue Nov 30 11:57:08 2010 +0000

----------------------------------------------------------------------
 Makefile.am                |   2 +-
 mochijson2.erl             |  51 +++++++++-
 mochinum.erl               |  77 +++++++++-----
 mochiweb.app.in            |   2 +-
 mochiweb.app.src           |   2 +-
 mochiweb_acceptor.erl      |   2 +-
 mochiweb_html.erl          | 219 ++++++++++++++++++++++++++++++++++++++--
 mochiweb_http.erl          |  53 ++++++----
 mochiweb_request.erl       |   9 +-
 mochiweb_request_tests.erl |  63 ++++++++++++
 mochiweb_socket_server.erl | 104 +++++++++++++++++--
 mochiweb_util.erl          |   2 +-
 12 files changed, 516 insertions(+), 70 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/couchdb-mochiweb/blob/964f4656/Makefile.am
----------------------------------------------------------------------
diff --git a/Makefile.am b/Makefile.am
index b622ff0..e14a529 100644
--- a/Makefile.am
+++ b/Makefile.am
@@ -14,7 +14,7 @@ if USE_NATIVE_MOCHIJSON
 MOCHIJSON_ERLC_FLAGS=+native
 endif
 
-mochiwebebindir = $(localerlanglibdir)/mochiweb-7c2bc2/ebin
+mochiwebebindir = $(localerlanglibdir)/mochiweb-1.4.1/ebin
 
 mochiweb_file_collection = \
 	mochifmt.erl \

http://git-wip-us.apache.org/repos/asf/couchdb-mochiweb/blob/964f4656/mochijson2.erl
----------------------------------------------------------------------
diff --git a/mochijson2.erl b/mochijson2.erl
index 64cabc8..bdf6d77 100644
--- a/mochijson2.erl
+++ b/mochijson2.erl
@@ -4,6 +4,38 @@
 %% @doc Yet another JSON (RFC 4627) library for Erlang. mochijson2 works
 %%      with binaries as strings, arrays as lists (without an {array, _})
 %%      wrapper and it only knows how to decode UTF-8 (and ASCII).
+%%
+%%      JSON terms are decoded as follows (javascript -> erlang):
+%%      <ul>
+%%          <li>{"key": "value"} ->
+%%              {struct, [{&lt;&lt;"key">>, &lt;&lt;"value">>}]}</li>
+%%          <li>["array", 123, 12.34, true, false, null] ->
+%%              [&lt;&lt;"array">>, 123, 12.34, true, false, null]
+%%          </li>
+%%      </ul>
+%%      <ul>
+%%          <li>Strings in JSON decode to UTF-8 binaries in Erlang</li>
+%%          <li>Objects decode to {struct, PropList}</li>
+%%          <li>Numbers decode to integer or float</li>
+%%          <li>true, false, null decode to their respective terms.</li>
+%%      </ul>
+%%      The encoder will accept the same format that the decoder will produce,
+%%      but will also allow additional cases for leniency:
+%%      <ul>
+%%          <li>atoms other than true, false, null will be considered UTF-8
+%%              strings (even as a proplist key)
+%%          </li>
+%%          <li>{json, IoList} will insert IoList directly into the output
+%%              with no validation
+%%          </li>
+%%          <li>{array, Array} will be encoded as Array
+%%              (legacy mochijson style)
+%%          </li>
+%%          <li>A non-empty raw proplist will be encoded as an object as long
+%%              as the first pair does not have an atom key of json, struct,
+%%              or array
+%%          </li>
+%%      </ul>
 
 -module(mochijson2).
 -author('bob@mochimedia.com').
@@ -101,10 +133,16 @@ json_encode(F, _State) when is_float(F) ->
     mochinum:digits(F);
 json_encode(S, State) when is_binary(S); is_atom(S) ->
     json_encode_string(S, State);
-json_encode(Array, State) when is_list(Array) ->
-    json_encode_array(Array, State);
+json_encode([{K, _}|_] = Props, State) when (K =/= struct andalso
+                                             K =/= array andalso
+                                             K =/= json) ->
+    json_encode_proplist(Props, State);
 json_encode({struct, Props}, State) when is_list(Props) ->
     json_encode_proplist(Props, State);
+json_encode(Array, State) when is_list(Array) ->
+    json_encode_array(Array, State);
+json_encode({array, Array}, State) when is_list(Array) ->
+    json_encode_array(Array, State);
 json_encode({json, IoList}, _State) ->
     IoList;
 json_encode(Bad, #encoder{handler=null}) ->
@@ -732,6 +770,15 @@ key_encode_test() ->
     ?assertEqual(
        <<"{\"foo\":1}">>,
        iolist_to_binary(encode({struct, [{"foo", 1}]}))),
+	?assertEqual(
+       <<"{\"foo\":1}">>,
+       iolist_to_binary(encode([{foo, 1}]))),
+    ?assertEqual(
+       <<"{\"foo\":1}">>,
+       iolist_to_binary(encode([{<<"foo">>, 1}]))),
+    ?assertEqual(
+       <<"{\"foo\":1}">>,
+       iolist_to_binary(encode([{"foo", 1}]))),
     ?assertEqual(
        <<"{\"\\ud834\\udd20\":1}">>,
        iolist_to_binary(

http://git-wip-us.apache.org/repos/asf/couchdb-mochiweb/blob/964f4656/mochinum.erl
----------------------------------------------------------------------
diff --git a/mochinum.erl b/mochinum.erl
index a7e2bfb..3c96b13 100644
--- a/mochinum.erl
+++ b/mochinum.erl
@@ -29,11 +29,10 @@ digits(N) when is_integer(N) ->
 digits(0.0) ->
     "0.0";
 digits(Float) ->
-    {Frac, Exp} = frexp(Float),
-    Exp1 = Exp - 53,
-    Frac1 = trunc(abs(Frac) * (1 bsl 53)),
-    [Place | Digits] = digits1(Float, Exp1, Frac1),
-    R = insert_decimal(Place, [$0 + D || D <- Digits]),
+    {Frac1, Exp1} = frexp_int(Float),
+    [Place0 | Digits0] = digits1(Float, Exp1, Frac1),
+    {Place, Digits} = transform_digits(Place0, Digits0),
+    R = insert_decimal(Place, Digits),
     case Float < 0 of
         true ->
             [$- | R];
@@ -64,7 +63,6 @@ int_pow(X, N) when N > 0 ->
 int_ceil(X) ->
     T = trunc(X),
     case (X - T) of
-        Neg when Neg < 0 -> T;
         Pos when Pos > 0 -> T + 1;
         _ -> T
     end.
@@ -228,6 +226,20 @@ log2floor(Int, N) ->
     log2floor(Int bsr 1, 1 + N).
 
 
+transform_digits(Place, [0 | Rest]) ->
+    transform_digits(Place, Rest);
+transform_digits(Place, Digits) ->
+    {Place, [$0 + D || D <- Digits]}.
+
+
+frexp_int(F) ->
+    case unpack(F) of
+        {_Sign, 0, Frac} ->
+            {Frac, ?MIN_EXP};
+        {_Sign, Exp, Frac} ->
+            {Frac + (1 bsl 52), Exp - 53 - ?FLOAT_BIAS}
+    end.
+
 %%
 %% Tests
 %%
@@ -235,21 +247,21 @@ log2floor(Int, N) ->
 -ifdef(TEST).
 
 int_ceil_test() ->
-    1 = int_ceil(0.0001),
-    0 = int_ceil(0.0),
-    1 = int_ceil(0.99),
-    1 = int_ceil(1.0),
-    -1 = int_ceil(-1.5),
-    -2 = int_ceil(-2.0),
+    ?assertEqual(1, int_ceil(0.0001)),
+    ?assertEqual(0, int_ceil(0.0)),
+    ?assertEqual(1, int_ceil(0.99)),
+    ?assertEqual(1, int_ceil(1.0)),
+    ?assertEqual(-1, int_ceil(-1.5)),
+    ?assertEqual(-2, int_ceil(-2.0)),
     ok.
 
 int_pow_test() ->
-    1 = int_pow(1, 1),
-    1 = int_pow(1, 0),
-    1 = int_pow(10, 0),
-    10 = int_pow(10, 1),
-    100 = int_pow(10, 2),
-    1000 = int_pow(10, 3),
+    ?assertEqual(1, int_pow(1, 1)),
+    ?assertEqual(1, int_pow(1, 0)),
+    ?assertEqual(1, int_pow(10, 0)),
+    ?assertEqual(10, int_pow(10, 1)),
+    ?assertEqual(100, int_pow(10, 2)),
+    ?assertEqual(1000, int_pow(10, 3)),
     ok.
 
 digits_test() ->
@@ -274,9 +286,9 @@ digits_test() ->
     ?assertEqual("4503599627370496.0",
                  digits(4503599627370496.0)),
     %% small denormalized number
-    %% 4.94065645841246544177e-324
+    %% 4.94065645841246544177e-324 =:= 5.0e-324
     <<SmallDenorm/float>> = <<0,0,0,0,0,0,0,1>>,
-    ?assertEqual("4.9406564584124654e-324",
+    ?assertEqual("5.0e-324",
                  digits(SmallDenorm)),
     ?assertEqual(SmallDenorm,
                  list_to_float(digits(SmallDenorm))),
@@ -301,31 +313,42 @@ digits_test() ->
                  digits(LargeNorm)),
     ?assertEqual(LargeNorm,
                  list_to_float(digits(LargeNorm))),
+    %% issue #10 - mochinum:frexp(math:pow(2, -1074)).
+    ?assertEqual("5.0e-324",
+                 digits(math:pow(2, -1074))),
     ok.
 
 frexp_test() ->
     %% zero
-    {0.0, 0} = frexp(0.0),
+    ?assertEqual({0.0, 0}, frexp(0.0)),
     %% one
-    {0.5, 1} = frexp(1.0),
+    ?assertEqual({0.5, 1}, frexp(1.0)),
     %% negative one
-    {-0.5, 1} = frexp(-1.0),
+    ?assertEqual({-0.5, 1}, frexp(-1.0)),
     %% small denormalized number
     %% 4.94065645841246544177e-324
     <<SmallDenorm/float>> = <<0,0,0,0,0,0,0,1>>,
-    {0.5, -1073} = frexp(SmallDenorm),
+    ?assertEqual({0.5, -1073}, frexp(SmallDenorm)),
     %% large denormalized number
     %% 2.22507385850720088902e-308
     <<BigDenorm/float>> = <<0,15,255,255,255,255,255,255>>,
-    {0.99999999999999978, -1022} = frexp(BigDenorm),
+    ?assertEqual(
+       {0.99999999999999978, -1022},
+       frexp(BigDenorm)),
     %% small normalized number
     %% 2.22507385850720138309e-308
     <<SmallNorm/float>> = <<0,16,0,0,0,0,0,0>>,
-    {0.5, -1021} = frexp(SmallNorm),
+    ?assertEqual({0.5, -1021}, frexp(SmallNorm)),
     %% large normalized number
     %% 1.79769313486231570815e+308
     <<LargeNorm/float>> = <<127,239,255,255,255,255,255,255>>,
-    {0.99999999999999989, 1024} = frexp(LargeNorm),
+    ?assertEqual(
+        {0.99999999999999989, 1024},
+        frexp(LargeNorm)),
+    %% issue #10 - mochinum:frexp(math:pow(2, -1074)).
+    ?assertEqual(
+       {0.5, -1073},
+       frexp(math:pow(2, -1074))),
     ok.
 
 -endif.

http://git-wip-us.apache.org/repos/asf/couchdb-mochiweb/blob/964f4656/mochiweb.app.in
----------------------------------------------------------------------
diff --git a/mochiweb.app.in b/mochiweb.app.in
index c6a2630..6a4a314 100644
--- a/mochiweb.app.in
+++ b/mochiweb.app.in
@@ -1,6 +1,6 @@
 {application, mochiweb,
  [{description, "MochiMedia Web Server"},
-  {vsn, "7c2bc2"},
+  {vsn, "1.4.1"},
   {modules, [
         mochihex,
         mochijson,

http://git-wip-us.apache.org/repos/asf/couchdb-mochiweb/blob/964f4656/mochiweb.app.src
----------------------------------------------------------------------
diff --git a/mochiweb.app.src b/mochiweb.app.src
index a1c95aa..37a21fb 100644
--- a/mochiweb.app.src
+++ b/mochiweb.app.src
@@ -1,7 +1,7 @@
 %% This is generated from src/mochiweb.app.src
 {application, mochiweb,
  [{description, "MochiMedia Web Server"},
-  {vsn, "7c2bc2"},
+  {vsn, "1.4.1"},
   {modules, []},
   {registered, []},
   {mod, {mochiweb_app, []}},

http://git-wip-us.apache.org/repos/asf/couchdb-mochiweb/blob/964f4656/mochiweb_acceptor.erl
----------------------------------------------------------------------
diff --git a/mochiweb_acceptor.erl b/mochiweb_acceptor.erl
index 79d172c..20a9b4b 100644
--- a/mochiweb_acceptor.erl
+++ b/mochiweb_acceptor.erl
@@ -22,7 +22,7 @@ init(Server, Listen, Loop) ->
         {error, closed} ->
             exit(normal);
         {error, timeout} ->
-            exit(normal);
+            init(Server, Listen, Loop);
         {error, esslaccept} ->
             exit(normal);
         Other ->

http://git-wip-us.apache.org/repos/asf/couchdb-mochiweb/blob/964f4656/mochiweb_html.erl
----------------------------------------------------------------------
diff --git a/mochiweb_html.erl b/mochiweb_html.erl
index a15c359..0f281db 100644
--- a/mochiweb_html.erl
+++ b/mochiweb_html.erl
@@ -131,6 +131,11 @@ to_html([], Acc) ->
     lists:reverse(Acc);
 to_html([{'=', Content} | Rest], Acc) ->
     to_html(Rest, [Content | Acc]);
+to_html([{pi, Bin} | Rest], Acc) ->
+    Open = [<<"<?">>,
+            Bin,
+            <<"?>">>],
+    to_html(Rest, [Open | Acc]);
 to_html([{pi, Tag, Attrs} | Rest], Acc) ->
     Open = [<<"<?">>,
             Tag,
@@ -216,6 +221,9 @@ to_tokens([{Tag0, [T0={'=', _C0} | R1]} | Rest], Acc) ->
 to_tokens([{Tag0, [T0={comment, _C0} | R1]} | Rest], Acc) ->
     %% Allow {comment, iolist()}
     to_tokens([{Tag0, R1} | Rest], [T0 | Acc]);
+to_tokens([{Tag0, [T0={pi, _S0} | R1]} | Rest], Acc) ->
+    %% Allow {pi, binary()}
+    to_tokens([{Tag0, R1} | Rest], [T0 | Acc]);
 to_tokens([{Tag0, [T0={pi, _S0, _A0} | R1]} | Rest], Acc) ->
     %% Allow {pi, binary(), list()}
     to_tokens([{Tag0, R1} | Rest], [T0 | Acc]);
@@ -290,6 +298,9 @@ tokenize(B, S=#decoder{offset=O}) ->
             tokenize_doctype(B, ?ADV_COL(S, 10));
         <<_:O/binary, "<![CDATA[", _/binary>> ->
             tokenize_cdata(B, ?ADV_COL(S, 9));
+        <<_:O/binary, "<?php", _/binary>> ->
+            {Body, S1} = raw_qgt(B, ?ADV_COL(S, 2)),
+            {{pi, Body}, S1};
         <<_:O/binary, "<?", _/binary>> ->
             {Tag, S1} = tokenize_literal(B, ?ADV_COL(S, 2)),
             {Attrs, S2} = tokenize_attributes(B, S1),
@@ -309,7 +320,7 @@ tokenize(B, S=#decoder{offset=O}) ->
             {Tag, S1} = tokenize_literal(B, ?INC_COL(S)),
             {Attrs, S2} = tokenize_attributes(B, S1),
             {S3, HasSlash} = find_gt(B, S2),
-            Singleton = HasSlash orelse is_singleton(norm(binary_to_list(Tag))),
+            Singleton = HasSlash orelse is_singleton(Tag),
             {{start_tag, Tag, Attrs, Singleton}, S3};
         _ ->
             tokenize_data(B, S)
@@ -333,6 +344,8 @@ tree([{start_tag, Tag, Attrs, true} | Rest], S) ->
     tree(Rest, append_stack_child(norm({Tag, Attrs}), S));
 tree([{start_tag, Tag, Attrs, false} | Rest], S) ->
     tree(Rest, stack(norm({Tag, Attrs}), S));
+tree([T={pi, _Raw} | Rest], S) ->
+    tree(Rest, append_stack_child(T, S));
 tree([T={pi, _Tag, _Attrs} | Rest], S) ->
     tree(Rest, append_stack_child(T, S));
 tree([T={comment, _Comment} | Rest], S) ->
@@ -367,6 +380,10 @@ stack(T1, Stack) ->
 append_stack_child(StartTag, [{Name, Attrs, Acc} | Stack]) ->
     [{Name, Attrs, [StartTag | Acc]} | Stack].
 
+destack(<<"br">>, Stack) ->
+    %% This is an ugly hack to make dumb_br_test() pass,
+    %% this makes it such that br can never have children.
+    Stack;
 destack(TagName, Stack) when is_list(Stack) ->
     F = fun (X) ->
                 case X of
@@ -387,8 +404,8 @@ destack(TagName, Stack) when is_list(Stack) ->
                         {_, []} ->
                             %% Actually was a singleton
                             Stack;
-                        {Pre, [{T1, A1, []} | Post1]} ->
-                            [{T0, A0, [{T1, A1, lists:reverse(Pre)} | Post1]}
+                        {Pre, [{T1, A1, Acc1} | Post1]} ->
+                            [{T0, A0, [{T1, A1, Acc1 ++ lists:reverse(Pre)} | Post1]}
                              | Post0]
                     end;
                 _ ->
@@ -459,10 +476,51 @@ tokenize_attr_value(Attr, B, S) ->
     case B of
         <<_:O/binary, "=", _/binary>> ->
             S2 = skip_whitespace(B, ?INC_COL(S1)),
-            tokenize_word_or_literal(B, S2);
+            tokenize_quoted_or_unquoted_attr_value(B, S2);
         _ ->
             {Attr, S1}
     end.
+    
+tokenize_quoted_or_unquoted_attr_value(B, S=#decoder{offset=O}) ->
+    case B of
+        <<_:O/binary>> ->
+            { [], S };
+        <<_:O/binary, Q, _/binary>> when Q =:= ?QUOTE orelse
+                                         Q =:= ?SQUOTE ->
+            tokenize_quoted_attr_value(B, ?INC_COL(S), [], Q);
+        <<_: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>> ->
+            { iolist_to_binary(lists:reverse(Acc)), S };
+        <<_:O/binary, $&, _/binary>> ->
+            {{data, Data, false}, S1} = tokenize_charref(B, ?INC_COL(S)),
+            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>> ->
+            { iolist_to_binary(lists:reverse(Acc)), S };
+        <<_:O/binary, $&, _/binary>> ->
+            {{data, Data, false}, S1} = tokenize_charref(B, ?INC_COL(S)),
+            tokenize_unquoted_attr_value(B, S1, [Data|Acc]);
+        <<_:O/binary, $/, $>, _/binary>> ->
+            { iolist_to_binary(lists:reverse(Acc)), S };
+        <<_:O/binary, C, _/binary>> when ?PROBABLE_CLOSE(C) ->
+            { iolist_to_binary(lists:reverse(Acc)), S };
+        <<_:O/binary, C, _/binary>> ->
+            tokenize_unquoted_attr_value(B, ?INC_COL(S), [C|Acc])
+    end.   
 
 skip_whitespace(B, S=#decoder{offset=O}) ->
     case B of
@@ -472,8 +530,17 @@ skip_whitespace(B, S=#decoder{offset=O}) ->
             S
     end.
 
-tokenize_literal(Bin, S) ->
-    tokenize_literal(Bin, S, []).
+tokenize_literal(Bin, S=#decoder{offset=O}) ->
+    case Bin of
+        <<_:O/binary, C, _/binary>> when C =:= $>
+                                    orelse C =:= $/
+                                    orelse C =:= $= ->
+            %% Handle case where tokenize_literal would consume
+            %% 0 chars. http://github.com/mochi/mochiweb/pull/13
+            {[C], ?INC_COL(S)};
+        _ ->
+            tokenize_literal(Bin, S, [])
+    end.
 
 tokenize_literal(Bin, S=#decoder{offset=O}, Acc) ->
     case Bin of
@@ -486,13 +553,33 @@ tokenize_literal(Bin, S=#decoder{offset=O}, Acc) ->
                                               orelse C =:= $=) ->
             tokenize_literal(Bin, ?INC_COL(S), [C | Acc]);
         _ ->
-            {iolist_to_binary(lists:reverse(Acc)), S}
+            {iolist_to_binary(string:to_lower(lists:reverse(Acc))), S}
+    end.
+
+raw_qgt(Bin, S=#decoder{offset=O}) ->
+    raw_qgt(Bin, S, O).
+
+raw_qgt(Bin, S=#decoder{offset=O}, Start) ->
+    case Bin of
+        <<_:O/binary, "?>", _/binary>> ->
+            Len = O - Start,
+            <<_:Start/binary, Raw:Len/binary, _/binary>> = Bin,
+            {Raw, ?ADV_COL(S, 2)};
+        <<_:O/binary, C, _/binary>> ->
+            raw_qgt(Bin, ?INC_CHAR(S, C), Start);
+        <<_:O/binary>> ->
+            <<_:Start/binary, Raw/binary>> = Bin,
+            {Raw, S}
     end.
 
 find_qgt(Bin, S=#decoder{offset=O}) ->
     case Bin of
         <<_:O/binary, "?>", _/binary>> ->
             ?ADV_COL(S, 2);
+        <<_:O/binary, ">", _/binary>> ->
+			?ADV_COL(S, 1);
+        <<_:O/binary, "/>", _/binary>> ->
+			?ADV_COL(S, 2);
         %% tokenize_attributes takes care of this state:
         %% <<_:O/binary, C, _/binary>> ->
         %%     find_qgt(Bin, ?INC_CHAR(S, C));
@@ -570,7 +657,7 @@ tokenize_word_or_literal(Bin, S=#decoder{offset=O}) ->
             tokenize_word(Bin, ?INC_COL(S), C);
         <<_:O/binary, C, _/binary>> when not ?IS_WHITESPACE(C) ->
             %% Sanity check for whitespace
-            tokenize_literal(Bin, S, [])
+            tokenize_literal(Bin, S)
     end.
 
 tokenize_word(Bin, S, Quote) ->
@@ -880,6 +967,15 @@ parse_test() ->
                            {<<"br">>, [], []},
                            <<"bar">>]}]},
        parse(<<"<html><link>foo<br>bar</link></html>">>)),
+    %% Case insensitive tags
+    ?assertEqual(
+       {<<"html">>, [],
+        [{<<"head">>, [], [<<"foo">>,
+                           {<<"br">>, [], []},
+                           <<"BAR">>]},
+         {<<"body">>, [{<<"class">>, <<"">>}, {<<"bgcolor">>, <<"#Aa01fF">>}], []}
+        ]},
+       parse(<<"<html><Head>foo<bR>BAR</head><body Class=\"\" bgcolor=\"#Aa01fF\"></BODY></html>">>)),
     ok.
 
 exhaustive_is_singleton_test() ->
@@ -1056,6 +1152,113 @@ doctype_test() ->
        mochiweb_html:parse("<html>"
                            "<!DOCTYPE html PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\" \"http://www.w3.org/TR/html4/loose.dtd\">"
                            "<head></head></body></html>")),
+    %% http://github.com/mochi/mochiweb/pull/13
+    ?assertEqual(
+       {<<"html">>,[],[{<<"head">>,[],[]}]},
+       mochiweb_html:parse("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.0 Transitional//EN\"/>"
+                           "<html>"
+                           "<head></head></body></html>")),
+    ok.
+
+dumb_br_test() ->
+    %% http://code.google.com/p/mochiweb/issues/detail?id=71
+    ?assertEqual(
+       {<<"div">>,[],[{<<"br">>, [], []}, {<<"br">>, [], []}, <<"z">>]},
+       mochiweb_html:parse("<div><br/><br/>z</br/></br/></div>")),
+    ?assertEqual(
+       {<<"div">>,[],[{<<"br">>, [], []}, {<<"br">>, [], []}, <<"z">>]},
+       mochiweb_html:parse("<div><br><br>z</br/></br/></div>")),
+    ?assertEqual(
+       {<<"div">>,[],[{<<"br">>, [], []}, {<<"br">>, [], []}, <<"z">>, {<<"br">>, [], []}, {<<"br">>, [], []}]},
+       mochiweb_html:parse("<div><br><br>z<br/><br/></div>")),
+    ?assertEqual(
+       {<<"div">>,[],[{<<"br">>, [], []}, {<<"br">>, [], []}, <<"z">>]},
+       mochiweb_html:parse("<div><br><br>z</br></br></div>")).
+
+
+php_test() ->
+    %% http://code.google.com/p/mochiweb/issues/detail?id=71
+    ?assertEqual(
+       [{pi, <<"php\n">>}],
+       mochiweb_html:tokens(
+         "<?php\n?>")),
+    ?assertEqual(
+       {<<"div">>, [], [{pi, <<"php\n">>}]},
+       mochiweb_html:parse(
+         "<div><?php\n?></div>")),
+    ok.
+
+parse_unquoted_attr_test() ->
+    D0 = <<"<html><img src=/images/icon.png/></html>">>,
+    ?assertEqual(
+        {<<"html">>,[],[
+            { <<"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() ->    
+    D0 = <<"<html><img src='/images/icon.png'></html>">>,
+    ?assertEqual(
+        {<<"html">>,[],[
+            { <<"img">>, [ { <<"src">>, <<"/images/icon.png">> } ], [] }
+        ]},
+        mochiweb_html:parse(D0)),     
+        
+    D1 = <<"<html><img src=\"/images/icon.png'></html>">>,
+    ?assertEqual(
+        {<<"html">>,[],[
+            { <<"img">>, [ { <<"src">>, <<"/images/icon.png'></html>">> } ], [] }
+        ]},
+        mochiweb_html:parse(D1)),     
+
+    D2 = <<"<html><img src=\"/images/icon&gt;.png\"></html>">>,
+    ?assertEqual(
+        {<<"html">>,[],[
+            { <<"img">>, [ { <<"src">>, <<"/images/icon>.png">> } ], [] }
+        ]},
+        mochiweb_html:parse(D2)),     
+    ok.
+
+parse_missing_attr_name_test() ->
+    D0 = <<"<html =black></html>">>,
+    ?assertEqual(
+        {<<"html">>, [ { <<"=">>, <<"=">> }, { <<"black">>, <<"black">> } ], [] },
+       mochiweb_html:parse(D0)),
     ok.
 
+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">> }, 
+			                             { <<"ns">>, <<"urn:schemas-microsoft-com:office:office">> } ] }
+		] },
+		mochiweb_html:parse(D0)),
+	ok.
+
+parse_funny_singletons_test() ->
+	D0 = <<"<html><input><input>x</input></input></html>">>,
+	?assertEqual(
+		{<<"html">>, [], [
+			{ <<"input">>, [], [] },
+			{ <<"input">>, [], [ <<"x">> ] }
+		] },
+		mochiweb_html:parse(D0)),
+	ok.
+    
 -endif.

http://git-wip-us.apache.org/repos/asf/couchdb-mochiweb/blob/964f4656/mochiweb_http.erl
----------------------------------------------------------------------
diff --git a/mochiweb_http.erl b/mochiweb_http.erl
index ab0af7e..23a4752 100644
--- a/mochiweb_http.erl
+++ b/mochiweb_http.erl
@@ -35,6 +35,16 @@ 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}
+%% @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.
+%%      The proplist is as follows: [{name, Name}, {port, Port}, {active_sockets, ActiveSockets}, {timing, Timing}].
+%% @end
 start(Options) ->
     mochiweb_socket_server:start(parse_options(Options)).
 
@@ -90,22 +100,23 @@ loop(Socket, Body) ->
     request(Socket, Body).
 
 request(Socket, Body) ->
-    case mochiweb_socket:recv(Socket, 0, ?REQUEST_RECV_TIMEOUT) of
-        {ok, {http_request, Method, Path, Version}} ->
+    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}]),
             headers(Socket, {Method, Path, Version}, [], Body, 0);
-        {error, {http_error, "\r\n"}} ->
+        {Protocol, _, {http_error, "\r\n"}} when Protocol == http orelse Protocol == ssl ->
             request(Socket, Body);
-        {error, {http_error, "\n"}} ->
+        {Protocol, _, {http_error, "\n"}} when Protocol == http orelse Protocol == ssl ->
             request(Socket, Body);
-        {error, closed} ->
-            mochiweb_socket:close(Socket),
-            exit(normal);
-        {error, timeout} ->
+        {tcp_closed, _} ->
             mochiweb_socket:close(Socket),
             exit(normal);
         _Other ->
             handle_invalid_request(Socket)
+    after ?REQUEST_RECV_TIMEOUT ->
+        mochiweb_socket:close(Socket),
+        exit(normal)
     end.
 
 reentry(Body) ->
@@ -118,21 +129,23 @@ headers(Socket, Request, Headers, _Body, ?MAX_HEADERS) ->
     mochiweb_socket:setopts(Socket, [{packet, raw}]),
     handle_invalid_request(Socket, Request, Headers);
 headers(Socket, Request, Headers, Body, HeaderCount) ->
-    case mochiweb_socket:recv(Socket, 0, ?HEADERS_RECV_TIMEOUT) of
-        {ok, http_eoh} ->
-            mochiweb_socket:setopts(Socket, [{packet, raw}]),
-            Req = mochiweb:new_request({Socket, Request,
-                                        lists:reverse(Headers)}),
+    mochiweb_socket:setopts(Socket, [{active, once}]),
+    receive
+        {Protocol, _, http_eoh} when Protocol == http orelse Protocol == ssl ->
+            Req = new_request(Socket, Request, Headers),
             call_body(Body, Req),
             ?MODULE:after_response(Body, Req);
-        {ok, {http_header, _, Name, _, Value}} ->
+        {Protocol, _, {http_header, _, Name, _, Value}} when Protocol == http orelse Protocol == ssl ->
             headers(Socket, Request, [{Name, Value} | Headers], Body,
                     1 + HeaderCount);
-        {error, closed} ->
+        {tcp_closed, _} ->
             mochiweb_socket:close(Socket),
             exit(normal);
         _Other ->
             handle_invalid_request(Socket, Request, Headers)
+    after ?HEADERS_RECV_TIMEOUT ->
+        mochiweb_socket:close(Socket),
+        exit(normal)
     end.
 
 call_body({M, F}, Req) ->
@@ -144,13 +157,15 @@ handle_invalid_request(Socket) ->
     handle_invalid_request(Socket, {'GET', {abs_path, "/"}, {0,9}}, []).
 
 handle_invalid_request(Socket, Request, RevHeaders) ->
-    mochiweb_socket:setopts(Socket, [{packet, raw}]),
-    Req = mochiweb:new_request({Socket, Request,
-                                lists:reverse(RevHeaders)}),
+    Req = new_request(Socket, Request, RevHeaders),
     Req:respond({400, [], []}),
     mochiweb_socket:close(Socket),
     exit(normal).
 
+new_request(Socket, Request, RevHeaders) ->
+    mochiweb_socket:setopts(Socket, [{packet, raw}]),
+    mochiweb:new_request({Socket, Request, lists:reverse(RevHeaders)}).
+
 after_response(Body, Req) ->
     Socket = Req:get(socket),
     case Req:should_close() of
@@ -162,6 +177,8 @@ after_response(Body, Req) ->
             ?MODULE:loop(Socket, Body)
     end.
 
+parse_range_request("bytes=0-") ->
+    undefined;
 parse_range_request(RawRange) when is_list(RawRange) ->
     try
         "bytes=" ++ RangeString = RawRange,

http://git-wip-us.apache.org/repos/asf/couchdb-mochiweb/blob/964f4656/mochiweb_request.erl
----------------------------------------------------------------------
diff --git a/mochiweb_request.erl b/mochiweb_request.erl
index ffe4e9e..8225778 100644
--- a/mochiweb_request.erl
+++ b/mochiweb_request.erl
@@ -40,8 +40,8 @@
 %% @type response(). A mochiweb_response parameterized module instance.
 %% @type ioheaders() = headers() | [{key(), value()}].
 
-% 10 second default idle timeout
--define(IDLE_TIMEOUT, 10000).
+% 5 minute default idle timeout
+-define(IDLE_TIMEOUT, 300000).
 
 % Maximum recv_body() length of 1MB
 -define(MAX_RECV_BODY, (1024*1024)).
@@ -382,8 +382,8 @@ ok({ContentType, ResponseHeaders, Body}) ->
 %% @doc Return true if the connection must be closed. If false, using
 %%      Keep-Alive should be safe.
 should_close() ->
-    ForceClose = erlang:get(mochiweb_request_force_close) =/= undefined,
-    DidNotRecv = erlang:get(mochiweb_request_recv) =:= undefined,
+    ForceClose = 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"
@@ -405,6 +405,7 @@ cleanup() ->
                        ?SAVE_PATH,
                        ?SAVE_RECV,
                        ?SAVE_BODY,
+                       ?SAVE_BODY_LENGTH,
                        ?SAVE_POST,
                        ?SAVE_COOKIE,
                        ?SAVE_FORCE_CLOSE]],

http://git-wip-us.apache.org/repos/asf/couchdb-mochiweb/blob/964f4656/mochiweb_request_tests.erl
----------------------------------------------------------------------
diff --git a/mochiweb_request_tests.erl b/mochiweb_request_tests.erl
new file mode 100644
index 0000000..b61a583
--- /dev/null
+++ b/mochiweb_request_tests.erl
@@ -0,0 +1,63 @@
+-module(mochiweb_request_tests).
+
+-include_lib("eunit/include/eunit.hrl").
+-ifdef(TEST).
+
+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")),
+
+    Req2 = mochiweb_request:new(nil, 'GET', "/foo", {1, 1},
+        mochiweb_headers:make([{"Accept", "text/html"}])),
+    ?assertEqual(false, Req2:accepts_content_type("multipart/related")),
+
+    Req3 = mochiweb_request:new(nil, 'GET', "/foo", {1, 1},
+        mochiweb_headers:make([{"Accept", "text/html, multipart/*"}])),
+    ?assertEqual(true, Req3:accepts_content_type("multipart/related")),
+
+    Req4 = mochiweb_request:new(nil, 'GET', "/foo", {1, 1},
+        mochiweb_headers:make([{"Accept", "text/html, multipart/*; q=0.0"}])),
+    ?assertEqual(false, Req4:accepts_content_type("multipart/related")),
+
+    Req5 = mochiweb_request:new(nil, 'GET', "/foo", {1, 1},
+        mochiweb_headers:make([{"Accept", "text/html, multipart/*; q=0"}])),
+    ?assertEqual(false, Req5:accepts_content_type("multipart/related")),
+
+    Req6 = mochiweb_request:new(nil, 'GET', "/foo", {1, 1},
+        mochiweb_headers:make([{"Accept", "text/html, */*; q=0.0"}])),
+    ?assertEqual(false, Req6:accepts_content_type("multipart/related")),
+
+    Req7 = mochiweb_request:new(nil, 'GET', "/foo", {1, 1},
+        mochiweb_headers:make([{"Accept", "multipart/*; q=0.0, */*"}])),
+    ?assertEqual(false, Req7:accepts_content_type("multipart/related")),
+
+    Req8 = mochiweb_request:new(nil, 'GET', "/foo", {1, 1},
+        mochiweb_headers:make([{"Accept", "*/*; q=0.0, multipart/*"}])),
+    ?assertEqual(true, Req8:accepts_content_type("multipart/related")),
+
+    Req9 = mochiweb_request:new(nil, 'GET', "/foo", {1, 1},
+        mochiweb_headers:make([{"Accept", "*/*; q=0.0, multipart/related"}])),
+    ?assertEqual(true, Req9:accepts_content_type("multipart/related")),
+
+    Req10 = mochiweb_request:new(nil, 'GET', "/foo", {1, 1},
+        mochiweb_headers:make([{"Accept", "text/html; level=1"}])),
+    ?assertEqual(true, Req10:accepts_content_type("text/html;level=1")),
+
+    Req11 = mochiweb_request:new(nil, 'GET', "/foo", {1, 1},
+        mochiweb_headers:make([{"Accept", "text/html; level=1, text/html"}])),
+    ?assertEqual(true, Req11:accepts_content_type("text/html")),
+
+    Req12 = mochiweb_request:new(nil, 'GET', "/foo", {1, 1},
+        mochiweb_headers:make([{"Accept", "text/html; level=1; q=0.0, text/html"}])),
+    ?assertEqual(false, Req12:accepts_content_type("text/html;level=1")),
+
+    Req13 = mochiweb_request:new(nil, 'GET', "/foo", {1, 1},
+        mochiweb_headers:make([{"Accept", "text/html; level=1; q=0.0, text/html"}])),
+    ?assertEqual(false, Req13:accepts_content_type("text/html; level=1")),
+
+    Req14 = mochiweb_request:new(nil, 'GET', "/foo", {1, 1},
+        mochiweb_headers:make([{"Accept", "text/html;level=1;q=0.1, text/html"}])),
+    ?assertEqual(true, Req14:accepts_content_type("text/html; level=1")).
+
+-endif.

http://git-wip-us.apache.org/repos/asf/couchdb-mochiweb/blob/964f4656/mochiweb_socket_server.erl
----------------------------------------------------------------------
diff --git a/mochiweb_socket_server.erl b/mochiweb_socket_server.erl
index 1aae09a..ff0d8f3 100644
--- a/mochiweb_socket_server.erl
+++ b/mochiweb_socket_server.erl
@@ -12,7 +12,7 @@
 -export([start/1, stop/1]).
 -export([init/1, handle_call/3, handle_cast/2, terminate/2, code_change/3,
          handle_info/2]).
--export([get/2]).
+-export([get/2, set/3]).
 
 -record(mochiweb_socket_server,
         {port,
@@ -28,7 +28,10 @@
          acceptor_pool_size=16,
          ssl=false,
          ssl_opts=[{ssl_imp, new}],
-         acceptor_pool=sets:new()}).
+         acceptor_pool=sets:new(),
+         profile_fun=undefined}).
+
+-define(is_old_state(State), not is_record(State, mochiweb_socket_server)).
 
 start(State=#mochiweb_socket_server{}) ->
     start_server(State);
@@ -38,6 +41,12 @@ start(Options) ->
 get(Name, Property) ->
     gen_server:call(Name, {get, Property}).
 
+set(Name, profile_fun, Fun) ->
+    gen_server:cast(Name, {set, profile_fun, Fun});
+set(Name, Property, _Value) ->
+    error_logger:info_msg("?MODULE:set for ~p with ~p not implemented~n",
+                          [Name, Property]).
+
 stop(Name) when is_atom(Name) ->
     gen_server:cast(Name, stop);
 stop(Pid) when is_pid(Pid) ->
@@ -102,7 +111,10 @@ parse_options([{ssl, Ssl} | Rest], State) when is_boolean(Ssl) ->
     parse_options(Rest, State#mochiweb_socket_server{ssl=Ssl});
 parse_options([{ssl_opts, SslOpts} | Rest], State) when is_list(SslOpts) ->
     SslOpts1 = [{ssl_imp, new} | proplists:delete(ssl_imp, SslOpts)],
-    parse_options(Rest, State#mochiweb_socket_server{ssl_opts=SslOpts1}).
+    parse_options(Rest, State#mochiweb_socket_server{ssl_opts=SslOpts1});
+parse_options([{profile_fun, ProfileFun} | Rest], State) when is_function(ProfileFun) ->
+    parse_options(Rest, State#mochiweb_socket_server{profile_fun=ProfileFun}).
+
 
 start_server(State=#mochiweb_socket_server{ssl=Ssl, name=Name}) ->
     case Ssl of
@@ -123,7 +135,7 @@ start_server(State=#mochiweb_socket_server{ssl=Ssl, name=Name}) ->
 ensure_int(N) when is_integer(N) ->
     N;
 ensure_int(S) when is_list(S) ->
-    integer_to_list(S).
+    list_to_integer(S).
 
 ipv6_supported() ->
     case (catch inet:getaddr("localhost", inet6)) of
@@ -157,7 +169,7 @@ init(State=#mochiweb_socket_server{ip=Ip, port=Port, backlog=Backlog, nodelay=No
         {stop, eacces} ->
             case Port < 1024 of
                 true ->
-                    case fdsrv:start() of
+                    case catch fdsrv:start() of
                         {ok, _} ->
                             case fdsrv:bind_socket(tcp, Port) of
                                 {ok, Fd} ->
@@ -203,6 +215,28 @@ do_get(port, #mochiweb_socket_server{port=Port}) ->
 do_get(active_sockets, #mochiweb_socket_server{active_sockets=ActiveSockets}) ->
     ActiveSockets.
 
+
+state_to_proplist(#mochiweb_socket_server{name=Name,
+                                          port=Port,
+                                          active_sockets=ActiveSockets}) ->
+    [{name, Name}, {port, Port}, {active_sockets, ActiveSockets}].
+
+upgrade_state(State = #mochiweb_socket_server{}) ->
+    State;
+upgrade_state({mochiweb_socket_server, Port, Loop, Name,
+             Max, IP, Listen, NoDelay, Backlog, ActiveSockets,
+             AcceptorPoolSize, SSL, SSL_opts,
+             AcceptorPool}) ->
+    #mochiweb_socket_server{port=Port, loop=Loop, name=Name, max=Max, ip=IP,
+                            listen=Listen, nodelay=NoDelay, backlog=Backlog,
+                            active_sockets=ActiveSockets,
+                            acceptor_pool_size=AcceptorPoolSize,
+                            ssl=SSL,
+                            ssl_opts=SSL_opts,
+                            acceptor_pool=AcceptorPool}.
+
+handle_call(Req, From, State) when ?is_old_state(State) ->
+    handle_call(Req, From, upgrade_state(State));
 handle_call({get, Property}, _From, State) ->
     Res = do_get(Property, State),
     {reply, Res, State};
@@ -210,13 +244,33 @@ handle_call(_Message, _From, State) ->
     Res = error,
     {reply, Res, State}.
 
-handle_cast({accepted, Pid, _Timing},
+
+handle_cast(Req, State) when ?is_old_state(State) ->
+    handle_cast(Req, upgrade_state(State));
+handle_cast({accepted, Pid, Timing},
             State=#mochiweb_socket_server{active_sockets=ActiveSockets}) ->
     State1 = State#mochiweb_socket_server{active_sockets=1 + ActiveSockets},
+    case State#mochiweb_socket_server.profile_fun of
+        undefined ->
+            undefined;
+        F when is_function(F) ->
+            catch F([{timing, Timing} | state_to_proplist(State1)])
+    end,
     {noreply, recycle_acceptor(Pid, State1)};
+handle_cast({set, profile_fun, ProfileFun}, State) ->
+    State1 = case ProfileFun of
+                 ProfileFun when is_function(ProfileFun); ProfileFun =:= undefined ->
+                     State#mochiweb_socket_server{profile_fun=ProfileFun};
+                 _ ->
+                     State
+             end,
+    {noreply, State1};
 handle_cast(stop, State) ->
     {stop, normal, 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
@@ -244,6 +298,8 @@ recycle_acceptor(Pid, State=#mochiweb_socket_server{
             State#mochiweb_socket_server{active_sockets=ActiveSockets - 1}
     end.
 
+handle_info(Msg, State) when ?is_old_state(State) ->
+    handle_info(Msg, upgrade_state(State));
 handle_info({'EXIT', Pid, normal}, State) ->
     {noreply, recycle_acceptor(Pid, State)};
 handle_info({'EXIT', Pid, Reason},
@@ -258,6 +314,20 @@ handle_info({'EXIT', Pid, Reason},
             ok
     end,
     {noreply, recycle_acceptor(Pid, State)};
+
+% this is what release_handler needs to get a list of modules,
+% since our supervisor modules list is set to 'dynamic'
+% see sasl-2.1.9.2/src/release_handler_1.erl get_dynamic_mods
+handle_info({From, Tag, get_modules}, State = #mochiweb_socket_server{name={local,Mod}}) ->
+    From ! {element(2,Tag), [Mod]},
+    {noreply, State};
+
+% If for some reason we can't get the module name, send empty list to avoid release_handler timeout:
+handle_info({From, Tag, get_modules}, State) ->
+    error_logger:info_msg("mochiweb_socket_server replying to dynamic modules request as '[]'~n",[]),
+    From ! {element(2,Tag), []},
+    {noreply, State};
+
 handle_info(Info, State) ->
     error_logger:info_report([{'INFO', Info}, {'State', State}]),
     {noreply, State}.
@@ -269,4 +339,26 @@ handle_info(Info, State) ->
 %%
 -include_lib("eunit/include/eunit.hrl").
 -ifdef(TEST).
+
+upgrade_state_test() ->
+    OldState = {mochiweb_socket_server,
+                port, loop, name,
+                max, ip, listen,
+                nodelay, backlog,
+                active_sockets,
+                acceptor_pool_size,
+                ssl, ssl_opts, acceptor_pool},
+    State = upgrade_state(OldState),
+    CmpState = #mochiweb_socket_server{port=port, loop=loop,
+                                       name=name, max=max, ip=ip,
+                                       listen=listen, nodelay=nodelay,
+                                       backlog=backlog,
+                                       active_sockets=active_sockets,
+                                       acceptor_pool_size=acceptor_pool_size,
+                                       ssl=ssl, ssl_opts=ssl_opts,
+                                       acceptor_pool=acceptor_pool,
+                                       profile_fun=undefined},
+    ?assertEqual(CmpState, State).
+
 -endif.
+

http://git-wip-us.apache.org/repos/asf/couchdb-mochiweb/blob/964f4656/mochiweb_util.erl
----------------------------------------------------------------------
diff --git a/mochiweb_util.erl b/mochiweb_util.erl
index 62ff0d0..3b50fe7 100644
--- a/mochiweb_util.erl
+++ b/mochiweb_util.erl
@@ -620,7 +620,7 @@ cmd_port_test_spool(Port, Acc) ->
             cmd_port_test_spool(Port, ["\n", Data | Acc]);
         {Port, Unknown} ->
             throw({unknown, Unknown})
-    after 100 ->
+    after 1000 ->
             throw(timeout)
     end.