Commits

Anonymous committed 75bbdc9 Merge

merge

Comments (0)

Files changed (17)

 
    Let's create a shopping list for bread at /groceries/mine:
 
-   (riaktest@example.com)6> O0 = riak_object:new("groceries", "mine", ["bread"]).
-   O0 = riak_object:new("groceries", "mine", ["bread"]).
-   {r_object,"groceries","mine",
+   (riaktest@example.com)6> O0 = riak_object:new(<<"groceries">>, <<"mine">>, ["bread"]).
+   O0 = riak_object:new(<<"groceries">>, <<"mine">>, ["bread"]).
+   {r_object,<<"groceries">>,<<"mine">>,
           [{r_content,{dict,0,16,16,8,80,48,
                             {[],[],[],[],[],[],[],[],[],[],[],[],[],[],...},
                             {{[],[],[],[],[],[],[],[],[],[],[],[],...}}},
                 {{[],[],[],[],[],[],[],[],[],[],[],[],[],...}}},
           undefined}
 
-    (riaktest@example.com)3> C:put(01, 1).
+    (riaktest@example.com)3> C:put(O0, 1).
     
     Now, read the list back from the Riak server and extract the value
 
-    (riaktest@example.com)4> {ok, O1} = C:get("groceries", "mine", 1).
-    {ok,{r_object,"groceries","mine",
+    (riaktest@example.com)4> {ok, O1} = C:get(<<"groceries">>, <<"mine">>, 1).
+    {ok,{r_object,<<"groceries">>,<<"mine">>,
               [{r_content,{dict,2,16,16,8,80,48,
                                 {[],[],[],[],[],[],[],[],[],[],[],[],...},
                                 {{[],[],[],[],[],[],
 
      (riaktest@example.com)6> %% add milk to the list
      (riaktest@example.com)6> O2 = riak_object:update_value(O1, ["milk" | V]).
-     {r_object,"groceries","mine",
+     {r_object,<<"groceries">>,<<"mine">>,
           [{r_content,{dict,2,16,16,8,80,48,
                             {[],[],[],[],[],[],[],[],[],[],[],[],[],[],...},
                             {{[],[],[],[],[],[],
 
      Finally, see what other keys are available in groceries bucket:
 
-     (riaktest@example.com)8> C:list_keys("groceries").
-     {ok,["mine"]}
+     (riaktest@example.com)8> C:list_keys(<<"groceries">>).
+     {ok,[<<"mine">>]}
 
 
 
    Assuming you have a working Erlang (R13B03 or later) installation,
    building Riak should be as simple as:
 
+#+BEGIN_EXAMPLE
    $ cd $RIAK
    $ make all rel
+#+END_EXAMPLE
 
 ** Starting Riak
 
    Once you have successfully built Riak, you can start the server with the
    following commands:
 
+#+BEGIN_EXAMPLE
    $ cd $RIAK/rel/riak
    $ bin/riak start
+#+END_EXAMPLE
 
    Now, verify that the server started up cleanly and is working:
 
-   $ bin/riak-admin test
+   : $ bin/riak-admin test
 
    Note that the $RIAK/rel/riak directory is a complete, self-contained instance
    of Riak and Erlang. It is strongly suggested that you move this directory
    Now that you have a functional server, let's try storing some data in
    it. First, start up a erlang node using our embedded version of erlang:
 
+#+BEGIN_EXAMPLE
    $ erts-<vsn>/bin/erl -name riaktest -setcookie riak
    
    Eshell V5.7.4  (abort with ^G)
    (riaktest@example.com)1>
+#+END_EXAMPLE
 
    Now construct the node name of Riak server and make sure we can talk to it:
 
+#+BEGIN_EXAMPLE
    (riaktest@example.com)4> RiakNode = riak_util:str_to_node(riak).
 
    (riaktest@example.com)2> net_adm:ping(RiakNode).
    pong
    (riaktest@example.com)2>
+#+END_EXAMPLE
    
    We are now ready to start the Riak client:
 
+#+BEGIN_EXAMPLE
    (riaktest@example.com)2> {ok, C} = riak:client_connect(RiakNode).
    {ok,{riak_client,'riak@example.com',<<4,136,81,151>>}}
+#+END_EXAMPLE
 
    Let's create a shopping list for bread at /groceries/mine:
 
+#+BEGIN_EXAMPLE
    (riaktest@example.com)6> O0 = riak_object:new(<<"groceries">>, <<"mine">>, ["bread"]).
    O0 = riak_object:new(<<"groceries">>, <<"mine">>, ["bread"]).
    {r_object,<<"groceries">>,<<"mine">>,
                 {{[],[],[],[],[],[],[],[],[],[],[],[],[],...}}},
           undefined}
 
-    (riaktest@example.com)3> C:put(01, 1).
+    (riaktest@example.com)3> C:put(O0, 1).
+#+END_EXAMPLE
     
     Now, read the list back from the Riak server and extract the value
 
+#+BEGIN_EXAMPLE
     (riaktest@example.com)4> {ok, O1} = C:get(<<"groceries">>, <<"mine">>, 1).
     {ok,{r_object,<<"groceries">>,<<"mine">>,
               [{r_content,{dict,2,16,16,8,80,48,
      (riaktest@example.com)5> %% extract the value
      (riaktest@example.com)5> V = riak_object:get_value(O1).
      ["bread"]
+#+END_EXAMPLE
 
      Add milk to our list of groceries and write the new value to Riak:
 
+#+BEGIN_EXAMPLE
      (riaktest@example.com)6> %% add milk to the list
      (riaktest@example.com)6> O2 = riak_object:update_value(O1, ["milk" | V]).
      {r_object,<<"groceries">>,<<"mine">>,
      (riaktest@example.com)7> %% store the new list
      (riaktest@example.com)7> C:put(O2, 1).
      ok
+#+END_EXAMPLE
 
      Finally, see what other keys are available in groceries bucket:
 
+#+BEGIN_EXAMPLE
      (riaktest@example.com)8> C:list_keys(<<"groceries">>).
      {ok,[<<"mine">>]}
+#+END_EXAMPLE
 
 
 * Server Management
 
     To start a daemonized (background) instance of Riak:
 
-    $ bin/riak start 
+    : $ bin/riak start 
 
     Once a server is running in the background you can attach to the Erlang
     console via:
 
-    $ bin/riak attach
+    : $ bin/riak attach
 
     Alternatively, if you want to run a foreground instance of Riak, start it
     with:
 
-    $ bin/riak console
+    : $ bin/riak console
 
     Stopping a foreground or background instance of Riak can be done from a
     shell prompt via:
 
-    $ bin/riak stop 
+    : $ bin/riak stop 
 
     Or if you are attached/on the Erlang console:
 
-    (riak@example.com)1> q().
+    : (riak@example.com)1> q().
 
     You can determine if the server is running by:
 
-    $ bin/riak ping
+    : $ bin/riak ping
 
 *** bin/riak-admin
     This script provides access to general administration of the Riak server. 
 
     To join a new Riak node to an existing cluster:
 
+#+BEGIN_EXAMPLE
     $ bin/riak start # If a local server is not already running
     $ bin/riak-admin join <node in cluster>
+#+END_EXAMPLE
 
     (Note that you must have a local node already running for this to work)
     
     To verify that the local Riak node is able to read/write data:
 
-    $ bin/riak-admin test
+    : $ bin/riak-admin test
 
     
     
 Kevin Smith
 Jonathan Lee
 Sean Cribbs
+Matthew Curry

apps/riak/src/riak_multi_backend.erl

 %% is specified, then the 'multi_backend_default' setting is used.
 %% If this is unset, then the first defined backend is used.
 %% 
-%% === Configuration == 
+%% === Configuration ===
 %% 
 %%     {storage_backend, riak_multi_backend},
 %%     {multi_backend_default, first_backend},
 %%
 %% Then, tell a bucket which one to use...
 %%
-%%     riak_bucket:set_bucket(<<"MY_BUCKET">>, [{backend, second_backend}])
+%%     riak_bucket:set_bucket(&lt;&lt;"MY_BUCKET"&gt;&gt;, [{backend, second_backend}])
+%%
 %%
 
 

apps/webmachine/include/wm_reqstate.hrl

--record(reqstate, {socket=undefined,
+-record(wm_reqstate, {socket=undefined,
                    metadata=dict:new(),
                    range=undefined,
                    peer=undefined,
                    reqdata=undefined,
                    bodyfetch=undefined,
+                   reqbody=undefined,
                    log_data=undefined
                   }).
 

apps/webmachine/src/webmachine.app

     webmachine_perf_logger,
     webmachine_resource,
     webmachine_request,
-    webmachine_request_srv,
     webmachine_skel,
     webmachine_sup,
     webmachine_mochiweb,

apps/webmachine/src/webmachine.erl

     RawPath = Request:get(raw_path), 
     Version = Request:get(version),
     Headers = Request:get(headers),
-    InitState = #reqstate{socket=Socket,
+    InitState = #wm_reqstate{socket=Socket,
                           reqdata=wrq:create(Method,Version,RawPath,Headers)},
+    
     InitReq = {webmachine_request,InitState},
     {Peer, ReqState} = InitReq:get_peer(),
-    PeerState = ReqState#reqstate{reqdata=wrq:set_peer(Peer,
-                                                  ReqState#reqstate.reqdata)},
+    PeerState = ReqState#wm_reqstate{reqdata=wrq:set_peer(Peer,
+                                              ReqState#wm_reqstate.reqdata)},
     LogData = #wm_log_data{start_time=now(),
 			   method=Method,
 			   headers=Headers,
-			   peer=PeerState#reqstate.peer,
+			   peer=PeerState#wm_reqstate.peer,
 			   path=RawPath,
 			   version=Version,
 			   response_code=404,
 			   response_length=0},
-    webmachine_request:new(PeerState#reqstate{log_data=LogData}).
+    webmachine_request:new(PeerState#wm_reqstate{log_data=LogData}).
 
 
 

apps/webmachine/src/webmachine_decision_core.erl

 	    Reason = {none, none, []},
 	    {ErrorHTML,ReqState} = ErrorHandler:render_error(
                           Code, {webmachine_request,get(reqstate)}, Reason),
-            put(reqstate, ReqState), 
+            put(reqstate, ReqState),
             wrcall({set_resp_body, ErrorHTML});
         304 ->
             wrcall({remove_resp_header, "Content-Type"}),
     case Body of
         {stream, StreamBody} ->
             {stream, make_encoder_stream(Encoder, Charsetter, StreamBody)};
+        {writer, BodyFun} ->
+            {writer, {Encoder, Charsetter, BodyFun}};
         _ ->
             Encoder(Charsetter(iolist_to_binary(Body)))
     end.
 make_encoder_stream(Encoder, Charsetter, {Body, Next}) ->
     {Encoder(Charsetter(Body)),
      fun() -> make_encoder_stream(Encoder, Charsetter, Next()) end}.
-            
+
 choose_encoding(AccEncHdr) ->
     Encs = [Enc || {Enc,_Fun} <- resource_call(encodings_provided)],
     case webmachine_util:choose_encoding(Encs, AccEncHdr) of

apps/webmachine/src/webmachine_dispatcher.erl

 -author('Bryan Fink <bryan@basho.com>').
 
 -export([dispatch/2, dispatch/3]).
+-include_lib("eunit/include/eunit.hrl").
 
 -define(SEPARATOR, $\/).
 -define(MATCH_ALL, '*').
 calculate_app_root(1) -> ".";
 calculate_app_root(N) when N > 1 ->
     string:join(lists:duplicate(N, ".."), [?SEPARATOR]).
+
+%%
+%% TEST
+%%
+
+app_root_test() ->
+    ?assertEqual(".",           calculate_app_root(1)),
+    ?assertEqual("../..",       calculate_app_root(2)),
+    ?assertEqual("../../..",    calculate_app_root(3)),
+    ?assertEqual("../../../..", calculate_app_root(4)).
+
+reconstitute_test() ->
+    ?assertEqual("",            reconstitute([])),
+    ?assertEqual("foo",         reconstitute(["foo"])),
+    ?assertEqual("foo/bar",     reconstitute(["foo","bar"])),
+    ?assertEqual("foo/bar/baz", reconstitute(["foo","bar","baz"])).
+
+split_host_test() ->
+    ?assertEqual(["foo","bar","baz"], split_host("foo.bar.baz")).
+
+split_host_port_test() ->
+    ?assertEqual({[], 80}, split_host_port("")),
+    ?assertEqual({["foo","bar","baz"], 80},
+                 split_host_port("foo.bar.baz:80")),
+    ?assertEqual({["foo","bar","baz"], 1234},
+                 split_host_port("foo.bar.baz:1234")).
+
+%% port binding
+bind_port_simple_match_test() ->
+    ?assertEqual({ok, []}, bind_port(80, 80, [])),
+    ?assertEqual({ok, [{foo, bar}]},
+                 bind_port(1234, 1234, [{foo, bar}])).
+
+bind_port_matchall_test() ->
+    ?assertEqual({ok, []}, bind_port('*', 80, [])),
+    ?assertEqual({ok, [{foo, bar}]},
+                 bind_port('*', 1234, [{foo, bar}])).
+
+bind_port_match_test() ->
+    ?assertEqual({ok, [{foo, 80}]}, bind_port(foo, 80, [])),
+    {ok, WholeBinding} = bind_port(foo, 1234, [{bar, baz}]),
+    ?assertEqual(2, length(WholeBinding)),
+    ?assertEqual(1234, proplists:get_value(foo, WholeBinding)),
+    ?assertEqual(baz, proplists:get_value(bar, WholeBinding)).
+
+ind_port_fail_test() ->
+    ?assertEqual(fail, bind_port(80, 1234, [])).
+
+%% path binding
+
+bind_path_empty_test() ->
+    ?assertEqual({ok, [], [], 0}, bind([], [], [], 0)),
+    ?assertEqual({ok, [], [{x,"a"}], 1},
+                 bind([], [], [{x,"a"}], 1)).
+
+bind_path_matchall_test() ->
+    ?assertEqual({ok, [], [], 1},
+                 bind(['*'], [], [], 1)),
+    ?assertEqual({ok, ["a","b"], [], 2},
+                 bind(['*'], ["a","b"], [], 0)).
+
+bind_path_fail_longer_match_test() ->
+    ?assertEqual(fail, bind(["x"], [], [], 0)),
+    ?assertEqual(fail, bind([foo], [], [], 0)).
+
+bind_path_with_binding_test() ->
+    ?assertEqual({ok, [], [{foo, "a"}], 1},
+                 bind([foo], ["a"], [], 0)),
+    {ok, Rest, Bind, Depth} = bind([foo,'*'], ["a","b"], [{bar, baz}], 1),
+    ?assertEqual(["b"], Rest),
+    ?assertEqual(3, Depth),
+    ?assertEqual(2, length(Bind)),
+    ?assertEqual("a", proplists:get_value(foo, Bind)),
+    ?assertEqual(baz, proplists:get_value(bar, Bind)).
+
+bind_path_string_match_test() ->
+    ?assertEqual({ok, [], [], 1},
+                 bind(["a"], ["a"], [], 0)),
+    ?assertEqual({ok, [], [{foo, bar}], 4},
+                 bind(["a","b","c"], ["a","b","c"], [{foo, bar}], 1)).
+
+bind_path_string_fail_test() ->
+    ?assertEqual(fail, bind(["a"], ["b"], [], 0)),
+    ?assertEqual(fail, bind(["a","b"], ["a","c"], [], 0)).
+
+try_path_matching_test() ->
+    ?assertEqual({bar, baz, [], [], ".", ""},
+                 try_path_binding([{["foo"], bar, baz}], ["foo"], [], 0)),
+    Dispatch = [{["a", x], foo, bar},
+                {["b", y], baz, quux},
+                {["b", y, '*'], baz2, quux2}],
+    ?assertEqual({foo, bar, [], [{x, "c"}], "../..", []},
+                 try_path_binding(Dispatch, ["a","c"], [], 0)),
+    ?assertEqual({baz, quux, [], [{y, "c"}], "../..", []},
+                 try_path_binding(Dispatch, ["b","c"], [], 0)),
+    ?assertEqual({baz2, quux2, ["z"], [{y, "c"}], "../../..", "z"},
+                 try_path_binding(Dispatch, ["b","c","z"], [], 0)),
+    ?assertEqual({baz2, quux2, ["z","v"], [{y, "c"}], "../../../..", "z/v"},
+                 try_path_binding(Dispatch, ["b","c","z","v"], [], 0)).
+
+try_path_failing_test() ->
+    ?assertEqual({no_dispatch_match, ["a"]},
+                 try_path_binding([{["b"], x, y}], ["a"], [], 0)).
+
+%% host binding
+
+try_host_binding_nohosts_test() ->
+    PathDispatches = [{["a"], foo, bar},
+                      {["b"], baz, quux}],
+    ?assertEqual(try_host_binding([{{['*'],'*'},PathDispatches}],
+                                  ["quux","baz"], 80, ["a"], 0),
+                 try_host_binding(PathDispatches,
+                                  ["quux","baz"], 80, ["a"], 0)),
+    ?assertEqual(try_host_binding([{{['*'],'*'},PathDispatches}],
+                                  ["quux","baz"], 80, ["b"], 0),
+                 try_host_binding(PathDispatches,
+                                  ["quux","baz"], 80, ["b"], 0)),
+    ?assertEqual(try_host_binding([ {{['*'],'*'},[D]} || D <- PathDispatches],
+                                  ["quux","baz"], 1234, ["a"], 0),
+                 try_host_binding(PathDispatches,
+                                  ["quux","baz"], 1234, ["a"], 0)),
+    ?assertEqual(try_host_binding([ {{['*'],'*'},[D]} || D <- PathDispatches],
+                                  ["quux","baz"], 1234, ["b"], 0),
+                 try_host_binding(PathDispatches,
+                                  ["quux","baz"], 1234, ["b"], 0)).
+
+try_host_binding_noport_test() ->
+    Dispatch = [{["foo","bar"], [{["a"],x,y}]},
+                {["baz","quux"],[{["b"],z,q}]},
+                {[m,"quux"],    [{["c"],r,s}]},
+                {['*',"quux"],  [{["d"],t,u}]}],
+    ExplicitWildPort = [ {{H, '*'},P} || {H, P} <- Dispatch ],
+    ?assertEqual(try_host_binding(ExplicitWildPort,
+                                  ["bar","foo"], 80, ["a"], 0),
+                 try_host_binding(Dispatch,
+                                  ["bar","foo"], 80, ["a"], 0)),
+    ?assertEqual(try_host_binding(ExplicitWildPort,
+                                  ["quux","baz"], 1234, ["b"], 0),
+                 try_host_binding(Dispatch,
+                                  ["quux","baz"], 1234, ["b"], 0)),
+    ?assertEqual(try_host_binding(ExplicitWildPort,
+                                  ["quux","yes"], 81, ["c"], 0),
+                 try_host_binding(Dispatch,
+                                  ["quux","yes"], 81, ["c"], 0)),
+    ?assertEqual(try_host_binding(ExplicitWildPort,
+                                  ["quux","no"], 82, ["d"], 0),
+                 try_host_binding(Dispatch,
+                                  ["quux","no"], 82, ["d"], 0)).
+
+try_host_binding_fullmatch_test() ->
+    Dispatch = [{{["foo","bar"],80},[{["a"],x,y}]},
+                {{[foo,"bar"],80},  [{["b"],z,q}]},
+                {{[foo,"bar"],baz}, [{["c"],r,s}]},
+                {{['*',"bar"],'*'}, [{["d"],t,u}]}],
+    ?assertEqual({x, y, [], 80, [], [], ".", ""},
+                 try_host_binding(Dispatch,
+                                  ["bar","foo"], 80, ["a"], 0)),
+    ?assertEqual({z, q, [], 80, [], [{foo,"baz"}], ".", ""},
+                 try_host_binding(Dispatch,
+                                  ["bar","baz"], 80, ["b"], 0)),
+    {Mod, Props, HostRemainder, Port, PathRemainder,
+     PathBindings, AppRoot, StringPath}=
+        try_host_binding(Dispatch, ["bar","quux"], 1234, ["c"], 0),
+    ?assertEqual(r, Mod),
+    ?assertEqual(s, Props),
+    ?assertEqual("", HostRemainder),
+    ?assertEqual(1234, Port),
+    ?assertEqual([], PathRemainder),
+    ?assertEqual(2, length(PathBindings)),
+    ?assertEqual("quux", proplists:get_value(foo, PathBindings)),
+    ?assertEqual(1234, proplists:get_value(baz, PathBindings)),
+    ?assertEqual(".", AppRoot),
+    ?assertEqual("", StringPath),
+    ?assertEqual({t, u, ["quux","foo"], 80, [], [], ".", ""},
+                 try_host_binding(Dispatch, ["bar","quux","foo"],80,["d"],0)).
+
+try_host_binding_fail_test() ->
+    ?assertEqual({no_dispatch_match, {["bar","foo"], 1234}, ["x","y","z"]},
+                 try_host_binding([], ["bar","foo"], 1234, ["x","y","z"], 0)).
+
+dispatch_test() ->
+    ?assertEqual({x, y, [], 80, [], [], "../../..", ""},
+                 dispatch("a/b/c",[{["a","b","c"],x,y}])),
+    ?assertEqual({x, y, [], 80, [], [], "../../..", ""},
+                 dispatch("foo.bar", "a/b/c",
+                          [{{["foo","bar"],80},[{["a","b","c"],x,y}]}])),
+    ?assertEqual({x, y, [], 1234, [], [], "../../..", ""},
+                 dispatch("foo.bar:1234", "a/b/",
+                          [{{["foo","bar"],1234},[{["a","b"],x,y}]}])),
+    ?assertEqual({no_dispatch_match, {["bar","baz"],8000}, ["q","r"]},
+                 dispatch("baz.bar:8000", "q/r",
+                          [{{["foo","bar"],80},[{["a","b","c"],x,y}]}])).

apps/webmachine/src/webmachine_error_handler.erl

 render_error(Code, Req, Reason) ->
     case Req:has_response_body() of
         {true,_} -> Req:response_body();
-        {false,_} -> render_error_body(Code, Req, Reason)
+        {false,_} -> render_error_body(Code, Req:trim_state(), Reason)
     end.
 
 render_error_body(404, Req, _Reason) ->

apps/webmachine/src/webmachine_mochiweb.erl

 
 host_headers(Req) ->
     [ V || {V,_ReqState} <- [Req:get_header_value(H)
-                             || H <- ["x-forwarded-for",
-                                      "x-forwarded-host",
+                             || H <- ["x-forwarded-host",
                                       "x-forwarded-server",
                                       "host"]],
            V /= undefined].

apps/webmachine/src/webmachine_multipart.erl

 -author('Justin Sheehy <justin@basho.com>').
 -author('Andy Gross <andy@basho.com>').
 -export([get_all_parts/2,stream_parts/2, find_boundary/1]).
--export([test_body/0,test_body2/0]).
+
+-include_lib("eunit/include/eunit.hrl").
 
 % @type incoming_req_body() = binary().
 % The request body, in "multipart/form-data" (rfc2388) form,
 % @spec get_all_parts(incoming_req_body(), boundary()) -> [fpart()]
 get_all_parts(Body, Boundary) when is_binary(Body), is_list(Boundary) ->
     StreamStruct = send_streamed_body(Body,1024),
-    getparts1(stream_parts(StreamStruct, "--" ++ Boundary), []).
+    getparts1(stream_parts(StreamStruct, Boundary), []).
 
 % @doc Similar to get_all_parts/2, but for streamed/chunked bodies.
 %   Takes as input the result of wrq:stream_req_body/2, and provides
             {Body, done}
     end.
 
-test_body() ->
+body_test() ->
     Body = <<"------------ae0gL6gL6Ij5KM7Ef1KM7ei4ae0cH2\r\nContent-Disposition: form-data; name=\"Filename\"\r\n\r\ntestfile.txt\r\n------------ae0gL6gL6Ij5KM7Ef1KM7ei4ae0cH2\r\nContent-Disposition: form-data; name=\"Filedata\"; filename=\"testfile.txt\"\r\nContent-Type: application/octet-stream\r\n\r\n%%% The contents of this file are a test,\n%%% do not be alarmed.\n\r\n------------ae0gL6gL6Ij5KM7Ef1KM7ei4ae0cH2\r\nContent-Disposition: form-data; name=\"Upload\"\r\n\r\nSubmit Query\r\n------------ae0gL6gL6Ij5KM7Ef1KM7ei4ae0cH2--">>,
     Boundary = "----------ae0gL6gL6Ij5KM7Ef1KM7ei4ae0cH2",
-    [{"Filename",
-      {[{<<"name">>,<<"Filename">>}],[]},
-      <<"testfile.txt\r\n">>},
-     {"Filedata",
-      {[{<<"name">>,<<"Filedata">>},
-        {<<"filename">>,<<"testfile.txt">>}],
-       [{<<"Content-Type">>,<<"application/octet-stream">>}]},
-      <<"%%% The contents of this file are a test,\n%%% do not be alarmed.\n\r\n">>},
-     {"Upload",
-      {[{<<"name">>,<<"Upload">>}],[]},
-      <<"Submit Query\r\n">>}]
-    = get_all_parts(Body, Boundary),
-    ok.
+    ?assertEqual(
+       [{"Filename",
+         {[{<<"name">>,<<"Filename">>}],[]},
+         <<"testfile.txt\r\n">>},
+        {"Filedata",
+         {[{<<"name">>,<<"Filedata">>},
+           {<<"filename">>,<<"testfile.txt">>}],
+          [{<<"Content-Type">>,<<"application/octet-stream">>}]},
+         <<"%%% The contents of this file are a test,\n%%% do not be alarmed.\n\r\n">>},
+        {"Upload",
+         {[{<<"name">>,<<"Upload">>}],[]},
+         <<"Submit Query\r\n">>}],
+       get_all_parts(Body, Boundary)).
     
-test_body2() ->
+body2_test() ->
     Body = <<"-----------------------------89205314411538515011004844897\r\nContent-Disposition: form-data; name=\"Filedata\"; filename=\"akamai.txt\"\r\nContent-Type: text/plain\r\n\r\nCAMBRIDGE, MA - February 18, 2009 - Akamai Technologies, Inc. (NASDAQ: AKAM), the leader in powering rich media, dynamic transactions and enterprise applications online, today announced that its Service & Support organization was awarded top honors for Innovation in Customer Service at the 3rd Annual Stevie Awards for Sales & Customer Service, an international competition recognizing excellence in disciplines that are crucial to business success.\n\n\"We have always set incredibly high standards with respect to the service and support we provide our customers,\" said Sanjay Singh, vice president of Global Service & Support at Akamai. \"Our support team provides highly responsive service around the clock to our global customer base and, as a result, has become an extension of our customers' online businesses. This prestigious award is validation of Akamai's commitment to customer service and technical support.\"\n\nAkamai Service & Support professionals are dedicated to working with customers on a daily basis to fine tune, optimize, and support their Internet initiatives. Akamai's winning submission highlighted the key pillars of its service and support offering, as well as the initiatives established to meet customer requirements for proactive communication, simplification, and faster response times.\n\n\"This year's honorees demonstrate that even in challenging economic times, it's possible for organizations to continue to shine in sales and customer service, the two most important functions in business: acquiring and keeping customers,\" said Michael Gallagher, president of the Stevie Awards.\n\nThe awards are presented by the Stevie Awards, which organizes several of the world's leading business awards shows, including the prestigious American Business Awards. Nicknamed the Stevies for the Greek word \"crowned,\" winners were announced during a gala banquet on Monday, February 9 at Caesars Palace in Las Vegas. Nominated customer service and sales executives from the U.S.A. and several other countries attended. More than 500 entries from companies of all sizes and in virtually every industry were submitted to this year's competition. There are 27 categories for customer service professionals, as well as 41 categories for sales professionals.\n\nDetails about the Stevie Awards for Sales & Customer Service and the list of honorees in all categories are available at www.stevieawards.com/sales. \n\r\n-----------------------------89205314411538515011004844897--\r\n">>,
     Boundary = "---------------------------89205314411538515011004844897",
-    get_all_parts(Body,Boundary).
+    ?assertEqual(
+       [{"Filedata",
+         {[{<<"name">>,<<"Filedata">>},
+           {<<"filename">>,<<"akamai.txt">>}],
+          [{<<"Content-Type">>,<<"text/plain">>}]},
+         <<"CAMBRIDGE, MA - February 18, 2009 - Akamai Technologies, Inc. (NASDAQ: AKAM), the leader in powering rich media, dynamic transactions and enterprise applications online, today announced that its Service & Support organization was awarded top honors for Innovation in Customer Service at the 3rd Annual Stevie Awards for Sales & Customer Service, an international competition recognizing excellence in disciplines that are crucial to business success.\n\n\"We have always set incredibly high standards with respect to the service and support we provide our customers,\" said Sanjay Singh, vice president of Global Service & Support at Akamai. \"Our support team provides highly responsive service around the clock to our global customer base and, as a result, has become an extension of our customers' online businesses. This prestigious award is validation of Akamai's commitment to customer service and technical support.\"\n\nAkamai Service & Support professionals are dedicated to working with customers on a daily basis to fine tune, optimize, and support their Internet initiatives. Akamai's winning submission highlighted the key pillars of its service and support offering, as well as the initiatives established to meet customer requirements for proactive communication, simplification, and faster response times.\n\n\"This year's honorees demonstrate that even in challenging economic times, it's possible for organizations to continue to shine in sales and customer service, the two most important functions in business: acquiring and keeping customers,\" said Michael Gallagher, president of the Stevie Awards.\n\nThe awards are presented by the Stevie Awards, which organizes several of the world's leading business awards shows, including the prestigious American Business Awards. Nicknamed the Stevies for the Greek word \"crowned,\" winners were announced during a gala banquet on Monday, February 9 at Caesars Palace in Las Vegas. Nominated customer service and sales executives from the U.S.A. and several other countries attended. More than 500 entries from companies of all sizes and in virtually every industry were submitted to this year's competition. There are 27 categories for customer service professionals, as well as 41 categories for sales professionals.\n\nDetails about the Stevie Awards for Sales & Customer Service and the list of honorees in all categories are available at www.stevieawards.com/sales. \n\r\n">>
+        }],
+       get_all_parts(Body,Boundary)).

apps/webmachine/src/webmachine_request.erl

 
 % actual interface for resource functions
 -export([
+         trim_state/0,
          get_reqdata/0,
          set_reqdata/1,
 	 socket/0,
 -include_lib("include/wm_reqstate.hrl").
 -include_lib("include/wm_reqdata.hrl").
 
--define(WMVSN, "1.5.1").
--define(QUIP, "hack the charles gibson").
+-define(WMVSN, "1.5.2").
+-define(QUIP, "that tip is the fix").
 -define(IDLE_TIMEOUT, infinity).
 
+trim_state() ->
+    TrimData = (ReqState#wm_reqstate.reqdata)#wm_reqdata{wm_state='WMSTATE'},
+    webmachine_request:new(ReqState#wm_reqstate{reqdata=TrimData}).
+
 get_peer() ->
-    case ReqState#reqstate.peer of
+    case ReqState#wm_reqstate.peer of
 	undefined ->
-            Socket = ReqState#reqstate.socket,
+            Socket = ReqState#wm_reqstate.socket,
 	    Peer = case inet:peername(Socket) of 
 		{ok, {Addr={10, _, _, _}, _Port}} ->
 		    case get_header_value("x-forwarded-for") of
 		{ok, {Addr, _Port}} ->
 		    inet_parse:ntoa(Addr)
             end,
-            NewReqState = ReqState#reqstate{peer=Peer},
+            NewReqState = ReqState#wm_reqstate{peer=Peer},
 	    {Peer, NewReqState};
 	_ ->
-	    {ReqState#reqstate.peer, ReqState}
+	    {ReqState#wm_reqstate.peer, ReqState}
     end.
 
-call(socket) -> {ReqState#reqstate.socket,ReqState};
-call(get_reqdata) -> {ReqState#reqstate.reqdata, ReqState};
-call({set_reqdata, RD}) -> {ok, ReqState#reqstate{reqdata=RD}};
-call(method) -> {wrq:method(ReqState#reqstate.reqdata), ReqState};
-call(version) -> {wrq:version(ReqState#reqstate.reqdata), ReqState};
-call(raw_path) -> {wrq:raw_path(ReqState#reqstate.reqdata), ReqState};
-call(req_headers) -> {wrq:req_headers(ReqState#reqstate.reqdata), ReqState};
+call(socket) -> {ReqState#wm_reqstate.socket,ReqState};
+call(get_reqdata) -> {ReqState#wm_reqstate.reqdata, ReqState};
+call({set_reqdata, RD}) -> {ok, ReqState#wm_reqstate{reqdata=RD}};
+call(method) -> {wrq:method(ReqState#wm_reqstate.reqdata), ReqState};
+call(version) -> {wrq:version(ReqState#wm_reqstate.reqdata), ReqState};
+call(raw_path) -> {wrq:raw_path(ReqState#wm_reqstate.reqdata), ReqState};
+call(req_headers) -> {wrq:req_headers(ReqState#wm_reqstate.reqdata), ReqState};
 call({req_body, MaxRecvBody}) ->
-    case ReqState#reqstate.bodyfetch of
+    case ReqState#wm_reqstate.bodyfetch of
         stream ->
             {stream_conflict, ReqState};
-        _ ->
-            RD=(ReqState#reqstate.reqdata)#wm_reqdata{
-                                      max_recv_body=MaxRecvBody},
-            NewReqState=ReqState#reqstate{reqdata=RD},
-            case RD#wm_reqdata.req_body of
-                not_fetched_yet ->
-                    NewBody = do_recv_body(NewReqState),
-                    NewRD = RD#wm_reqdata{req_body=NewBody},
-                    {NewBody, NewReqState#reqstate{
-                                bodyfetch=standard,reqdata=NewRD}};
-                X ->
-                    {X, ReqState#reqstate{bodyfetch=standard}}
-            end
+        standard ->
+            {ReqState#wm_reqstate.reqbody, ReqState};
+        undefined ->
+            RD=(ReqState#wm_reqstate.reqdata)#wm_reqdata{
+                 max_recv_body=MaxRecvBody},
+            NewReqState=ReqState#wm_reqstate{reqdata=RD},
+            NewBody = case get(req_body) of
+                undefined ->
+                    NewB = do_recv_body(NewReqState),
+                    put(req_body, NewB),
+                    NewB;
+                B -> B
+            end,
+            NewRD = RD#wm_reqdata{req_body=NewBody},
+            {NewBody, NewReqState#wm_reqstate{
+                        bodyfetch=standard,reqdata=NewRD,reqbody=NewBody}}
     end;
 call({stream_req_body, MaxHunk}) ->
-    case ReqState#reqstate.bodyfetch of
+    case ReqState#wm_reqstate.bodyfetch of
         standard ->
             {stream_conflict, ReqState};
         _ ->
             {recv_stream_body(ReqState, MaxHunk),
-             ReqState#reqstate{bodyfetch=stream}}
+             ReqState#wm_reqstate{bodyfetch=stream}}
     end;
-call(resp_headers) -> {wrq:resp_headers(ReqState#reqstate.reqdata), ReqState};
-call(resp_redirect) -> {wrq:resp_redirect(ReqState#reqstate.reqdata), ReqState};
+call(resp_headers) ->
+    {wrq:resp_headers(ReqState#wm_reqstate.reqdata), ReqState};
+call(resp_redirect) ->
+    {wrq:resp_redirect(ReqState#wm_reqstate.reqdata), ReqState};
 call({get_resp_header, HdrName}) ->
     Reply = mochiweb_headers:get_value(HdrName,
-                wrq:resp_headers(ReqState#reqstate.reqdata)),
+                wrq:resp_headers(ReqState#wm_reqstate.reqdata)),
     {Reply, ReqState};
 call(get_path_info) ->
-    PropList = dict:to_list(wrq:path_info(ReqState#reqstate.reqdata)),
+    PropList = dict:to_list(wrq:path_info(ReqState#wm_reqstate.reqdata)),
     {PropList, ReqState};
 call({get_path_info, Key}) ->
-    {wrq:path_info(Key, ReqState#reqstate.reqdata), ReqState};
+    {wrq:path_info(Key, ReqState#wm_reqstate.reqdata), ReqState};
 call(peer) -> get_peer();
 call(range) -> get_range();
-call(response_code) -> {wrq:response_code(ReqState#reqstate.reqdata), ReqState};
-call(app_root) -> {wrq:app_root(ReqState#reqstate.reqdata), ReqState};
-call(disp_path) -> {wrq:disp_path(ReqState#reqstate.reqdata), ReqState};
-call(path) -> {wrq:path(ReqState#reqstate.reqdata), ReqState};
+call(response_code) ->
+    {wrq:response_code(ReqState#wm_reqstate.reqdata), ReqState};
+call(app_root) -> {wrq:app_root(ReqState#wm_reqstate.reqdata), ReqState};
+call(disp_path) -> {wrq:disp_path(ReqState#wm_reqstate.reqdata), ReqState};
+call(path) -> {wrq:path(ReqState#wm_reqstate.reqdata), ReqState};
 call({get_req_header, K}) ->
-    {wrq:get_req_header(K, ReqState#reqstate.reqdata), ReqState};
+    {wrq:get_req_header(K, ReqState#wm_reqstate.reqdata), ReqState};
 call({set_response_code, Code}) ->
-    {ok, ReqState#reqstate{reqdata=wrq:set_response_code(
-                                     Code, ReqState#reqstate.reqdata)}};
+    {ok, ReqState#wm_reqstate{reqdata=wrq:set_response_code(
+                                     Code, ReqState#wm_reqstate.reqdata)}};
 call({set_resp_header, K, V}) ->
-    {ok, ReqState#reqstate{reqdata=wrq:set_resp_header(
-                                     K, V, ReqState#reqstate.reqdata)}};
+    {ok, ReqState#wm_reqstate{reqdata=wrq:set_resp_header(
+                                     K, V, ReqState#wm_reqstate.reqdata)}};
 call({set_resp_headers, Hdrs}) ->
-    {ok, ReqState#reqstate{reqdata=wrq:set_resp_headers(
-                                     Hdrs, ReqState#reqstate.reqdata)}};
+    {ok, ReqState#wm_reqstate{reqdata=wrq:set_resp_headers(
+                                     Hdrs, ReqState#wm_reqstate.reqdata)}};
 call({remove_resp_header, K}) ->
-    {ok, ReqState#reqstate{reqdata=wrq:remove_resp_header(
-                                     K, ReqState#reqstate.reqdata)}};
+    {ok, ReqState#wm_reqstate{reqdata=wrq:remove_resp_header(
+                                     K, ReqState#wm_reqstate.reqdata)}};
 call({merge_resp_headers, Hdrs}) ->
-    {ok, ReqState#reqstate{reqdata=wrq:merge_resp_headers(
-                                     Hdrs, ReqState#reqstate.reqdata)}};
+    {ok, ReqState#wm_reqstate{reqdata=wrq:merge_resp_headers(
+                                     Hdrs, ReqState#wm_reqstate.reqdata)}};
 call({append_to_response_body, Data}) ->
-    {ok, ReqState#reqstate{reqdata=wrq:append_to_response_body(
-                                     Data, ReqState#reqstate.reqdata)}};
+    {ok, ReqState#wm_reqstate{reqdata=wrq:append_to_response_body(
+                                     Data, ReqState#wm_reqstate.reqdata)}};
 call({set_disp_path, P}) ->
-    {ok, ReqState#reqstate{reqdata=wrq:set_disp_path(
-                                     P, ReqState#reqstate.reqdata)}};
+    {ok, ReqState#wm_reqstate{reqdata=wrq:set_disp_path(
+                                     P, ReqState#wm_reqstate.reqdata)}};
 call(do_redirect) ->
-    {ok, ReqState#reqstate{reqdata=wrq:do_redirect(true,
-                                                   ReqState#reqstate.reqdata)}};
+    {ok, ReqState#wm_reqstate{
+           reqdata=wrq:do_redirect(true, ReqState#wm_reqstate.reqdata)}};
 call({send_response, Code}) ->
     {Reply, NewState} = 
 	case Code of
 	    _ ->
 		    send_response(Code)
 	end,
-    LogData = NewState#reqstate.log_data,
+    LogData = NewState#wm_reqstate.log_data,
     NewLogData = LogData#wm_log_data{finish_time=now()},
-    {Reply, NewState#reqstate{log_data=NewLogData}};
-call(resp_body) -> {wrq:resp_body(ReqState#reqstate.reqdata), ReqState};
+    {Reply, NewState#wm_reqstate{log_data=NewLogData}};
+call(resp_body) -> {wrq:resp_body(ReqState#wm_reqstate.reqdata), ReqState};
 call({set_resp_body, Body}) ->
-    {ok, ReqState#reqstate{reqdata=wrq:set_resp_body(Body,
-                                       ReqState#reqstate.reqdata)}};
+    {ok, ReqState#wm_reqstate{reqdata=wrq:set_resp_body(Body,
+                                       ReqState#wm_reqstate.reqdata)}};
 call(has_resp_body) ->
-    Reply = case wrq:resp_body(ReqState#reqstate.reqdata) of
+    Reply = case wrq:resp_body(ReqState#wm_reqstate.reqdata) of
                 undefined -> false;
                 <<>> -> false;
                 [] -> false;
             end,
     {Reply, ReqState};
 call({get_metadata, Key}) ->
-    Reply = case dict:find(Key, ReqState#reqstate.metadata) of
+    Reply = case dict:find(Key, ReqState#wm_reqstate.metadata) of
 		{ok, Value} -> Value;
 		error -> undefined
 	    end,
     {Reply, ReqState};
 call({set_metadata, Key, Value}) ->
-    NewDict = dict:store(Key, Value, ReqState#reqstate.metadata),
-    {ok, ReqState#reqstate{metadata=NewDict}};
-call(path_tokens) -> {wrq:path_tokens(ReqState#reqstate.reqdata), ReqState};
-call(req_cookie) -> {wrq:req_cookie(ReqState#reqstate.reqdata), ReqState};
-call(req_qs) -> {wrq:req_qs(ReqState#reqstate.reqdata), ReqState};
+    NewDict = dict:store(Key, Value, ReqState#wm_reqstate.metadata),
+    {ok, ReqState#wm_reqstate{metadata=NewDict}};
+call(path_tokens) -> {wrq:path_tokens(ReqState#wm_reqstate.reqdata), ReqState};
+call(req_cookie) -> {wrq:req_cookie(ReqState#wm_reqstate.reqdata), ReqState};
+call(req_qs) -> {wrq:req_qs(ReqState#wm_reqstate.reqdata), ReqState};
 call({load_dispatch_data, PathProps, HostTokens, Port,
       PathTokens, AppRoot, DispPath}) ->
     PathInfo = dict:from_list(PathProps),
-    NewState = ReqState#reqstate{reqdata=wrq:load_dispatch_data(
+    NewState = ReqState#wm_reqstate{reqdata=wrq:load_dispatch_data(
                         PathInfo,HostTokens,Port,PathTokens,AppRoot,
-                        DispPath,ReqState#reqstate.reqdata)},
+                        DispPath,ReqState#wm_reqstate.reqdata)},
     {ok, NewState};
-call(log_data) -> {ReqState#reqstate.log_data, ReqState}.
+call(log_data) -> {ReqState#wm_reqstate.log_data, ReqState}.
 
 get_header_value(K) ->
-    {wrq:get_req_header(K, ReqState#reqstate.reqdata), ReqState}.
+    {wrq:get_req_header(K, ReqState#wm_reqstate.reqdata), ReqState}.
 
 get_outheader_value(K) ->
     {mochiweb_headers:get_value(K,
-      wrq:resp_headers(ReqState#reqstate.reqdata)), ReqState}.
+      wrq:resp_headers(ReqState#wm_reqstate.reqdata)), ReqState}.
 
 send(Socket, Data) ->
     case gen_tcp:send(Socket, iolist_to_binary(Data)) of
     Size = send_chunk(Socket, Data),
     send_stream_body(Socket, Next(), Size + SoFar).
 
+send_writer_body(Socket, {Encoder, Charsetter, BodyFun}) ->
+    put(bytes_written, 0),
+    Writer = fun(Data) ->
+        Size = send_chunk(Socket, Encoder(Charsetter(Data))),
+        put(bytes_written, get(bytes_written) + Size),
+        Size
+    end,
+    BodyFun(Writer),
+    send_chunk(Socket, <<>>),
+    get(bytes_written).
+
 send_chunk(Socket, Data) ->
     Size = iolist_size(Data),
     send(Socket, mochihex:to_hex(Size)),
     Size.
 
 send_ok_response() ->
-    RD0 = ReqState#reqstate.reqdata,
+    RD0 = ReqState#wm_reqstate.reqdata,
     {Range, State} = get_range(),
     case Range of
 	X when X =:= undefined; X =:= fail ->
                         [{"Accept-Ranges", "bytes"} | RangeHeaders], RD0),
                     RespBodyRD = wrq:set_resp_body(
                                    RangeBody, RespHdrsRD),
-		    NewState = State#reqstate{reqdata=RespBodyRD},
+		    NewState = State#wm_reqstate{reqdata=RespBodyRD},
 		    send_response(206, NewState)
 	    end
     end.
 
 send_response(Code) -> send_response(Code,ReqState).
-send_response(Code, PassedState=#reqstate{reqdata=RD}) ->
+send_response(Code, PassedState=#wm_reqstate{reqdata=RD}) ->
     Body0 = wrq:resp_body(RD),
     {Body,Length} = case Body0 of
-        {stream, StreamBody} -> {StreamBody, chunked};
+        {stream, StreamBody} -> {{stream, StreamBody}, chunked};
+        {writer, WriteBody} -> {{writer, WriteBody}, chunked};
         _ -> {Body0, iolist_size([Body0])}
     end,
-    send(PassedState#reqstate.socket,
+    send(PassedState#wm_reqstate.socket,
 	 [make_version(wrq:version(RD)),
           make_code(Code), <<"\r\n">> | 
          make_headers(Code, Length, RD)]),
     FinalLength = case wrq:method(RD) of 
 	'HEAD' -> Length;
 	_ -> 
-            case Length of
-                chunked -> send_stream_body(PassedState#reqstate.socket, Body);
-                _ -> send(PassedState#reqstate.socket, Body), Length
+            case Body of
+                {stream, Body2} ->
+                    send_stream_body(PassedState#wm_reqstate.socket, Body2);
+                {writer, Body2} ->
+                    send_writer_body(PassedState#wm_reqstate.socket, Body2);
+                _ ->
+                    send(PassedState#wm_reqstate.socket, Body),
+                    Length
             end
     end,
-    InitLogData = PassedState#reqstate.log_data,
+    InitLogData = PassedState#wm_reqstate.log_data,
     FinalLogData = InitLogData#wm_log_data{response_code=Code,
 					   response_length=FinalLength},
-    {ok, PassedState#reqstate{reqdata=wrq:set_response_code(Code, RD),
+    {ok, PassedState#wm_reqstate{reqdata=wrq:set_response_code(Code, RD),
                      log_data=FinalLogData}}.
 
 %% @doc  Infer body length from transfer-encoding and content-length headers.
 
 %% @doc Receive the body of the HTTP request (defined by Content-Length).
 %%      Will only receive up to the default max-body length
-do_recv_body(PassedState=#reqstate{reqdata=RD}) ->
+do_recv_body(PassedState=#wm_reqstate{reqdata=RD}) ->
     MRB = RD#wm_reqdata.max_recv_body,
     read_whole_stream(recv_stream_body(PassedState, MRB), [], MRB, 0).
 
             end
     end.
 
-recv_stream_body(PassedState=#reqstate{reqdata=RD}, MaxHunkSize) ->
+recv_stream_body(PassedState=#wm_reqstate{reqdata=RD}, MaxHunkSize) ->
     case get_header_value("expect") of
 	{"100-continue", _} ->
-	    send(PassedState#reqstate.socket, 
+	    send(PassedState#wm_reqstate.socket, 
 		 [make_version(wrq:version(RD)),
                   make_code(100), <<"\r\n">>]);
 	_Else ->
         {unknown_transfer_encoding, X} -> exit({unknown_transfer_encoding, X});
         undefined -> {<<>>, done};
         0 -> {<<>>, done};
-        chunked -> recv_chunked_body(PassedState#reqstate.socket, MaxHunkSize);
-        Length -> recv_unchunked_body(PassedState#reqstate.socket,
-                                                       MaxHunkSize, Length)
+        chunked -> recv_chunked_body(PassedState#wm_reqstate.socket,
+                                     MaxHunkSize);
+        Length -> recv_unchunked_body(PassedState#wm_reqstate.socket,
+                                      MaxHunkSize, Length)
     end.
 
 recv_unchunked_body(Socket, MaxHunk, DataLeft) ->
 get_range() ->
     case get_header_value("range") of
 	{undefined, _} ->
-	    {undefined, ReqState#reqstate{range=undefined}};
+	    {undefined, ReqState#wm_reqstate{range=undefined}};
 	{RawRange, _} ->
 	    Range = parse_range_request(RawRange),
-	    {Range, ReqState#reqstate{range=Range}}
+	    {Range, ReqState#wm_reqstate{range=Range}}
     end.
 
 range_parts(_RD=#wm_reqdata{resp_body={file, IoDevice}}, Ranges) ->
                     invalid_range ->
                         Acc;
                     {Skip, Length} ->
-                        <<_:Skip/binary, PartialBody:Length/binary, _/binary>> = Body,
+                        <<_:Skip/binary,
+                         PartialBody:Length/binary,
+                         _/binary>> = Body,
                         [{Skip, Skip + Length - 1, PartialBody} | Acc]
                 end
         end,
 
 req_cookie() -> call(req_cookie).
 parse_cookie() -> req_cookie().
-get_cookie_value(Key) -> proplists:get_value(Key, req_cookie()).
+get_cookie_value(Key) ->
+    {ReqCookie, NewReqState} = req_cookie(),
+    {proplists:get_value(Key, ReqCookie), NewReqState}.
 
 req_qs() -> call(req_qs).
 parse_qs() -> req_qs().
-get_qs_value(Key) -> proplists:get_value(Key, req_qs()).
-get_qs_value(Key, Default) -> proplists:get_value(Key, req_qs(), Default).
+get_qs_value(Key) ->
+    {ReqQS, NewReqState} = req_qs(),
+    {proplists:get_value(Key, ReqQS), NewReqState}.
+get_qs_value(Key, Default) ->
+    {ReqQS, NewReqState} = req_qs(),
+    {proplists:get_value(Key, ReqQS, Default), NewReqState}.
 
 set_resp_body(Body) -> call({set_resp_body, Body}).
 resp_body() -> call(resp_body).

apps/webmachine/src/webmachine_resource.erl

     RState0 = proplists:get_value(reqstate, ReqProps),
     put(tmp_reqstate, empty),
     {Reply, ReqData, NewModState} = handle_wm_call(Fun, 
-                    (RState0#reqstate.reqdata)#wm_reqdata{wm_state=RState0}),
+                    (RState0#wm_reqstate.reqdata)#wm_reqdata{wm_state=RState0}),
     ReqState = case get(tmp_reqstate) of
                    empty -> RState0;
                    X -> X
                end,
     {Reply,
      webmachine_resource:new(R_Mod, NewModState, R_ModExports, R_Trace),
-     ReqState#reqstate{reqdata=ReqData}}.
+     ReqState#wm_reqstate{reqdata=ReqData}}.
 
 handle_wm_call(Fun, ReqData) ->
     case default(Fun) of
             end
     end.
 
+trim_trace([{M,F,[RD = #wm_reqdata{},S]}|STRest]) ->
+    TrimState = (RD#wm_reqdata.wm_state)#wm_reqstate{reqdata='REQDATA'},
+    TrimRD = RD#wm_reqdata{wm_state=TrimState},
+    [{M,F,[TrimRD,S]}|STRest];
+trim_trace(X) -> X.
+
 resource_call(F, ReqData) ->
     case R_Trace of
         false -> nop;
     Result = try
         apply(R_Mod, F, [ReqData, R_ModState])
     catch C:R ->
-	    Reason = {C, R, erlang:get_stacktrace()},
+	    Reason = {C, R, trim_trace(erlang:get_stacktrace())},
             {{error, Reason}, ReqData, R_ModState}
     end,
         case R_Trace of
     {'WMTRACE_ESCAPED_PORT', erlang:port_to_list(Port)};
 escape_trace_data(List) when is_list(List) ->
     escape_trace_list(List, []);
+escape_trace_data(R=#wm_reqstate{}) ->
+    list_to_tuple(
+      escape_trace_data(
+        tuple_to_list(R#wm_reqstate{reqdata='WMTRACE_NESTED_REQDATA'})));
 escape_trace_data(Tuple) when is_tuple(Tuple) ->
     list_to_tuple(escape_trace_data(tuple_to_list(Tuple)));
 escape_trace_data(Other) ->

apps/webmachine/src/webmachine_util.erl

 -export([unquote_header/1]).
 -export([now_diff_milliseconds/2]).
 -export([media_type_to_detail/1]).
--export([test/0]).
+
+-include_lib("eunit/include/eunit.hrl").
 
 convert_request_date(Date) ->
     try 
 	    "application/x-gzip";
         ".htc" ->
             "text/x-component";
+	".manifest" ->
+	    "text/cache-manifest";
+        ".svg" ->
+            "image/svg+xml";
 	_ ->
 	    "text/plain"
     end.
 		    QVal = case Val of
 			"1" ->
 			    1;
+                        [$.|_] ->
+                            %% handle strange FeedBurner Accept
+                            list_to_float([$0|Val]); 
 			_ -> list_to_float(Val)
 		    end,
 		    {QVal, Type, Rest ++ Acc};
             case PrioStr of
                 "0" -> {0.0, Choice};
                 "1" -> {1.0, Choice};
+                [$.|_] ->
+                    %% handle strange FeedBurner Accept
+                    {list_to_float([$0|PrioStr]), Choice};
                 _ -> {list_to_float(PrioStr), Choice}
             end;
         {Choice} ->
 now_diff_milliseconds({M,S,U}, {M1,S1,U1}) ->
     ((M-M1)*1000000+(S-S1))*1000 + ((U-U1) div 1000).
 
-test() ->
-    test_choose_media_type(),
-    ok.
+%%
+%% TEST
+%%
 
-test_choose_media_type() ->
+choose_media_type_test() ->
     Provided = "text/html",
     ShouldMatch = ["*", "*/*", "text/*", "text/html"],
     WantNone = ["foo", "text/xml", "application/*", "foo/bar/baz"],
-    [ Provided = choose_media_type([Provided], I) || I <- ShouldMatch ],
-    [ none = choose_media_type([Provided], I) || I <- WantNone ],
-    ok.
+    [ ?assertEqual(Provided, choose_media_type([Provided], I))
+      || I <- ShouldMatch ],
+    [ ?assertEqual(none, choose_media_type([Provided], I))
+      || I <- WantNone ].
+
+choose_media_type_qval_test() ->
+    Provided = ["text/html", "image/jpeg"],
+    HtmlMatch = ["image/jpeg;q=0.5, text/html",
+                 "text/html, image/jpeg; q=0.5",
+                 "text/*; q=0.8, image/*;q=0.7",
+                 "text/*;q=.8, image/*;q=.7"], %% strange FeedBurner format
+    JpgMatch = ["image/*;q=1, text/html;q=0.9",
+                "image/png, image/*;q=0.3"],
+    [ ?assertEqual("text/html", choose_media_type(Provided, I))
+      || I <- HtmlMatch ],
+    [ ?assertEqual("image/jpeg", choose_media_type(Provided, I))
+      || I <- JpgMatch ].
+
+convert_request_date_test() ->
+    ?assertMatch({{_,_,_},{_,_,_}},
+                 convert_request_date("Wed, 30 Dec 2009 14:39:02 GMT")),
+    ?assertMatch(bad_date,
+                 convert_request_date(<<"does not handle binaries">>)).
+
+compare_ims_dates_test() ->
+    Late = {{2009,12,30},{14,39,02}},
+    Early = {{2009,12,30},{13,39,02}},
+    ?assertEqual(true, compare_ims_dates(Late, Early)),
+    ?assertEqual(false, compare_ims_dates(Early, Late)).
+
+guess_mime_test() ->
+    TextTypes = [".html",".css",".htc",".manifest",".txt"],
+    AppTypes = [".xhtml",".xml",".js",".swf",".zip",".bz2",
+                ".gz",".tar",".tgz"],
+    ImgTypes = [".jpg",".jpeg",".gif",".png",".ico",".svg"],
+    ?assertEqual([], [ T || T <- TextTypes,
+                            1 /= string:str(guess_mime(T),"text/") ]),
+    ?assertEqual([], [ T || T <- AppTypes,
+                            1 /= string:str(guess_mime(T),"application/") ]),
+    ?assertEqual([], [ T || T <- ImgTypes,
+                            1 /= string:str(guess_mime(T),"image/") ]).
+
+unquote_header_test() ->
+    ?assertEqual("hello", unquote_header("hello")),
+    ?assertEqual("hello", unquote_header("\"hello\"")),
+    ?assertEqual("hello", unquote_header("\"hello")),
+    ?assertEqual("hello", unquote_header("\"\\h\\e\\l\\l\\o\"")).
+
+now_diff_milliseconds_test() ->
+    Late = {10, 10, 10},
+    Early1 = {10, 9, 9},
+    Early2 = {9, 9, 9},
+    ?assertEqual(1000, now_diff_milliseconds(Late, Early1)),
+    ?assertEqual(1000001000, now_diff_milliseconds(Late, Early2)).

apps/webmachine/src/wrq.erl

 
 resp_body(_RD = #wm_reqdata{resp_body=undefined}) -> undefined;
 resp_body(_RD = #wm_reqdata{resp_body={stream,X}}) -> {stream,X};
+resp_body(_RD = #wm_reqdata{resp_body={writer,X}}) -> {writer,X};
 resp_body(_RD = #wm_reqdata{resp_body=RespB}) when is_binary(RespB) -> RespB;
 resp_body(_RD = #wm_reqdata{resp_body=RespB}) -> iolist_to_binary(RespB).
 

client_lib/jiak.rb

     # and 'acc' fields.  If a field is not defined in a spec hash,
     # the wildcard '_' will be used instead.
     def walk(bucket, key, spec)
-      do_req(Net::HTTP::Get.new(path(bucket, key)+convert_walk_spec(spec)),
+      do_req(Net::HTTP::Get.new(path(bucket, key)+'/'+convert_walk_spec(spec)),
              '200')
     end
 
   jc = Riak::Client.new("127.0.0.1", 8098)
 
   puts "Creating bucket foo..."
-  jc.set_bucket_schema('foo', {'allowed_fields'=>['bar','baz']})
+  jc.set_bucket_schema('foo', {'allowed_fields'=>['bar','baz'],
+                               'write_mask'=>['bar','baz']})
   b = jc.list_bucket('foo')
   puts "Bucket foo's schema: "
   p b['schema']