Kirill Korinskiy avatar Kirill Korinskiy committed 32fb6d9 Merge

Merge with upstream

Comments (0)

Files changed (84)

 fdc2c2e4cebea159a622dbb0716c5430a096030b riak-0.5.2
 5ffa6ae7e6999f9c765930c36a8b24d0e8b5874d riak-0.5.3
 11895faae1cec4e14f76ec1fb55b87e739299e05 riak-0.6
+93750f3fbbe2b4ca7cba3dbac16bad4039b84822 riak-0.6.1
+46af62b77937ee39060d55011ffc74768b88a011 riak-0.6.2
+46af62b77937ee39060d55011ffc74768b88a011 riak-0.6.2
+07bb099b7b102e603edfea30a0f3b780168b11f6 riak-0.6.2

client_lib/jiak.js

         this.baseurl += '/';
 
     this.opts = Opts||{};
+
+    // utility to convert an integer to base64-encoded 32-bits
+    base64 = function(N) {
+        var base64digits = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
+        return base64digits[(N >>> 26)]
+            +base64digits[((N >>> 20)&63)]
+            +base64digits[((N >>> 14)&63)]
+            +base64digits[((N >>> 8)&63)]
+            +base64digits[((N >>> 2)&63)]
+            +base64digits[((N << 4)&63)]
+            +'==';
+    }
+
+    if (('clientId' in this.opts) && !!this.opts.clientId) {
+        if (typeof this.opts.clientId == "number"
+            && this.opts.clientId > 0 && this.opts.clientId < 4294967296) {
+            this.opts.clientId = base64(this.opts.clientId);
+        }
+        //otherwise, just use whatever clientId was given
+    } else {
+        //choose a client id if the caller didn't provide one
+        this.opts.clientId = base64(Math.floor(Math.random()*4294967296));
+    }
 }
 
 JiakClient.prototype.store = function(Object, Callback, NoReturnBody, W, DW, R) {
+    var cid = this.opts.clientId;
     var req = {
         contentType: "application/json",
-        dataType: "json"
+        dataType: "json",
+        beforeSend: function(xhr) {
+            xhr.setRequestHeader("X-Riak-ClientId", cid);
+        }
     };
 
     if (this.opts.alwaysPost || !Object.key)
 }
 
 JiakClient.prototype.remove = function(Bucket, Key, Callback, RW) {
+    var cid = this.opts.clientId;
     return $.ajax({
         type:    'DELETE',
         url:     this.path(Bucket, Key)+
                    ((RW||this.opts.rw)?('?rw='+(RW||this.opts.rw)):''),
-        success: Callback
+        success: Callback,
+        beforeSend: function(xhr) {
+            xhr.setRequestHeader("X-Riak-ClientId", cid);
+        }
     });
 }
 

client_lib/jiak.php

                 array("Content-type: application/json")));
     }
     
-    function bucket_info($bucket) {
-        return $this->_expect(200,
-            $this->_do_req("GET", $this->JKP . urlencode($bucket)));
-    }
-    
     function store_all($ar, $w=2, $dw=2) {
         foreach($ar as $o) {
             $this->store($o, $w, $dw);
         $data = $this->_expect(200, $resp);
         return $this->_make_object($data);
     }
+
+    function list_bucket($bucket) {
+        $o = $this->bucket_info($bucket, true);
+        return $o['keys'];
+    }
+
+    function get_bucket_schema($bucket) {
+        $o = $this->bucket_info($bucket, false);
+        return $o['schema'];
+    }
+
+    function bucket_info($bucket, $with_keys=true) {
+        $resp = $this->_do_req("GET",
+                    $this->JKP . urlencode($bucket) . "/" .
+                    ($with_keys===true?"":"?keys=false"));
+        if ($resp['http_code'] == 404) {
+            return null;
+        }
+        $data = $this->_expect(200, $resp);
+        return $data;
+    }
     
     function delete($bucket, $key, $dw=2) {
         $resp = $this->_do_req("DELETE", $this->JKP .
     }
 }
 
+
 /*
     // jiak.php example
     //
         
         print ("\nDeleting jiak_test object ('test_object')...\n");
         $JC->delete("jiak_test", "test_object");
-        
+ 
+        print ("\nListing keys for bucket '" . $demo_bucket . "'...\n");
+        print_r($JC->list_bucket($demo_bucket));
+
+        print ("\nGetting jiak bucket schema for bucket '" . $demo_bucket . "'...\n");
+        print_r($JC->get_bucket_schema($demo_bucket));
+       
         print ("\nDone!\n\n");
         
     } catch (Exception $ex) {
     }
 */
 
-
 ?>

client_lib/jiak.rb

     @port = port
     @prefix = jiakPrefix
     @opts = options
+
+    if (@opts['clientId'])
+      if (@opts['clientId'].kind_of? Integer &&
+          @opts['clientId'] > 0 &&
+          @opts['clientId'] < 4294967296)
+        @opts['clientId'] = base64(@opts['clientId'])
+      end
+    else
+      @opts['clientId'] = base64(rand(4294967296))
+    end
   end
 
   # Set the schema for 'bucket'.  The schema parameter
       'r'=>(r||@opts['r'])
     }
     if (object['key'])
-      req = Net::HTTP::Put.new(path(object['bucket'], object['key'], q))
+      req = Net::HTTP::Put.new(path(object['bucket'], object['key'], q),
+                               initheader={"X-Riak-ClientId" => @opts['clientId']})
       code = '200'
     else
-      req = Net::HTTP::Post.new(path(object['bucket'], nil, q))
+      req = Net::HTTP::Post.new(path(object['bucket'], nil, q),
+                                initheader={"X-Riak-ClientId" => @opts['clientId']})
       code = '201'
     end
 
   # Delete the data stored in 'bucket' at 'key'
   def delete(bucket, key, rw=nil)
     do_req(Net::HTTP::Delete.new(path(bucket, key,
-                                      {'rw'=>(rw||@opts['rw'])})),
+                                      {'rw'=>(rw||@opts['rw'])}),
+                                 initheader={"X-Riak-ClientId" => @opts['clientId']}),
            '204')
   end
 
       raise JiakException.new(res.code+' '+res.message+' '+res.body)
     end
   end
+
+  def base64(n)
+    base64digits = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/="
+    "%c%c%c%c%c%c==" %
+      [base64digits[(n >> 26)],
+       base64digits[((n >> 20)&63)],
+       base64digits[((n >> 14)&63)],
+       base64digits[((n >> 8)&63)],
+       base64digits[((n >> 2)&63)],
+       base64digits[((n << 4)&63)]]
+  end
   private:convert_walk_spec
   private:path
   private:set_data
   private:do_req
+  private:base64
 end
 
 class JiakException<Exception

config/riak.erlenv

 {riak_web_ip, "127.0.0.1"}.
 %% riak_web_port is the TCP port that Riak's HTTP interface will bind to.
 {riak_web_port, 8098}.
-%% jiak_name is the first part of all URLs used by Riak's HTTP interface.
-%%  See the HTTP interface documents for more details.  If you change this,
+%% jiak_name is the first part of all URLs used by Riak's JSON HTTP interface.
+%%  See the JSON HTTP interface documents for more details.  If you change this,
 %%  the defaults used by some Riak HTTP clients may not work.
 {jiak_name, "jiak"}.
+%% raw_name is the first part of all URLS used by Riak's raw HTTP interface.
+%%  See riak_web.erl and raw_http_resource.erl for details.
+{raw_name, "raw"}.
+

demo/stickynotes/priv/www/js/jiak.js

         this.baseurl += '/';
 
     this.opts = Opts||{};
+
+    // utility to convert an integer to base64-encoded 32-bits
+    base64 = function(N) {
+        var base64digits = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=";
+        return base64digits[(N >>> 26)]
+            +base64digits[((N >>> 20)&63)]
+            +base64digits[((N >>> 14)&63)]
+            +base64digits[((N >>> 8)&63)]
+            +base64digits[((N >>> 2)&63)]
+            +base64digits[((N << 4)&63)]
+            +'==';
+    }
+
+    if (('clientId' in this.opts) && !!this.opts.clientId) {
+        if (typeof this.opts.clientId == "number"
+            && this.opts.clientId > 0 && this.opts.clientId < 4294967296) {
+            this.opts.clientId = base64(this.opts.clientId);
+        }
+        //otherwise, just use whatever clientId was given
+    } else {
+        //choose a client id if the caller didn't provide one
+        this.opts.clientId = base64(Math.floor(Math.random()*4294967296));
+    }
 }
 
 JiakClient.prototype.store = function(Object, Callback, NoReturnBody, W, DW, R) {
+    var cid = this.opts.clientId;
     var req = {
         contentType: "application/json",
-        dataType: "json"
+        dataType: "json",
+        beforeSend: function(xhr) {
+            xhr.setRequestHeader("X-Riak-ClientId", cid);
+        }
     };
 
     if (this.opts.alwaysPost || !Object.key)
 }
 
 JiakClient.prototype.remove = function(Bucket, Key, Callback, RW) {
+    var cid = this.opts.clientId;
     return $.ajax({
         type:    'DELETE',
         url:     this.path(Bucket, Key)+
                    ((RW||this.opts.rw)?('?rw='+(RW||this.opts.rw)):''),
-        success: Callback
+        success: Callback,
+        beforeSend: function(xhr) {
+            xhr.setRequestHeader("X-Riak-ClientId", cid);
+        }
     });
 }
 

deps/webmachine/THANKS

 The following people have contributed to Webmachine:
 
+Andy Gross
 Justin Sheehy
-Andy Gross
+John Muellerleile
 Robert Ahrens
 Jeremy Latt
 Bryan Fink
 Seth Falcon
 Tuncer Ayaz
 Martin Scholl
+Paul Mineiro
+Dave Smith
 

deps/webmachine/include/wm_reqdata.hrl

--record(wm_reqdata, {method, version, peer, wmreq,
+-record(wm_reqdata, {method, version, peer, wm_state,
                      disp_path, path, raw_path, path_info, path_tokens,
                      app_root,response_code,max_recv_body,
                      req_cookie, req_qs, req_headers, req_body,
-                     resp_redirect, resp_headers, resp_body
+                     resp_redirect, resp_headers, resp_body,
+                     host_tokens, port
                     }).
 

deps/webmachine/include/wm_reqstate.hrl

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

deps/webmachine/priv/skel/src/skel.erl

 
 -module(skel).
 -author('author <author@example.com>').
--export([start/0, stop/0]).
+-export([start/0, start_link/0, stop/0]).
 
 ensure_started(App) ->
     case application:start(App) of
 	{error, {already_started, App}} ->
 	    ok
     end.
-	
+
+%% @spec start_link() -> {ok,Pid::pid()}
+%% @doc Starts the app for inclusion in a supervisor tree
+start_link() ->
+    skel_deps:ensure(),
+    ensure_started(crypto),
+    application:set_env(webmachine, webmachine_logger_module, 
+                        webmachine_logger),
+    ensure_started(webmachine),
+    skel_sup:start_link().
+
 %% @spec start() -> ok
 %% @doc Start the skel server.
 start() ->
     skel_deps:ensure(),
     ensure_started(crypto),
+    application:set_env(webmachine, webmachine_logger_module, 
+                        webmachine_logger),
     ensure_started(webmachine),
     application:start(skel).
 

deps/webmachine/src/webmachine.app

 {application, webmachine,
  [{description, "webmachine"},
-  {vsn, "1.4"},
+  {vsn, "1.5"},
   {modules, [
     webmachine,
     webmachine_app,
     webmachine_resource,
     webmachine_request,
     webmachine_request_srv,
+    webmachine_skel,
     webmachine_sup,
     webmachine_mochiweb,
+    webmachine_multipart,
     webmachine_util,
     wrq,
     wmtrace_resource

deps/webmachine/src/webmachine.erl

 -export([start/0, stop/0]).
 -export([new_request/2]).
 
+-include("webmachine_logger.hrl").
+-include_lib("include/wm_reqstate.hrl").
+-include_lib("include/wm_reqdata.hrl").
+
 %% @spec start() -> ok
 %% @doc Start the webmachine server.
 start() ->
     RawPath = Request:get(raw_path), 
     Version = Request:get(version),
     Headers = Request:get(headers),
-    {ok, Pid} = webmachine_request_srv:start_link(Socket, Method, RawPath, Version, Headers),
-    webmachine_request:new(Pid).
+    InitState = #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)},
+    LogData = #wm_log_data{start_time=now(),
+			   method=Method,
+			   headers=Headers,
+			   peer=PeerState#reqstate.peer,
+			   path=RawPath,
+			   version=Version,
+			   response_code=404,
+			   response_length=0},
+    webmachine_request:new(PeerState#reqstate{log_data=LogData}).
+
+
 
 
   

deps/webmachine/src/webmachine_decision_core.erl

 -export([do_log/1]).
 -include("webmachine_logger.hrl").
 
-handle_request(Req, Resource) ->
-    put(req, Req),
+
+handle_request(Resource, ReqState) ->
     put(resource, Resource),
+    put(reqstate, ReqState),
     try
         d(v3b13)
     catch
     end.
 
 wrcall(X) ->
-    Req = get(req),
-    Req:call(X).
+    RS0 = get(reqstate),
+    Req = webmachine_request:new(RS0),
+    {Response, RS1} = Req:call(X),
+    put(reqstate, RS1),
+    Response.
+
+resource_call(Fun) ->
+    Resource = get(resource),
+    {Reply, NewResource, NewRS} = Resource:do(Fun,get()),
+    put(resource, NewResource),
+    put(reqstate, NewRS),
+    Reply.
 
 get_header_val(H) -> wrcall({get_req_header, H}).
 
 	404 ->
 	    {ok, ErrorHandler} = application:get_env(webmachine, error_handler),
 	    Reason = {none, none, []},
-	    ErrorHTML = ErrorHandler:render_error(Code, get(req), Reason),
+	    ErrorHTML = ErrorHandler:render_error(
+                          Code, {webmachine_request,get(reqstate)}, Reason),
             wrcall({set_resp_body, ErrorHTML});
         304 ->
             wrcall({remove_resp_header, "Content-Type"}),
     LogData = LogData0#wm_log_data{resource_module=RMod,
 				   end_time=EndTime},
     spawn(fun() -> do_log(LogData) end),
-    Resource:stop(),
-    Req = get(req),
-    Req:stop().
+    Resource:stop().
 
 respond(Code, Headers) ->
     wrcall({set_resp_headers, Headers}),
 
 error_response(Code, Reason) ->
     {ok, ErrorHandler} = application:get_env(webmachine, error_handler),
-    ErrorHTML = ErrorHandler:render_error(Code, get(req), Reason),
+    ErrorHTML = ErrorHandler:render_error(
+                  Code, {webmachine_request,get(reqstate)}, Reason),
     wrcall({set_resp_body, ErrorHTML}),
     respond(Code).
 error_response(Reason) ->
     error_response(ErrCode, Reason).
 
 do_log(LogData) ->
-    LoggerModule =
-	case application:get_env(webmachine, webmachine_logger_module) of
-	    {ok, Val} -> Val;
-	    _ -> webmachine_logger
-	end,
-    LoggerModule:log_access(LogData),
+    case application:get_env(webmachine, webmachine_logger_module) of
+        {ok, LoggerModule} -> LoggerModule:log_access(LogData);
+        _ -> nop
+    end,
     case application:get_env(webmachine, enable_perf_logger) of
 	{ok, true} ->
 	    webmachine_perf_logger:log(LogData);
 	    ignore
     end.
 
-resource_call(Fun) ->
-    Resource = get(resource),
-    {Reply, NewResource} = Resource:do(Fun,get()),
-    put(resource, NewResource),
-    Reply.
-
 log_decision(DecisionID) -> 
     Resource = get(resource),
     Resource:log_d(DecisionID).
             end;
         _ ->
             case resource_call(process_post) of
-                true -> stage1_ok;
+                true -> 
+                    encode_body_if_set(),
+                    stage1_ok;
                 {halt, Code} -> respond(Code);
                 Err -> error_response(Err)
             end
         [] -> {respond,415};
         AcceptedContentList ->
             F = hd(AcceptedContentList),
-            resource_call(F)
+            case resource_call(F) of
+                true ->
+                    encode_body_if_set(),
+                    true;
+                Result -> Result
+            end
+    end.
+
+encode_body_if_set() ->
+    case wrcall(has_resp_body) of
+        true ->
+            Body = wrcall(resp_body),
+            wrcall({set_resp_body, encode_body(Body)}),
+            true;
+        _ -> false
     end.
 
 encode_body(Body) ->

deps/webmachine/src/webmachine_dispatcher.erl

 -module(webmachine_dispatcher).
 -author('Robert Ahrens <rahrens@basho.com>').
 -author('Justin Sheehy <justin@basho.com>').
+-author('Bryan Fink <bryan@basho.com>').
 
--export([dispatch/2]).
+-export([dispatch/2, dispatch/3]).
 
 -define(SEPARATOR, $\/).
 -define(MATCH_ALL, '*').
 %% @doc Interface for URL dispatching.
 %% See also http://bitbucket.org/justin/webmachine/wiki/DispatchConfiguration
 dispatch(PathAsString, DispatchList) ->
+    dispatch([], PathAsString, DispatchList).
+
+%% @spec dispatch(Host::string(), Path::string(),
+%%                DispatchList::[matchterm()]) ->
+%%         dispterm() | dispfail()
+%% @doc Interface for URL dispatching.
+%% See also http://bitbucket.org/justin/webmachine/wiki/DispatchConfiguration
+dispatch(HostAsString, PathAsString, DispatchList) ->
     Path = string:tokens(PathAsString, [?SEPARATOR]),
     % URIs that end with a trailing slash are implicitly one token
     % "deeper" than we otherwise might think as we are "inside"
 		     true -> 1;
 		     _ -> 0
 		 end,
-    try_binding(DispatchList, Path, ExtraDepth).
+    {Host, Port} = split_host_port(HostAsString),
+    try_host_binding(DispatchList, lists:reverse(Host), Port,
+                     Path, ExtraDepth).
 
-%% @type matchterm() = {[pathterm()], matchmod(), matchopts()}.
+split_host_port(HostAsString) ->
+    case string:tokens(HostAsString, ":") of
+        [HostPart, PortPart] ->
+            {split_host(HostPart), list_to_integer(PortPart)};
+        [HostPart] ->
+            {split_host(HostPart), 80};
+        [] ->
+            %% no host header
+            {[], 80}
+    end.
+
+split_host(HostAsString) ->
+    string:tokens(HostAsString, ".").
+
+%% @type matchterm() = hostmatchterm() | pathmatchterm().
 % The dispatch configuration is a list of these terms, and the
+% first one whose host and path terms match the input is used.
+% Using a pathmatchterm() here is equivalent to using a hostmatchterm()
+% of the form {{['*'],'*'}, [pathmatchterm()]}.
+
+%% @type hostmatchterm() = {hostmatch(), [pathmatchterm()]}.
+% The dispatch configuration contains a list of these terms, and the
+% first one whose host and one pathmatchterm match is used.
+
+%% @type hostmatch() = [hostterm()] | {[hostterm()], portterm()}.
+% A host header (Host, X-Forwarded-For, etc.) will be matched against
+% this term.  Using a raws [hostterm()] list is equivalent to using
+% {[hostterm()], '*'}.
+
+%% @type hostterm() = '*' | string() | atom().
+% A list of hostterms is matched against a '.'-separated hostname.
+% The '*' hosterm matches all remaining tokens, and is only allowed at
+% the head of the list.
+% A string hostterm will match a token of exactly the same string.
+% Any atom hostterm other than '*' will match any token and will
+% create a binding in the result if a complete match occurs.
+
+%% @type portterm() = '*' | integer() | atom().
+% A portterm is matched against the integer port after any ':' in
+% the hostname, or 80 if no port is found.
+% The '*' portterm patches any port
+% An integer portterm will match a port of exactly the same integer.
+% Any atom portterm other than '*' will match any port and will
+% create a binding in the result if a complete match occurs.
+
+%% @type pathmatchterm() = {[pathterm()], matchmod(), matchopts()}.
+% The dispatch configuration contains a list of these terms, and the
 % first one whose list of pathterms matches the input path is used.
 
 %% @type pathterm() = '*' | string() | atom().
 
 %% @type dispfail() = {no_dispatch_match, pathtokens()}.
 
-try_binding([], PathTokens, _) ->
+try_host_binding([], Host, Port, Path, _Depth) ->
+    {no_dispatch_match, {Host, Port}, Path};
+try_host_binding([Dispatch|Rest], Host, Port, Path, Depth) ->
+    {{HostSpec,PortSpec},PathSpec} =
+        case Dispatch of
+            {{H,P},S} -> {{H,P},S};
+            {H,S}     -> {{H,?MATCH_ALL},S};
+            S         -> {{[?MATCH_ALL],?MATCH_ALL},[S]}
+        end,
+    case bind_port(PortSpec, Port, []) of
+        {ok, PortBindings} ->
+            case bind(lists:reverse(HostSpec), Host, PortBindings, 0) of
+                {ok, HostRemainder, HostBindings, _} ->
+                    case try_path_binding(PathSpec, Path, HostBindings, Depth) of
+                        {Mod, Props, PathRemainder, PathBindings,
+                         AppRoot, StringPath} ->
+                            {Mod, Props, HostRemainder, Port, PathRemainder,
+                             PathBindings, AppRoot, StringPath};
+                        {no_dispatch_match, _} ->
+                            try_host_binding(Rest, Host, Port, Path, Depth)
+                    end;
+                fail ->
+                    try_host_binding(Rest, Host, Port, Path, Depth)
+            end;
+        fail ->
+            try_host_binding(Rest, Host, Port, Path, Depth)
+    end.
+
+bind_port(Port, Port, Bindings) -> {ok, Bindings};
+bind_port(?MATCH_ALL, _Port, Bindings) -> {ok, Bindings};
+bind_port(PortAtom, Port, Bindings) when is_atom(PortAtom) ->
+    {ok, [{PortAtom, Port}|Bindings]};
+bind_port(_, _, _) -> fail.
+
+try_path_binding([], PathTokens, _, _) ->
     {no_dispatch_match, PathTokens};
-try_binding([{PathSchema, Mod, Props}|Rest], PathTokens, ExtraDepth) ->
-    case bind_path(PathSchema, PathTokens, [], 0) of
-        {ok, Remainder, Bindings, Depth} ->
-            {Mod, Props, Remainder, Bindings,
+try_path_binding([{PathSchema, Mod, Props}|Rest], PathTokens,
+                 Bindings, ExtraDepth) ->
+    case bind(PathSchema, PathTokens, Bindings, 0) of
+        {ok, Remainder, NewBindings, Depth} ->
+            {Mod, Props, Remainder, NewBindings,
              calculate_app_root(Depth + ExtraDepth), reconstitute(Remainder)};
         fail -> 
-            try_binding(Rest, PathTokens, ExtraDepth)
+            try_path_binding(Rest, PathTokens, Bindings, ExtraDepth)
     end.
 
-bind_path([], [], Bindings, Depth) ->
+bind([], [], Bindings, Depth) ->
     {ok, [], Bindings, Depth};
-bind_path([?MATCH_ALL], PathRest, Bindings, Depth) when is_list(PathRest) ->
-    {ok, PathRest, Bindings, Depth + length(PathRest)};
-bind_path(_, [], _, _) ->
+bind([?MATCH_ALL], Rest, Bindings, Depth) when is_list(Rest) ->
+    {ok, Rest, Bindings, Depth + length(Rest)};
+bind(_, [], _, _) ->
     fail;
-bind_path([Token|Rest],[Match|PathRest],Bindings,Depth) when is_atom(Token) ->
-    bind_path(Rest, PathRest, [{Token, Match}|Bindings], Depth + 1);
-bind_path([Token|Rest], [Token|PathRest], Bindings, Depth) ->
-    bind_path(Rest, PathRest, Bindings, Depth + 1);
-bind_path(_, _, _, _) ->
+bind([Token|RestToken],[Match|RestMatch],Bindings,Depth) when is_atom(Token) ->
+    bind(RestToken, RestMatch, [{Token, Match}|Bindings], Depth + 1);
+bind([Token|RestToken], [Token|RestMatch], Bindings, Depth) ->
+    bind(RestToken, RestMatch, Bindings, Depth + 1);
+bind(_, _, _, _) ->
     fail.
 
 reconstitute([]) -> "";

deps/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)
+        {true,_} -> Req:response_body();
+        {false,_} -> render_error_body(Code, Req, Reason)
     end.
 
 render_error_body(404, Req, _Reason) ->
-    Req:add_response_header("Content-Type", "text/html"),
-    <<"<HTML><HEAD><TITLE>404 Not Found</TITLE></HEAD><BODY><H1>Not Found</H1>The requested document was not found on this server.<P><HR><ADDRESS>mochiweb+webmachine web server</ADDRESS></BODY></HTML>">>;
+    {ok, ReqState} = Req:add_response_header("Content-Type", "text/html"),
+    {<<"<HTML><HEAD><TITLE>404 Not Found</TITLE></HEAD><BODY><H1>Not Found</H1>The requested document was not found on this server.<P><HR><ADDRESS>mochiweb+webmachine web server</ADDRESS></BODY></HTML>">>, ReqState};
 
 render_error_body(500, Req, Reason) ->
-    Req:add_response_header("Content-Type", "text/html"),
-    error_logger:error_msg("webmachine error: path=~p~n~p~n", [Req:path(), Reason]),
+    {ok, ReqState} = Req:add_response_header("Content-Type", "text/html"),
+    {Path,_} = Req:path(),
+    error_logger:error_msg("webmachine error: path=~p~n~p~n", [Path, Reason]),
     STString = io_lib:format("~p", [Reason]),
     ErrorStart = "<html><head><title>500 Internal Server Error</title></head><body><h1>Internal Server Error</h1>The server encountered an error while processing this request:<br><pre>",
     ErrorEnd = "</pre><P><HR><ADDRESS>mochiweb+webmachine web server</ADDRESS></body></html>",
     ErrorIOList = [ErrorStart,STString,ErrorEnd],
-    erlang:iolist_to_binary(ErrorIOList);
+    {erlang:iolist_to_binary(ErrorIOList), ReqState};
 
 render_error_body(501, Req, _Reason) ->
-    Req:add_response_header("Content-Type", "text/html"),
+    {ok, ReqState} = Req:add_response_header("Content-Type", "text/html"),
+    {Method,_} = Req:method(),
     error_logger:error_msg("Webmachine does not support method ~p~n",
-                           [Req:method()]),
+                           [Method]),
     ErrorStr = io_lib:format("<html><head><title>501 Not Implemented</title>"
                              "</head><body><h1>Internal Server Error</h1>"
                              "The server does not support the ~p method.<br>"
                              "<P><HR><ADDRESS>mochiweb+webmachine web server"
                              "</ADDRESS></body></html>",
-                             [Req:method()]),
-    erlang:iolist_to_binary(ErrorStr);
+                             [Method]),
+    {erlang:iolist_to_binary(ErrorStr), ReqState};
 
 render_error_body(503, Req, _Reason) ->
-    Req:add_response_header("Content-Type", "text/html"),
+    {ok, ReqState} = Req:add_response_header("Content-Type", "text/html"),
     error_logger:error_msg("Webmachine cannot fulfill"
-                           "the request at this time"),
+                           " the request at this time"),
     ErrorStr = "<html><head><title>503 Service Unavailable</title>"
                "</head><body><h1>Service Unavailable</h1>"
                "The server is currently unable to handle "
                "or maintenance of the server.<br>"
                "<P><HR><ADDRESS>mochiweb+webmachine web server"
                "</ADDRESS></body></html>",
-    list_to_binary(ErrorStr).
+    {list_to_binary(ErrorStr), ReqState}.
 

deps/webmachine/src/webmachine_logger.erl

     gen_server:cast(?MODULE, {refresh, Time}).
 
 log_access(#wm_log_data{}=D) ->
-    gen_server:call(?MODULE, {log_access, D}).
+    gen_server:cast(?MODULE, {log_access, D}).
 
-handle_call({log_access, LogData}, _From, State) ->
+handle_call(_Msg,_From,State) -> {noreply,State}.
+
+handle_cast({log_access, LogData}, State) ->
     NewState = maybe_rotate(State, now()),
     Msg = format_req(LogData),
     log_write(NewState#state.handle, Msg),
-    {reply, ok, NewState}.
-
+    {noreply, NewState};
 handle_cast({refresh, Time}, State) ->
     {noreply, maybe_rotate(State, Time)}.
 
+handle_info({_Label, {From, MRef}, get_modules}, State) ->
+    From ! {MRef, [?MODULE]},
+    {noreply, State};
 handle_info(_Info, State) ->
-    {ok, State}.
+    {noreply, State}.
 
 terminate(_Reason, _State) ->
     ok.

deps/webmachine/src/webmachine_mochiweb.erl

 loop(MochiReq) ->
     Req = webmachine:new_request(mochiweb, MochiReq),
     {ok, DispatchList} = application:get_env(webmachine, dispatch_list),
-    case webmachine_dispatcher:dispatch(Req:path(), DispatchList) of
-        {no_dispatch_match, _UnmatchedPathTokens} ->
+    Host = case host_headers(Req) of
+               [H|_] -> H;
+               [] -> []
+           end,
+    {Path, _} = Req:path(),
+    case webmachine_dispatcher:dispatch(Host, Path, DispatchList) of
+        {no_dispatch_match, _UnmatchedHost, _UnmatchedPathTokens} ->
             {ok, ErrorHandler} = application:get_env(webmachine, error_handler),
-	    ErrorHTML = ErrorHandler:render_error(404, Req, {none, none, []}),
-	    Req:append_to_response_body(ErrorHTML),
-	    Req:send_response(404),
-	    LogData = Req:log_data(),
-	    LogModule = 
-                case application:get_env(webmachine,webmachine_logger_module) of
-		    {ok, Val} -> Val;
-		    _ -> webmachine_logger
-		end,
-	    spawn(LogModule, log_access, [LogData]),
-	    Req:stop();
-        {Mod, ModOpts, PathTokens, Bindings, AppRoot, StringPath} ->
+	    {ErrorHTML,ReqState1} = 
+                ErrorHandler:render_error(404, Req, {none, none, []}),
+            Req1 = {webmachine_request,ReqState1},
+	    {ok,ReqState2} = Req1:append_to_response_body(ErrorHTML),
+            Req2 = {webmachine_request,ReqState2},
+	    {ok,ReqState3} = Req2:send_response(404),
+            Req3 = {webmachine_request,ReqState3},
+	    {LogData,_ReqState4} = Req3:log_data(),
+            case application:get_env(webmachine,webmachine_logger_module) of
+                {ok, LogModule} ->
+                    spawn(LogModule, log_access, [LogData]);
+                _ -> nop
+            end;
+        {Mod, ModOpts, HostTokens, Port, PathTokens, Bindings,
+         AppRoot, StringPath} ->
             BootstrapResource = webmachine_resource:new(x,x,x,x),
             {ok, Resource} = BootstrapResource:wrap(Mod, ModOpts),
-	    Req:load_dispatch_data(Bindings,PathTokens,AppRoot,StringPath,Req),
-	    Req:set_metadata('resource_module', Mod),
-            webmachine_decision_core:handle_request(Req, Resource)
+            {ok,RS1} = Req:load_dispatch_data(Bindings,HostTokens,Port,
+                                              PathTokens,AppRoot,StringPath),
+            XReq1 = {webmachine_request,RS1},
+            {ok,RS2} = XReq1:set_metadata('resource_module', Mod),
+            webmachine_decision_core:handle_request(Resource, RS2)
     end.
 
 get_option(Option, Options) ->
     {proplists:get_value(Option, Options), proplists:delete(Option, Options)}.
 
+host_headers(Req) ->
+    [ V || {V,_ReqState} <- [Req:get_header_value(H)
+                             || H <- ["x-forwarded-for",
+                                      "x-forwarded-host",
+                                      "x-forwarded-server",
+                                      "host"]],
+           V /= undefined].

deps/webmachine/src/webmachine_perf_logger.erl

     {noreply, maybe_rotate(State, Time)}.
 
 handle_info(_Info, State) ->
-    {ok, State}.
+    {noreply, State}.
 
 terminate(_Reason, _State) ->
     ok.

deps/webmachine/src/webmachine_request.erl

 
 %% @doc Webmachine HTTP Request Abstraction.
 
--module(webmachine_request, [Pid]).
+-module(webmachine_request, [ReqState]).
 -author('Justin Sheehy <justin@basho.com>').
 -author('Andy Gross <andy@basho.com>').
 
+-export([get_peer/0]). % used in initialization
+-export([call/1]). % internal switching interface, used by wrcall
+
+% actual interface for resource functions
 -export([
          get_reqdata/0,
          set_reqdata/1,
          disp_path/0,
 	 path/0,
 	 raw_path/0,
+         get_req_header/1,
 	 req_headers/0,
 	 req_body/1,
 	 stream_req_body/1,
          set_resp_body/1,
 	 response_body/0,
 	 has_response_body/0,
-	 stop/0,
          do_redirect/0,
          resp_redirect/0,
 	 set_metadata/2,
 	 get_metadata/1,
 	 get_path_info/0,
 	 get_path_info/1,
-	 load_dispatch_data/5,
+	 load_dispatch_data/6,
 	 get_path_tokens/0,
 	 get_app_root/0,
 	 parse_cookie/0,
 	 get_qs_value/1,
 	 get_qs_value/2,
          range/0,
-	 log_data/0,
-         call/1
+	 log_data/0
 	 ]).
 
--define(TIMEOUT, 150000).
+-include("webmachine_logger.hrl").
+-include_lib("include/wm_reqstate.hrl").
+-include_lib("include/wm_reqdata.hrl").
 
-call(Message) -> gen_server:call(Pid, Message, ?TIMEOUT).
+-define(WMVSN, "1.5.1").
+-define(QUIP, "hack the charles gibson").
+-define(IDLE_TIMEOUT, infinity).
+
+get_peer() ->
+    case ReqState#reqstate.peer of
+	undefined ->
+            Socket = ReqState#reqstate.socket,
+	    Peer = case inet:peername(Socket) of 
+		{ok, {Addr={10, _, _, _}, _Port}} ->
+		    case get_header_value("x-forwarded-for") of
+			{undefined, _} ->
+			    inet_parse:ntoa(Addr);
+			{Hosts, _} ->
+			    string:strip(lists:last(string:tokens(Hosts, ",")))
+		    end;
+		{ok, {{127, 0, 0, 1}, _Port}} ->
+		    case get_header_value("x-forwarded-for") of
+			{undefined, _} ->
+			    "127.0.0.1";
+			{Hosts, _} ->
+			    string:strip(lists:last(string:tokens(Hosts, ",")))
+		    end;
+		{ok, {Addr, _Port}} ->
+		    inet_parse:ntoa(Addr)
+            end,
+            NewReqState = ReqState#reqstate{peer=Peer},
+	    {Peer, NewReqState};
+	_ ->
+	    {ReqState#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({req_body, MaxRecvBody}) ->
+    case ReqState#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
+    end;
+call({stream_req_body, MaxHunk}) ->
+    case ReqState#reqstate.bodyfetch of
+        standard ->
+            {stream_conflict, ReqState};
+        _ ->
+            {recv_stream_body(ReqState, MaxHunk),
+             ReqState#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({get_resp_header, HdrName}) ->
+    Reply = mochiweb_headers:get_value(HdrName,
+                wrq:resp_headers(ReqState#reqstate.reqdata)),
+    {Reply, ReqState};
+call(get_path_info) ->
+    PropList = dict:to_list(wrq:path_info(ReqState#reqstate.reqdata)),
+    {PropList, ReqState};
+call({get_path_info, Key}) ->
+    {wrq:path_info(Key, ReqState#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({get_req_header, K}) ->
+    {wrq:get_req_header(K, ReqState#reqstate.reqdata), ReqState};
+call({set_response_code, Code}) ->
+    {ok, ReqState#reqstate{reqdata=wrq:set_response_code(
+                                     Code, ReqState#reqstate.reqdata)}};
+call({set_resp_header, K, V}) ->
+    {ok, ReqState#reqstate{reqdata=wrq:set_resp_header(
+                                     K, V, ReqState#reqstate.reqdata)}};
+call({set_resp_headers, Hdrs}) ->
+    {ok, ReqState#reqstate{reqdata=wrq:set_resp_headers(
+                                     Hdrs, ReqState#reqstate.reqdata)}};
+call({remove_resp_header, K}) ->
+    {ok, ReqState#reqstate{reqdata=wrq:remove_resp_header(
+                                     K, ReqState#reqstate.reqdata)}};
+call({merge_resp_headers, Hdrs}) ->
+    {ok, ReqState#reqstate{reqdata=wrq:merge_resp_headers(
+                                     Hdrs, ReqState#reqstate.reqdata)}};
+call({append_to_response_body, Data}) ->
+    {ok, ReqState#reqstate{reqdata=wrq:append_to_response_body(
+                                     Data, ReqState#reqstate.reqdata)}};
+call({set_disp_path, P}) ->
+    {ok, ReqState#reqstate{reqdata=wrq:set_disp_path(
+                                     P, ReqState#reqstate.reqdata)}};
+call(do_redirect) ->
+    {ok, ReqState#reqstate{reqdata=wrq:do_redirect(true,
+                                                   ReqState#reqstate.reqdata)}};
+call({send_response, Code}) ->
+    {Reply, NewState} = 
+	case Code of
+	    200 ->
+		    send_ok_response();
+	    _ ->
+		    send_response(Code)
+	end,
+    LogData = NewState#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};
+call({set_resp_body, Body}) ->
+    {ok, ReqState#reqstate{reqdata=wrq:set_resp_body(Body,
+                                       ReqState#reqstate.reqdata)}};
+call(has_resp_body) ->
+    Reply = case wrq:resp_body(ReqState#reqstate.reqdata) of
+                undefined -> false;
+                <<>> -> false;
+                [] -> false;
+                _ -> true
+            end,
+    {Reply, ReqState};
+call({get_metadata, Key}) ->
+    Reply = case dict:find(Key, ReqState#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};
+call({load_dispatch_data, PathProps, HostTokens, Port,
+      PathTokens, AppRoot, DispPath}) ->
+    PathInfo = dict:from_list(PathProps),
+    NewState = ReqState#reqstate{reqdata=wrq:load_dispatch_data(
+                        PathInfo,HostTokens,Port,PathTokens,AppRoot,
+                        DispPath,ReqState#reqstate.reqdata)},
+    {ok, NewState};
+call(log_data) -> {ReqState#reqstate.log_data, ReqState}.
+
+get_header_value(K) ->
+    {wrq:get_req_header(K, ReqState#reqstate.reqdata), ReqState}.
+
+get_outheader_value(K) ->
+    {mochiweb_headers:get_value(K,
+      wrq:resp_headers(ReqState#reqstate.reqdata)), ReqState}.
+
+send(Socket, Data) ->
+    case gen_tcp:send(Socket, iolist_to_binary(Data)) of
+	ok -> ok;
+	{error,closed} -> ok;
+	_ -> exit(normal)
+    end.
+
+send_stream_body(Socket, X) -> send_stream_body(Socket, X, 0).
+send_stream_body(Socket, {<<>>, done}, SoFar) ->
+    send_chunk(Socket, <<>>),
+    SoFar;
+send_stream_body(Socket, {Data, done}, SoFar) ->
+    Size = send_chunk(Socket, Data),
+    send_chunk(Socket, <<>>),
+    Size + SoFar;
+send_stream_body(Socket, {<<>>, Next}, SoFar) ->
+    send_stream_body(Socket, Next(), SoFar);
+send_stream_body(Socket, {[], Next}, SoFar) ->
+    send_stream_body(Socket, Next(), SoFar);
+send_stream_body(Socket, {Data, Next}, SoFar) ->
+    Size = send_chunk(Socket, Data),
+    send_stream_body(Socket, Next(), Size + SoFar).
+
+send_chunk(Socket, Data) ->
+    Size = iolist_size(Data),
+    send(Socket, mochihex:to_hex(Size)),
+    send(Socket, <<"\r\n">>),
+    send(Socket, Data),
+    send(Socket, <<"\r\n">>),
+    Size.
+
+send_ok_response() ->
+    RD0 = ReqState#reqstate.reqdata,
+    {Range, State} = get_range(),
+    case Range of
+	X when X =:= undefined; X =:= fail ->
+	    send_response(200);
+	Ranges ->
+	    {PartList, Size} = range_parts(RD0, Ranges),
+	    case PartList of
+		[] -> %% no valid ranges
+		    %% could be 416, for now we'll just return 200
+		    send_response(200);
+		PartList ->
+		    {RangeHeaders, RangeBody} = parts_to_body(PartList, Size),
+		    RespHdrsRD = wrq:set_resp_headers(
+                        [{"Accept-Ranges", "bytes"} | RangeHeaders], RD0),
+                    RespBodyRD = wrq:set_resp_body(
+                                   RangeBody, RespHdrsRD),
+		    NewState = State#reqstate{reqdata=RespBodyRD},
+		    send_response(206, NewState)
+	    end
+    end.
+
+send_response(Code) -> send_response(Code,ReqState).
+send_response(Code, PassedState=#reqstate{reqdata=RD}) ->
+    Body0 = wrq:resp_body(RD),
+    {Body,Length} = case Body0 of
+        {stream, StreamBody} -> {StreamBody, chunked};
+        _ -> {Body0, iolist_size([Body0])}
+    end,
+    send(PassedState#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
+            end
+    end,
+    InitLogData = PassedState#reqstate.log_data,
+    FinalLogData = InitLogData#wm_log_data{response_code=Code,
+					   response_length=FinalLength},
+    {ok, PassedState#reqstate{reqdata=wrq:set_response_code(Code, RD),
+                     log_data=FinalLogData}}.
+
+%% @doc  Infer body length from transfer-encoding and content-length headers.
+body_length() ->
+    case get_header_value("transfer-encoding") of
+        {undefined, _} ->
+            case get_header_value("content-length") of
+                {undefined, _} -> undefined;
+                {Length, _} -> list_to_integer(Length)
+            end;
+        {"chunked", _} -> chunked;
+        Unknown -> {unknown_transfer_encoding, Unknown}
+    end.
+
+%% @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}) ->
+    MRB = RD#wm_reqdata.max_recv_body,
+    read_whole_stream(recv_stream_body(PassedState, MRB), [], MRB, 0).
+
+read_whole_stream({Hunk,_}, _, MaxRecvBody, SizeAcc)
+  when SizeAcc + byte_size(Hunk) > MaxRecvBody -> 
+    {error, req_body_too_large};
+read_whole_stream({Hunk,Next}, Acc0, MaxRecvBody, SizeAcc) ->
+    HunkSize = byte_size(Hunk),
+    if SizeAcc + HunkSize > MaxRecvBody -> 
+            {error, req_body_too_large};
+       true ->
+            Acc = [Hunk|Acc0],
+            case Next of
+                done -> iolist_to_binary(lists:reverse(Acc));
+                _ -> read_whole_stream(Next(), Acc,
+                                       MaxRecvBody, SizeAcc + HunkSize)
+            end
+    end.
+
+recv_stream_body(PassedState=#reqstate{reqdata=RD}, MaxHunkSize) ->
+    case get_header_value("expect") of
+	{"100-continue", _} ->
+	    send(PassedState#reqstate.socket, 
+		 [make_version(wrq:version(RD)),
+                  make_code(100), <<"\r\n">>]);
+	_Else ->
+	    ok
+    end,
+    case body_length() of
+        {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)
+    end.
+
+recv_unchunked_body(Socket, MaxHunk, DataLeft) ->
+    case MaxHunk >= DataLeft of
+        true ->
+            {ok,Data1} = gen_tcp:recv(Socket,DataLeft,?IDLE_TIMEOUT),
+            {Data1, done};
+        false ->
+            {ok,Data2} = gen_tcp:recv(Socket,MaxHunk,?IDLE_TIMEOUT),
+            {Data2,
+             fun() -> recv_unchunked_body(
+                        Socket, MaxHunk, DataLeft-MaxHunk)
+             end}
+    end.
+    
+recv_chunked_body(Socket, MaxHunk) ->
+    case read_chunk_length(Socket) of
+        0 -> {<<>>, done};
+        ChunkLength -> recv_chunked_body(Socket,MaxHunk,ChunkLength)
+    end.
+recv_chunked_body(Socket, MaxHunk, LeftInChunk) ->
+    case MaxHunk >= LeftInChunk of
+        true ->
+            {ok,Data1} = gen_tcp:recv(Socket,LeftInChunk,?IDLE_TIMEOUT),
+            {Data1,
+             fun() -> recv_chunked_body(Socket, MaxHunk)
+             end};
+        false ->
+            {ok,Data2} = gen_tcp:recv(Socket,MaxHunk,?IDLE_TIMEOUT),
+            {Data2,
+             fun() -> recv_chunked_body(Socket, MaxHunk, LeftInChunk-MaxHunk)
+             end}
+    end.
+
+read_chunk_length(Socket) ->
+    inet:setopts(Socket, [{packet, line}]),
+    case gen_tcp:recv(Socket, 0, ?IDLE_TIMEOUT) of
+        {ok, Header} ->
+            inet:setopts(Socket, [{packet, raw}]),
+            Splitter = fun (C) ->
+                               C =/= $\r andalso C =/= $\n andalso C =/= $
+                       end,
+            {Hex, _Rest} = lists:splitwith(Splitter, binary_to_list(Header)),
+            case Hex of
+                [] -> 0;
+                _ -> erlang:list_to_integer(Hex, 16)
+            end;
+        _ ->
+            exit(normal)
+    end.
+
+get_range() ->
+    case get_header_value("range") of
+	{undefined, _} ->
+	    {undefined, ReqState#reqstate{range=undefined}};
+	{RawRange, _} ->
+	    Range = parse_range_request(RawRange),
+	    {Range, ReqState#reqstate{range=Range}}
+    end.
+
+range_parts(_RD=#wm_reqdata{resp_body={file, IoDevice}}, Ranges) ->
+    Size = iodevice_size(IoDevice),
+    F = fun (Spec, Acc) ->
+                case range_skip_length(Spec, Size) of
+                    invalid_range ->
+                        Acc;
+                    V ->
+                        [V | Acc]
+                end
+        end,
+    LocNums = lists:foldr(F, [], Ranges),
+    {ok, Data} = file:pread(IoDevice, LocNums),
+    Bodies = lists:zipwith(fun ({Skip, Length}, PartialBody) ->
+                                   {Skip, Skip + Length - 1, PartialBody}
+                           end,
+                           LocNums, Data),
+    {Bodies, Size};
+
+range_parts(RD=#wm_reqdata{resp_body={stream, {Hunk,Next}}}, Ranges) ->
+    % for now, streamed bodies are read in full for range requests
+    MRB = RD#wm_reqdata.max_recv_body,
+    range_parts(read_whole_stream({Hunk,Next}, [], MRB, 0), Ranges);
+
+range_parts(_RD=#wm_reqdata{resp_body=Body0}, Ranges) ->
+    Body = iolist_to_binary(Body0),
+    Size = size(Body),
+    F = fun(Spec, Acc) ->
+                case range_skip_length(Spec, Size) of
+                    invalid_range ->
+                        Acc;
+                    {Skip, Length} ->
+                        <<_:Skip/binary, PartialBody:Length/binary, _/binary>> = Body,
+                        [{Skip, Skip + Length - 1, PartialBody} | Acc]
+                end
+        end,
+    {lists:foldr(F, [], Ranges), Size}.
+
+range_skip_length(Spec, Size) ->
+    case Spec of
+        {none, R} when R =< Size, R >= 0 ->
+            {Size - R, R};
+        {none, _OutOfRange} ->
+            {0, Size};
+        {R, none} when R >= 0, R < Size ->
+            {R, Size - R};
+        {_OutOfRange, none} ->
+            invalid_range;
+        {Start, End} when 0 =< Start, Start =< End, End < Size ->
+            {Start, End - Start + 1};
+        {_OutOfRange, _End} ->
+            invalid_range
+    end.
+
+parse_range_request(RawRange) when is_list(RawRange) ->
+    try
+        "bytes=" ++ RangeString = RawRange,
+        Ranges = string:tokens(RangeString, ","),
+        lists:map(fun ("-" ++ V)  ->
+                          {none, list_to_integer(V)};
+                      (R) ->
+                          case string:tokens(R, "-") of
+                              [S1, S2] ->
+                                  {list_to_integer(S1), list_to_integer(S2)};
+                              [S] ->
+                                  {list_to_integer(S), none}
+                          end
+                  end,
+                  Ranges)
+    catch
+        _:_ ->
+            fail
+    end.
+
+parts_to_body([{Start, End, Body}], Size) ->
+    %% return body for a range reponse with a single body
+    ContentType = 
+	case get_outheader_value("content-type") of
+	    {undefined, _} ->
+		"text/html";
+	    {CT, _} ->
+		CT
+	end,
+    HeaderList = [{"Content-Type", ContentType},
+                  {"Content-Range",
+                   ["bytes ",
+                    make_io(Start), "-", make_io(End),
+                    "/", make_io(Size)]}],
+    {HeaderList, Body};
+parts_to_body(BodyList, Size) when is_list(BodyList) ->
+    %% return
+    %% header Content-Type: multipart/byteranges; boundary=441934886133bdee4
+    %% and multipart body
+    ContentType = 
+	case get_outheader_value("content-type") of
+	    {undefined, _} ->
+		"text/html";
+	    {CT, _} ->
+		CT
+	end,
+    Boundary = mochihex:to_hex(crypto:rand_bytes(8)),
+    HeaderList = [{"Content-Type",
+                   ["multipart/byteranges; ",
+                    "boundary=", Boundary]}],
+    MultiPartBody = multipart_body(BodyList, ContentType, Boundary, Size),
+    {HeaderList, MultiPartBody}.
+
+multipart_body([], _ContentType, Boundary, _Size) ->
+    ["--", Boundary, "--\r\n"];
+multipart_body([{Start, End, Body} | BodyList], ContentType, Boundary, Size) ->
+    ["--", Boundary, "\r\n",
+     "Content-Type: ", ContentType, "\r\n",
+     "Content-Range: ",
+         "bytes ", make_io(Start), "-", make_io(End),
+             "/", make_io(Size), "\r\n\r\n",
+     Body, "\r\n"
+     | multipart_body(BodyList, ContentType, Boundary, Size)].
+
+iodevice_size(IoDevice) ->
+    {ok, Size} = file:position(IoDevice, eof),
+    {ok, 0} = file:position(IoDevice, bof),
+    Size.
+
+make_io(Atom) when is_atom(Atom) ->
+    atom_to_list(Atom);
+make_io(Integer) when is_integer(Integer) ->
+    integer_to_list(Integer);
+make_io(Io) when is_list(Io); is_binary(Io) ->
+    Io.
+
+make_code(X) when is_integer(X) ->
+    [integer_to_list(X), [" " | httpd_util:reason_phrase(X)]];
+make_code(Io) when is_list(Io); is_binary(Io) ->
+    Io.
+
+make_version({1, 0}) ->
+    <<"HTTP/1.0 ">>;
+make_version(_) ->
+    <<"HTTP/1.1 ">>.
+
+make_headers(Code, Length, RD) ->
+    Hdrs0 = case Code of
+        304 ->
+            mochiweb_headers:make(wrq:resp_headers(RD));
+        _ -> 
+            case Length of
+                chunked ->
+                    mochiweb_headers:enter(
+                      "Transfer-Encoding","chunked",
+                      mochiweb_headers:make(wrq:resp_headers(RD)));
+                _ ->
+                    mochiweb_headers:enter(
+                      "Content-Length",integer_to_list(Length),
+                      mochiweb_headers:make(wrq:resp_headers(RD)))
+            end
+    end,
+    ServerHeader = "MochiWeb/1.1 WebMachine/" ++ ?WMVSN ++ " (" ++ ?QUIP ++ ")",
+    WithSrv = mochiweb_headers:enter("Server", ServerHeader, Hdrs0),
+    Hdrs = case mochiweb_headers:get_value("date", WithSrv) of
+	undefined ->
+            mochiweb_headers:enter("Date", httpd_util:rfc1123_date(), WithSrv);
+	_ ->
+	    WithSrv
+    end,
+    F = fun({K, V}, Acc) ->
+		[make_io(K), <<": ">>, V, <<"\r\n">> | Acc]
+	end,
+    lists:foldl(F, [<<"\r\n">>], mochiweb_headers:to_list(Hdrs)).
 
 get_reqdata() -> call(get_reqdata).
 
 get_qs_value(Key) -> proplists:get_value(Key, req_qs()).
 get_qs_value(Key, Default) -> proplists:get_value(Key, req_qs(), Default).
 
-stop() -> gen_server:cast(Pid, stop).
-
 set_resp_body(Body) -> call({set_resp_body, Body}).
 resp_body() -> call(resp_body).
 response_body() -> resp_body().
 
 get_req_header(K) -> call({get_req_header, K}).
-get_header_value(K) -> get_req_header(K).
 
 set_resp_header(K, V) -> call({set_resp_header, K, V}).
 add_response_header(K, V) -> set_resp_header(K, V).
 
 resp_redirect() -> call({resp_redirect}).
 
-send_response(Code) -> call({send_response, Code}).
-
 get_metadata(Key) -> call({get_metadata, Key}).
 
 set_metadata(Key, Value) -> call({set_metadata, Key, Value}).
 app_root() -> call(app_root).
 get_app_root() -> app_root().
 
-load_dispatch_data(Bindings, PathTokens, AppRoot, DispPath, Req) ->
-    call({load_dispatch_data, Bindings, PathTokens, AppRoot, DispPath, Req}).
+load_dispatch_data(Bindings, HostTokens, Port, PathTokens,
+                   AppRoot, DispPath) ->
+    call({load_dispatch_data, Bindings, HostTokens, Port,
+          PathTokens, AppRoot, DispPath}).
 
 log_data() -> call(log_data).

deps/webmachine/src/webmachine_request_srv.erl

-%% @author Andy Gross <andy@basho.com> 
-%% @author Justin Sheehy <justin@basho.com>
-%% @copyright 2007-2009 Basho Technologies
-%% Portions derived from code Copyright 2007-2008 Bob Ippolito, Mochi Media
-%%
-%%    Licensed under the Apache License, Version 2.0 (the "License");
-%%    you may not use this file except in compliance with the License.
-%%    You may obtain a copy of the License at
-%%
-%%        http://www.apache.org/licenses/LICENSE-2.0
-%%
-%%    Unless required by applicable law or agreed to in writing, software
-%%    distributed under the License is distributed on an "AS IS" BASIS,
-%%    WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
-%%    See the License for the specific language governing permissions and
-%%    limitations under the License.
-
--module(webmachine_request_srv).
--author('Justin Sheehy <justin@basho.com>').
--author('Andy Gross <andy@basho.com>').
--behaviour(gen_server).
--export([start_link/5]).
--export([init/1, handle_call/3, handle_cast/2, handle_info/2,
-	 terminate/2, code_change/3]).
--include("webmachine_logger.hrl").
--include_lib("include/wm_reqdata.hrl").
-
--define(WMVSN, "1.4").
--define(QUIP, "our dis is finally out").
-
-% 120 second default idle timeout
--define(IDLE_TIMEOUT, infinity).
--record(state, {socket=undefined,
-		metadata=dict:new(),
-		range=undefined,
-		peer=undefined,
-                reqdata=undefined,
-                bodyfetch=undefined,
-		log_data=#wm_log_data{}
-	       }).
-
-start_link(Socket, Method, RawPath, Version, Headers) ->
-    gen_server:start_link(?MODULE,
-                          [Socket, Method, RawPath, Version, Headers], []).
-
-init([Socket, Method, RawPath, Version, Headers]) ->
-    %%process_flag(trap_exit, true),
-    %% Calling get_peer() here is a little bit of an ugly way to populate the
-    %% client IP address but it will do for now.
-    {Peer, State} = get_peer(#state{socket=Socket,
-         reqdata=wrq:create(Method,Version,RawPath,Headers)}),
-    PeerState = State#state{reqdata=wrq:set_peer(Peer,State#state.reqdata)},
-    LogData = #wm_log_data{start_time=now(),
-			   method=Method,
-			   headers=Headers,
-			   peer=State#state.peer,
-			   path=RawPath,
-			   version=Version,
-			   response_code=404,
-			   response_length=0},
-    {ok, PeerState#state{log_data=LogData}}.
-
-handle_call(socket, _From, State) ->
-    Reply = State#state.socket,
-    {reply, Reply, State};
-handle_call(get_reqdata, _From, State) ->
-    {reply, State#state.reqdata, State};
-handle_call({set_reqdata, RD=#wm_reqdata{req_body=RBody}}, _From, State) ->
-    TheRD = case RBody of
-        not_fetched_yet ->
-            OldRD = State#state.reqdata,
-            OldBody = OldRD#wm_reqdata.req_body,
-            RD#wm_reqdata{req_body=OldBody};
-        _ ->
-            RD
-    end,
-    {reply, ok, State#state{reqdata=TheRD}};
-handle_call(method, _From, State) ->
-    {reply, wrq:method(State#state.reqdata), State};
-handle_call(version, _From, State) ->
-    {reply, wrq:version(State#state.reqdata), State};
-handle_call(raw_path, _From, State) ->
-    {reply, wrq:raw_path(State#state.reqdata), State};
-handle_call(req_headers, _From, State) ->
-    {reply, wrq:req_headers(State#state.reqdata), State};
-handle_call(req_body, _From, State=#state{bodyfetch=stream}) ->
-    {reply, stream_conflict, State};
-handle_call({req_body, MaxRecvBody}, _From, State0=#state{reqdata=RD0}) ->
-    RD=RD0#wm_reqdata{max_recv_body=MaxRecvBody},
-    State=State0#state{reqdata=RD},
-    {Body, FinalState} = case RD#wm_reqdata.req_body of
-        not_fetched_yet ->
-            NewBody = do_recv_body(State),
-            NewRD = RD#wm_reqdata{req_body=NewBody},
-            {NewBody, State#state{bodyfetch=standard,reqdata=NewRD}};
-        X ->
-            {X, State#state{bodyfetch=standard}}
-    end,
-    {reply, Body, FinalState};
-handle_call({stream_req_body,_}, _From, State=#state{bodyfetch=standard}) ->
-    {reply, stream_conflict, State};
-handle_call({stream_req_body, MaxHunk}, _From, State) ->
-    {reply, recv_stream_body(State, MaxHunk), State#state{bodyfetch=stream}};
-handle_call(resp_headers, _From, State) ->
-    {reply, wrq:resp_headers(State#state.reqdata), State};
-handle_call(resp_redirect, _From, State) ->
-    {reply, wrq:resp_redirect(State#state.reqdata), State};
-handle_call({get_resp_header, HdrName}, _From, State) ->
-    Reply = mochiweb_headers:get_value(HdrName,
-                wrq:resp_headers(State#state.reqdata)),
-    {reply, Reply, State};
-handle_call(get_path_info, _From, State) ->
-    PropList = dict:to_list(wrq:path_info(State#state.reqdata)),
-    {reply, PropList, State};
-handle_call({get_path_info, Key}, _From, State) ->
-    {reply, wrq:path_info(Key, State#state.reqdata), State};
-handle_call(peer, _From, State) ->
-    {Reply, NewState} = get_peer(State),
-    {reply, Reply, NewState};
-handle_call(range, _From, State) ->
-    {Reply, NewState} = get_range(State),
-    {reply, Reply, NewState};
-handle_call(response_code, _From, State) ->
-    {reply, wrq:response_code(State#state.reqdata), State};
-handle_call(app_root, _From, State) ->
-    {reply, wrq:app_root(State#state.reqdata), State};
-handle_call(disp_path, _From, State) ->
-    {reply, wrq:disp_path(State#state.reqdata), State};
-handle_call(path, _From, State) ->
-    {reply, wrq:path(State#state.reqdata), State};
-handle_call({get_req_header, K}, _From, State) ->
-    {reply, wrq:get_req_header(K, State#state.reqdata), State};
-handle_call({set_response_code, Code}, _From, State) ->
-    NewState = State#state{reqdata=wrq:set_response_code(
-                                     Code, State#state.reqdata)},
-    {reply, ok, NewState};
-handle_call({set_resp_header, K, V}, _From, State) ->
-    NewState = State#state{reqdata=wrq:set_resp_header(
-                                     K, V, State#state.reqdata)},
-    {reply, ok, NewState};
-handle_call({set_resp_headers, Hdrs}, _From, State) ->
-    NewState = State#state{reqdata=wrq:set_resp_headers(
-                                     Hdrs, State#state.reqdata)},
-    {reply, ok, NewState};
-handle_call({remove_resp_header, K}, _From, State) ->
-    NewState = State#state{reqdata=wrq:remove_resp_header(
-                                     K, State#state.reqdata)},
-    {reply, ok, NewState};
-handle_call({merge_resp_headers, Hdrs}, _From, State) ->
-    NewState = State#state{reqdata=wrq:merge_resp_headers(
-                                     Hdrs, State#state.reqdata)},
-    {reply, ok, NewState};
-handle_call({append_to_response_body, Data}, _From, State) ->
-    NewState = State#state{reqdata=wrq:append_to_response_body(
-                                     Data, State#state.reqdata)},
-    {reply, ok, NewState};
-handle_call({set_disp_path, P}, _From, State) ->
-    NewState = State#state{reqdata=wrq:set_disp_path(
-                                     P, State#state.reqdata)},
-    {reply, ok, NewState};
-handle_call(do_redirect, _From, State) ->
-    NewState = State#state{reqdata=wrq:do_redirect(true,
-                                     State#state.reqdata)},
-    {reply, ok, NewState};
-handle_call({send_response, Code}, _From, State) ->
-    {Reply, NewState} = 
-	case Code of
-	    200 ->
-		    send_ok_response(Code, State);
-	    _ ->
-		    send_response(Code, State)
-	end,
-    LogData = NewState#state.log_data,
-    NewLogData = LogData#wm_log_data{finish_time=now()},
-    {reply, Reply, NewState#state{log_data=NewLogData}};
-handle_call(resp_body, _From, State) ->
-    {reply, wrq:resp_body(State#state.reqdata), State};
-handle_call({set_resp_body, Body}, _From, State) ->
-    NewState = State#state{reqdata=wrq:set_resp_body(Body,
-                                     State#state.reqdata)},
-    {reply, ok, NewState};
-handle_call(has_resp_body, _From, State) ->
-    Reply = case wrq:resp_body(State#state.reqdata) of
-                undefined -> false;
-                <<>> -> false;
-                [] -> false;
-                _ -> true
-            end,
-    {reply, Reply, State};
-handle_call({get_metadata, Key}, _From, State) ->
-    Reply = case dict:find(Key, State#state.metadata) of
-		{ok, Value} -> Value;
-		error -> undefined
-	    end,
-    {reply, Reply, State};
-handle_call({set_metadata, Key, Value}, _From, State) ->
-    NewDict = dict:store(Key, Value, State#state.metadata),
-    {reply, ok, State#state{metadata=NewDict}};
-handle_call(path_tokens, _From, State) ->
-    {reply, wrq:path_tokens(State#state.reqdata), State};
-handle_call(req_cookie, _From, State) ->
-    {reply, wrq:req_cookie(State#state.reqdata), State};
-handle_call(req_qs, _From, State) ->
-    {reply, wrq:req_qs(State#state.reqdata), State};
-handle_call({load_dispatch_data, PathProps,PathTokens,AppRoot,DispPath,WMReq},
-            _From, State) ->
-    PathInfo = dict:from_list(PathProps),
-    NewState = State#state{reqdata=wrq:load_dispatch_data(
-               PathInfo,PathTokens,AppRoot,DispPath,WMReq,State#state.reqdata)},
-    {reply, ok, NewState};
-handle_call(log_data, _From, State) -> {reply, State#state.log_data, State}.
-
-handle_cast(stop, State) -> {stop, normal, State}.
-
-handle_info(_Info, State) -> {noreply, State}.
-
-terminate(_Reason, _State) -> ok.
-
-code_change(_OldVsn, State, _Extra) -> {ok, State}.
-
-get_peer(State) ->
-    case State#state.peer of
-	undefined ->
-            Socket = State#state.socket,
-	    Peer = case inet:peername(Socket) of 
-		{ok, {Addr={10, _, _, _}, _Port}} ->
-		    case get_header_value("x-forwarded-for", State) of
-			{undefined, _} ->
-			    inet_parse:ntoa(Addr);
-			{Hosts, _} ->
-			    string:strip(lists:last(string:tokens(Hosts, ",")))
-		    end;
-		{ok, {{127, 0, 0, 1}, _Port}} ->
-		    case get_header_value("x-forwarded-for", State) of
-			{undefined, _} ->
-			    "127.0.0.1";
-			{Hosts, _} ->
-			    string:strip(lists:last(string:tokens(Hosts, ",")))
-		    end;
-		{ok, {Addr, _Port}} ->
-		    inet_parse:ntoa(Addr)
-            end,
-            NewState = State#state{peer=Peer},
-	    {Peer, NewState};
-	_ ->
-	    {State#state.peer, State}
-    end.
-
-get_header_value(K, State) ->
-    {wrq:get_req_header(K, State#state.reqdata), State}.
-
-get_outheader_value(K, State) ->
-    {mochiweb_headers:get_value(K,
-      wrq:resp_headers(State#state.reqdata)), State}.
-
-send(Socket, Data) ->
-    case gen_tcp:send(Socket, iolist_to_binary(Data)) of
-	ok -> ok;
-	{error,closed} -> ok;
-	_ -> exit(normal)
-    end.
-
-send_stream_body(Socket, X) -> send_stream_body(Socket, X, 0).
-send_stream_body(Socket, {Data, done}, SoFar) ->
-    Size = send_chunk(Socket, Data),
-    send_chunk(Socket, <<>>),
-    Size + SoFar;
-send_stream_body(Socket, {Data, Next}, SoFar) ->
-    Size = send_chunk(Socket, Data),
-    send_stream_body(Socket, Next(), Size + SoFar).
-
-send_chunk(Socket, Data) ->
-    Size = iolist_size(Data),
-    send(Socket, mochihex:to_hex(Size)),
-    send(Socket, <<"\r\n">>),
-    send(Socket, Data),
-    send(Socket, <<"\r\n">>),
-    Size.
-
-send_ok_response(200, InitState) ->
-    RD0 = InitState#state.reqdata,
-    {Range, State} = get_range(InitState),
-    case Range of
-	X when X =:= undefined; X =:= fail ->
-	    send_response(200, State);
-	Ranges ->
-	    {PartList, Size} = range_parts(RD0, Ranges),
-	    case PartList of
-		[] -> %% no valid ranges
-		    %% could be 416, for now we'll just return 200
-		    send_response(200, State);
-		PartList ->
-		    {RangeHeaders, RangeBody} =
-			parts_to_body(PartList, State, Size),
-		    RespHdrsRD = wrq:set_resp_headers(
-                        [{"Accept-Ranges", "bytes"} | RangeHeaders], RD0),
-                    RespBodyRD = wrq:set_resp_body(
-                                   RangeBody, RespHdrsRD),
-		    NewState = State#state{reqdata=RespBodyRD},
-		    send_response(206, NewState)
-	    end
-    end.
-
-send_response(Code, State=#state{reqdata=RD}) ->
-    Body0 = wrq:resp_body(RD),
-    {Body,Length} = case Body0 of
-        {stream, StreamBody} -> {StreamBody, chunked};
-        _ -> {Body0, iolist_size([Body0])}
-    end,
-    send(State#state.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(State#state.socket, Body);
-                _ -> send(State#state.socket, Body), Length
-            end
-    end,
-    InitLogData = State#state.log_data,
-    FinalLogData = InitLogData#wm_log_data{response_code=Code,
-					   response_length=FinalLength},
-    {ok, State#state{reqdata=wrq:set_response_code(Code, RD),
-                     log_data=FinalLogData}}.
-
-%% @spec body_length(state()) -> undefined | chunked | unknown_transfer_encoding | integer()
-%% @doc  Infer body length from transfer-encoding and content-length headers.
-body_length(State) ->
-    case get_header_value("transfer-encoding", State) of
-        {undefined, _} ->
-            case get_header_value("content-length", State) of
-                {undefined, _} -> undefined;
-                {Length, _} -> list_to_integer(Length)
-            end;
-        {"chunked", _} -> chunked;
-        Unknown -> {unknown_transfer_encoding, Unknown}
-    end.
-
-%% @spec do_recv_body(state()) -> binary()
-%% @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(State=#state{reqdata=RD}) ->
-    MRB = RD#wm_reqdata.max_recv_body,
-    read_whole_stream(recv_stream_body(State, MRB), [], MRB, 0).
-
-read_whole_stream({Hunk,_}, _, MaxRecvBody, SizeAcc)
-  when SizeAcc + byte_size(Hunk) > MaxRecvBody -> 
-    {error, req_body_too_large};
-read_whole_stream({Hunk,Next}, Acc0, MaxRecvBody, SizeAcc) ->
-    HunkSize = byte_size(Hunk),
-    if SizeAcc + HunkSize > MaxRecvBody -> 
-            {error, req_body_too_large};
-       true ->
-            Acc = [Hunk|Acc0],
-            case Next of
-                done -> iolist_to_binary(lists:reverse(Acc));
-                _ -> read_whole_stream(Next(), Acc,
-                                       MaxRecvBody, SizeAcc + HunkSize)
-            end
-    end.
-
-recv_stream_body(State = #state{reqdata=RD}, MaxHunkSize) ->
-    case get_header_value("expect", State) of
-	{"100-continue", _} ->
-	    send(State#state.socket, 
-		 [make_version(wrq:version(RD)),
-                  make_code(100), <<"\r\n">>]);
-	_Else ->
-	    ok
-    end,
-    case body_length(State) of
-        {unknown_transfer_encoding, X} -> exit({unknown_transfer_encoding, X});
-        undefined -> {<<>>, done};
-        0 -> {<<>>, done};
-        chunked -> recv_chunked_body(State#state.socket, MaxHunkSize);
-        Length -> recv_unchunked_body(State#state.socket, MaxHunkSize, Length)
-    end.
-
-recv_unchunked_body(Socket, MaxHunk, DataLeft) ->
-    case MaxHunk >= DataLeft of
-        true ->
-            {ok,Data1} = gen_tcp:recv(Socket,DataLeft,?IDLE_TIMEOUT),
-            {Data1, done};
-        false ->
-            {ok,Data2} = gen_tcp:recv(Socket,MaxHunk,?IDLE_TIMEOUT),
-            {Data2,
-             fun() -> recv_unchunked_body(
-                        Socket, MaxHunk, DataLeft-MaxHunk)
-             end}
-    end.
-    
-recv_chunked_body(Socket, MaxHunk) ->
-    case read_chunk_length(Socket) of
-        0 -> {<<>>, done};
-        ChunkLength -> recv_chunked_body(Socket,MaxHunk,ChunkLength)
-    end.
-recv_chunked_body(Socket, MaxHunk, LeftInChunk) ->
-    case MaxHunk >= LeftInChunk of
-        true ->
-            {ok,Data1} = gen_tcp:recv(Socket,LeftInChunk,?IDLE_TIMEOUT),
-            {Data1,
-             fun() -> recv_chunked_body(Socket, MaxHunk)
-             end};
-        false ->
-            {ok,Data2} = gen_tcp:recv(Socket,MaxHunk,?IDLE_TIMEOUT),
-            {Data2,
-             fun() -> recv_chunked_body(Socket, MaxHunk, LeftInChunk-MaxHunk)
-             end}
-    end.
-
-read_chunk_length(Socket) ->
-    inet:setopts(Socket, [{packet, line}]),
-    case gen_tcp:recv(Socket, 0, ?IDLE_TIMEOUT) of
-        {ok, Header} ->
-            inet:setopts(Socket, [{packet, raw}]),
-            Splitter = fun (C) ->
-                               C =/= $\r andalso C =/= $\n andalso C =/= $
-                       end,
-            {Hex, _Rest} = lists:splitwith(Splitter, binary_to_list(Header)),
-            case Hex of
-                [] -> 0;
-                _ -> erlang:list_to_integer(Hex, 16)
-            end;
-        _ ->
-            exit(normal)
-    end.
-
-get_range(State) ->
-    case get_header_value("range", State) of
-	{undefined, _} ->
-	    {undefined, State#state{range=undefined}};
-	{RawRange, _} ->
-	    Range = parse_range_request(RawRange),
-	    {Range, State#state{range=Range}}
-    end.
-
-range_parts(_RD=#wm_reqdata{resp_body={file, IoDevice}}, Ranges) ->
-    Size = iodevice_size(IoDevice),
-    F = fun (Spec, Acc) ->
-                case range_skip_length(Spec, Size) of