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

[52/57] [abbrv] remove couch_httpd

http://git-wip-us.apache.org/repos/asf/couchdb/blob/c0855434/apps/couch_httpd/src/couch_httpd_oauth.erl
----------------------------------------------------------------------
diff --git a/apps/couch_httpd/src/couch_httpd_oauth.erl b/apps/couch_httpd/src/couch_httpd_oauth.erl
deleted file mode 100644
index 07229d3..0000000
--- a/apps/couch_httpd/src/couch_httpd_oauth.erl
+++ /dev/null
@@ -1,387 +0,0 @@
-% Licensed under the Apache License, Version 2.0 (the "License"); you may not
-% use this file except in compliance with the License.  You may obtain a copy of
-% the License at
-%
-%   http://www.apache.org/licenses/LICENSE-2.0
-%
-% Unless required by applicable law or agreed to in writing, software
-% distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-% WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.  See the
-% License for the specific language governing permissions and limitations under
-% the License.
-
--module(couch_httpd_oauth).
-
--include_lib("couch/include/couch_db.hrl").
--include_lib("couch/include/couch_js_functions.hrl").
-
--export([oauth_authentication_handler/1, handle_oauth_req/1]).
-
--define(OAUTH_DDOC_ID, <<"_design/oauth">>).
--define(OAUTH_VIEW_NAME, <<"oauth_credentials">>).
-
--record(callback_params, {
-    consumer,
-    token,
-    token_secret,
-    url,
-    signature,
-    params,
-    username
-}).
-
-% OAuth auth handler using per-node user db
-oauth_authentication_handler(Req) ->
-    serve_oauth(Req, fun oauth_auth_callback/2, true).
-
-
-oauth_auth_callback(Req, #callback_params{token_secret = undefined}) ->
-    couch_httpd:send_error(
-         Req, 400, <<"invalid_token">>, <<"Invalid OAuth token.">>);
-
-oauth_auth_callback(#httpd{mochi_req = MochiReq} = Req, CbParams) ->
-    Method = atom_to_list(MochiReq:get(method)),
-    #callback_params{
-        consumer = Consumer,
-        token_secret = TokenSecret,
-        url = Url,
-        signature = Sig,
-        params = Params,
-        username = User
-    } = CbParams,
-    case oauth:verify(Sig, Method, Url, Params, Consumer, TokenSecret) of
-    true ->
-        set_user_ctx(Req, User);
-    false ->
-        ?LOG_DEBUG("OAuth handler: signature verification failed for user `~p`~n"
-            "Received signature is `~p`~n"
-            "HTTP method is `~p`~n"
-            "URL is `~p`~n"
-            "Parameters are `~p`~n"
-            "Consumer is `~p`, token secret is `~p`~n"
-            "Expected signature was `~p`~n",
-            [User, Sig, Method, Url, Params, Consumer, TokenSecret,
-                oauth:signature(Method, Url, Params, Consumer, TokenSecret)]),
-        Req
-    end.
-
-
-% Look up the consumer key and get the roles to give the consumer
-set_user_ctx(_Req, undefined) ->
-    throw({bad_request, unknown_oauth_token});
-set_user_ctx(Req, Name) ->
-    case couch_auth_cache:get_user_creds(Name) of
-        nil ->
-            ?LOG_DEBUG("OAuth handler: user `~p` credentials not found", [Name]),
-            Req;
-        User ->
-            Roles = couch_util:get_value(<<"roles">>, User, []),
-            Req#httpd{user_ctx=#user_ctx{name=Name, roles=Roles}}
-    end.
-
-% OAuth request_token
-handle_oauth_req(#httpd{path_parts=[_OAuth, <<"request_token">>], method=Method}=Req1) ->
-    serve_oauth(Req1, fun(Req, CbParams) ->
-        #callback_params{
-            consumer = Consumer,
-            token_secret = TokenSecret,
-            url = Url,
-            signature = Sig,
-            params = Params
-        } = CbParams,
-        case oauth:verify(
-            Sig, atom_to_list(Method), Url, Params, Consumer, TokenSecret) of
-        true ->
-            ok(Req, <<"oauth_token=requestkey&oauth_token_secret=requestsecret">>);
-        false ->
-            invalid_signature(Req)
-        end
-    end, false);
-handle_oauth_req(#httpd{path_parts=[_OAuth, <<"authorize">>]}=Req) ->
-    {ok, serve_oauth_authorize(Req)};
-handle_oauth_req(#httpd{path_parts=[_OAuth, <<"access_token">>], method='GET'}=Req1) ->
-    serve_oauth(Req1, fun(Req, CbParams) ->
-        #callback_params{
-            consumer = Consumer,
-            token = Token,
-            url = Url,
-            signature = Sig,
-            params = Params
-        } = CbParams,
-        case Token of
-        "requestkey" ->
-            case oauth:verify(
-                Sig, "GET", Url, Params, Consumer, "requestsecret") of
-            true ->
-                ok(Req,
-                    <<"oauth_token=accesskey&oauth_token_secret=accesssecret">>);
-            false ->
-                invalid_signature(Req)
-            end;
-        _ ->
-            couch_httpd:send_error(
-                Req, 400, <<"invalid_token">>, <<"Invalid OAuth token.">>)
-        end
-    end, false);
-handle_oauth_req(#httpd{path_parts=[_OAuth, <<"access_token">>]}=Req) ->
-    couch_httpd:send_method_not_allowed(Req, "GET").
-
-invalid_signature(Req) ->
-    couch_httpd:send_error(Req, 400, <<"invalid_signature">>, <<"Invalid signature value.">>).
-
-% This needs to be protected i.e. force user to login using HTTP Basic Auth or form-based login.
-serve_oauth_authorize(#httpd{method=Method}=Req1) ->
-    case Method of
-        'GET' ->
-            % Confirm with the User that they want to authenticate the Consumer
-            serve_oauth(Req1, fun(Req, CbParams) ->
-                #callback_params{
-                    consumer = Consumer,
-                    token_secret = TokenSecret,
-                    url = Url,
-                    signature = Sig,
-                    params = Params
-                } = CbParams,
-                case oauth:verify(
-                    Sig, "GET", Url, Params, Consumer, TokenSecret) of
-                true ->
-                    ok(Req, <<"oauth_token=requestkey&",
-                        "oauth_token_secret=requestsecret">>);
-                false ->
-                    invalid_signature(Req)
-                end
-            end, false);
-        'POST' ->
-            % If the User has confirmed, we direct the User back to the Consumer with a verification code
-            serve_oauth(Req1, fun(Req, CbParams) ->
-                #callback_params{
-                    consumer = Consumer,
-                    token_secret = TokenSecret,
-                    url = Url,
-                    signature = Sig,
-                    params = Params
-                } = CbParams,
-                case oauth:verify(
-                    Sig, "POST", Url, Params, Consumer, TokenSecret) of
-                true ->
-                    %redirect(oauth_callback, oauth_token, oauth_verifier),
-                    ok(Req, <<"oauth_token=requestkey&",
-                        "oauth_token_secret=requestsecret">>);
-                false ->
-                    invalid_signature(Req)
-                end
-            end, false);
-        _ ->
-            couch_httpd:send_method_not_allowed(Req1, "GET,POST")
-    end.
-
-serve_oauth(#httpd{mochi_req=MochiReq}=Req, Fun, FailSilently) ->
-    % 1. In the HTTP Authorization header as defined in OAuth HTTP Authorization Scheme.
-    % 2. As the HTTP POST request body with a content-type of application/x-www-form-urlencoded.
-    % 3. Added to the URLs in the query part (as defined by [RFC3986] section 3).
-    AuthHeader = case MochiReq:get_header_value("authorization") of
-        undefined ->
-            "";
-        Else ->
-            [Head | Tail] = re:split(Else, "\\s", [{parts, 2}, {return, list}]),
-            case [string:to_lower(Head) | Tail] of
-                ["oauth", Rest] -> Rest;
-                _ -> ""
-            end
-    end,
-    HeaderParams = oauth:header_params_decode(AuthHeader),
-    %Realm = couch_util:get_value("realm", HeaderParams),
-
-    % get requested path
-    RequestedPath = case MochiReq:get_header_value("x-couchdb-requested-path") of
-        undefined ->
-            case MochiReq:get_header_value("x-couchdb-vhost-path") of
-                undefined ->
-                    MochiReq:get(raw_path);
-                VHostPath ->
-                    VHostPath
-            end;
-        RequestedPath0 ->
-           RequestedPath0
-    end,
-    {_, QueryString, _} = mochiweb_util:urlsplit_path(RequestedPath),
-
-    Params = proplists:delete("realm", HeaderParams) ++ mochiweb_util:parse_qs(QueryString),
-
-    ?LOG_DEBUG("OAuth Params: ~p", [Params]),
-    case couch_util:get_value("oauth_version", Params, "1.0") of
-        "1.0" ->
-            case couch_util:get_value("oauth_consumer_key", Params, undefined) of
-                undefined ->
-                    case FailSilently of
-                        true -> Req;
-                        false -> couch_httpd:send_error(Req, 400, <<"invalid_consumer">>, <<"Invalid consumer.">>)
-                    end;
-                ConsumerKey ->
-                    Url = couch_httpd:absolute_uri(Req, RequestedPath),
-                    case get_callback_params(ConsumerKey, Params, Url) of
-                        {ok, CallbackParams} ->
-                            Fun(Req, CallbackParams);
-                        invalid_consumer_token_pair ->
-                            couch_httpd:send_error(
-                                Req, 400,
-                                <<"invalid_consumer_token_pair">>,
-                                <<"Invalid consumer and token pair.">>);
-                        {error, {Error, Reason}} ->
-                            couch_httpd:send_error(Req, 400, Error, Reason)
-                    end
-            end;
-        _ ->
-            couch_httpd:send_error(Req, 400, <<"invalid_oauth_version">>, <<"Invalid OAuth version.">>)
-    end.
-
-
-get_callback_params(ConsumerKey, Params, Url) ->
-    Token = couch_util:get_value("oauth_token", Params),
-    SigMethod = sig_method(Params),
-    CbParams0 = #callback_params{
-        token = Token,
-        signature = couch_util:get_value("oauth_signature", Params),
-        params = proplists:delete("oauth_signature", Params),
-        url = Url
-    },
-    case oauth_credentials_info(Token, ConsumerKey) of
-    nil ->
-        invalid_consumer_token_pair;
-    {error, _} = Err ->
-        Err;
-    {OauthCreds} ->
-        User = couch_util:get_value(<<"username">>, OauthCreds, []),
-        ConsumerSecret = ?b2l(couch_util:get_value(
-            <<"consumer_secret">>, OauthCreds, <<>>)),
-        TokenSecret = ?b2l(couch_util:get_value(
-            <<"token_secret">>, OauthCreds, <<>>)),
-        case (User =:= []) orelse (ConsumerSecret =:= []) orelse
-            (TokenSecret =:= []) of
-        true ->
-            invalid_consumer_token_pair;
-        false ->
-            CbParams = CbParams0#callback_params{
-                consumer = {ConsumerKey, ConsumerSecret, SigMethod},
-                token_secret = TokenSecret,
-                username = User
-            },
-            ?LOG_DEBUG("Got OAuth credentials, for ConsumerKey `~p` and "
-                "Token `~p`, from the views, User: `~p`, "
-                "ConsumerSecret: `~p`, TokenSecret: `~p`",
-                [ConsumerKey, Token, User, ConsumerSecret, TokenSecret]),
-            {ok, CbParams}
-        end
-    end.
-
-
-sig_method(Params) ->
-    sig_method_1(couch_util:get_value("oauth_signature_method", Params)).
-sig_method_1("PLAINTEXT") ->
-    plaintext;
-% sig_method_1("RSA-SHA1") ->
-%    rsa_sha1;
-sig_method_1("HMAC-SHA1") ->
-    hmac_sha1;
-sig_method_1(_) ->
-    undefined.
-
-
-ok(#httpd{mochi_req=MochiReq}, Body) ->
-    {ok, MochiReq:respond({200, [], Body})}.
-
-
-oauth_credentials_info(Token, ConsumerKey) ->
-    case use_auth_db() of
-    {ok, Db} ->
-        Result = case query_oauth_view(Db, [?l2b(ConsumerKey), ?l2b(Token)]) of
-        [] ->
-            nil;
-        [Creds] ->
-            Creds;
-        [_ | _] ->
-            Reason = iolist_to_binary(
-                io_lib:format("Found multiple OAuth credentials for the pair "
-                    " (consumer_key: `~p`, token: `~p`)", [ConsumerKey, Token])),
-            {error, {<<"oauth_token_consumer_key_pair">>, Reason}}
-        end,
-        couch_db:close(Db),
-        Result;
-    nil ->
-        {
-            case couch_config:get("oauth_consumer_secrets", ConsumerKey) of
-            undefined -> [];
-            ConsumerSecret -> [{<<"consumer_secret">>, ?l2b(ConsumerSecret)}]
-            end
-            ++
-            case couch_config:get("oauth_token_secrets", Token) of
-            undefined -> [];
-            TokenSecret -> [{<<"token_secret">>, ?l2b(TokenSecret)}]
-            end
-            ++
-            case couch_config:get("oauth_token_users", Token) of
-            undefined -> [];
-            User -> [{<<"username">>, ?l2b(User)}]
-            end
-        }
-    end.
-
-
-use_auth_db() ->
-    case couch_config:get("couch_httpd_oauth", "use_users_db", "false") of
-    "false" ->
-        nil;
-    "true" ->
-        AuthDb = open_auth_db(),
-        {ok, _AuthDb2} = ensure_oauth_views_exist(AuthDb)
-    end.
-
-
-open_auth_db() ->
-    DbName = ?l2b(couch_config:get("couch_httpd_auth", "authentication_db")),
-    DbOptions = [{user_ctx, #user_ctx{roles = [<<"_admin">>]}}],
-    {ok, AuthDb} = couch_db:open_int(DbName, DbOptions),
-    AuthDb.
-
-
-ensure_oauth_views_exist(AuthDb) ->
-    case couch_db:open_doc(AuthDb, ?OAUTH_DDOC_ID, []) of
-    {ok, _DDoc} ->
-        {ok, AuthDb};
-    _ ->
-        {ok, DDoc} = get_oauth_ddoc(),
-        {ok, _Rev} = couch_db:update_doc(AuthDb, DDoc, []),
-        {ok, _AuthDb2} = couch_db:reopen(AuthDb)
-    end.
-
-
-get_oauth_ddoc() ->
-    Json = {[
-        {<<"_id">>, ?OAUTH_DDOC_ID},
-        {<<"language">>, <<"javascript">>},
-        {<<"views">>,
-            {[
-                {?OAUTH_VIEW_NAME,
-                    {[
-                        {<<"map">>, ?OAUTH_MAP_FUN}
-                    ]}
-                }
-            ]}
-        }
-    ]},
-    {ok, couch_doc:from_json_obj(Json)}.
-
-
-query_oauth_view(Db, Key) ->
-    ViewOptions = [
-        {start_key, Key},
-        {end_key, Key}
-    ],
-    Callback = fun({row, Row}, Acc) ->
-            {ok, [couch_util:get_value(value, Row) | Acc]};
-        (_, Acc) ->
-            {ok, Acc}
-    end,
-    {ok, Result} = couch_mrview:query_view(
-        Db, ?OAUTH_DDOC_ID, ?OAUTH_VIEW_NAME, ViewOptions, Callback, []),
-    Result.

http://git-wip-us.apache.org/repos/asf/couchdb/blob/c0855434/apps/couch_httpd/src/couch_httpd_proxy.erl
----------------------------------------------------------------------
diff --git a/apps/couch_httpd/src/couch_httpd_proxy.erl b/apps/couch_httpd/src/couch_httpd_proxy.erl
deleted file mode 100644
index bce2c7f..0000000
--- a/apps/couch_httpd/src/couch_httpd_proxy.erl
+++ /dev/null
@@ -1,426 +0,0 @@
-% Licensed under the Apache License, Version 2.0 (the "License"); you may not
-% use this file except in compliance with the License. You may obtain a copy of
-% the License at
-%
-%   http://www.apache.org/licenses/LICENSE-2.0
-%
-% Unless required by applicable law or agreed to in writing, software
-% distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-% WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-% License for the specific language governing permissions and limitations under
-% the License.
--module(couch_httpd_proxy).
-
--export([handle_proxy_req/2]).
-
--include_lib("couch/include/couch_db.hrl").
--include_lib("ibrowse/include/ibrowse.hrl").
-
--define(TIMEOUT, infinity).
--define(PKT_SIZE, 4096).
-
-
-handle_proxy_req(Req, ProxyDest) ->
-    Method = get_method(Req),
-    Url = get_url(Req, ProxyDest),
-    Version = get_version(Req),
-    Headers = get_headers(Req),
-    Body = get_body(Req),
-    Options = [
-        {http_vsn, Version},
-        {headers_as_is, true},
-        {response_format, binary},
-        {stream_to, {self(), once}}
-    ],
-    case ibrowse:send_req(Url, Headers, Method, Body, Options, ?TIMEOUT) of
-        {ibrowse_req_id, ReqId} ->
-            stream_response(Req, ProxyDest, ReqId);
-        {error, Reason} ->
-            throw({error, Reason})
-    end.
-
-
-get_method(#httpd{mochi_req=MochiReq}) ->
-    case MochiReq:get(method) of
-        Method when is_atom(Method) ->
-            list_to_atom(string:to_lower(atom_to_list(Method)));
-        Method when is_list(Method) ->
-            list_to_atom(string:to_lower(Method));
-        Method when is_binary(Method) ->
-            list_to_atom(string:to_lower(?b2l(Method)))
-    end.
-
-
-get_url(Req, ProxyDest) when is_binary(ProxyDest) ->
-    get_url(Req, ?b2l(ProxyDest));
-get_url(#httpd{mochi_req=MochiReq}=Req, ProxyDest) ->
-    BaseUrl = case mochiweb_util:partition(ProxyDest, "/") of
-        {[], "/", _} -> couch_httpd:absolute_uri(Req, ProxyDest);
-        _ -> ProxyDest
-    end,
-    ProxyPrefix = "/" ++ ?b2l(hd(Req#httpd.path_parts)),
-    RequestedPath = MochiReq:get(raw_path),
-    case mochiweb_util:partition(RequestedPath, ProxyPrefix) of
-        {[], ProxyPrefix, []} ->
-            BaseUrl;
-        {[], ProxyPrefix, [$/ | DestPath]} ->
-            remove_trailing_slash(BaseUrl) ++ "/" ++ DestPath;
-        {[], ProxyPrefix, DestPath} ->
-            remove_trailing_slash(BaseUrl) ++ "/" ++ DestPath;
-        _Else ->
-            throw({invalid_url_path, {ProxyPrefix, RequestedPath}})
-    end.
-
-get_version(#httpd{mochi_req=MochiReq}) ->
-    MochiReq:get(version).
-
-
-get_headers(#httpd{mochi_req=MochiReq}) ->
-    to_ibrowse_headers(mochiweb_headers:to_list(MochiReq:get(headers)), []).
-
-to_ibrowse_headers([], Acc) ->
-    lists:reverse(Acc);
-to_ibrowse_headers([{K, V} | Rest], Acc) when is_atom(K) ->
-    to_ibrowse_headers([{atom_to_list(K), V} | Rest], Acc);
-to_ibrowse_headers([{K, V} | Rest], Acc) when is_list(K) ->
-    case string:to_lower(K) of
-        "content-length" ->
-            to_ibrowse_headers(Rest, [{content_length, V} | Acc]);
-        % This appears to make ibrowse too smart.
-        %"transfer-encoding" ->
-        %    to_ibrowse_headers(Rest, [{transfer_encoding, V} | Acc]);
-        _ ->
-            to_ibrowse_headers(Rest, [{K, V} | Acc])
-    end.
-
-get_body(#httpd{method='GET'}) ->
-    fun() -> eof end;
-get_body(#httpd{method='HEAD'}) ->
-    fun() -> eof end;
-get_body(#httpd{method='DELETE'}) ->
-    fun() -> eof end;
-get_body(#httpd{mochi_req=MochiReq}) ->
-    case MochiReq:get(body_length) of
-        undefined ->
-            <<>>;
-        {unknown_transfer_encoding, Unknown} ->
-            exit({unknown_transfer_encoding, Unknown});
-        chunked ->
-            {fun stream_chunked_body/1, {init, MochiReq, 0}};
-        0 ->
-            <<>>;
-        Length when is_integer(Length) andalso Length > 0 ->
-            {fun stream_length_body/1, {init, MochiReq, Length}};
-        Length ->
-            exit({invalid_body_length, Length})
-    end.
-
-
-remove_trailing_slash(Url) ->
-    rem_slash(lists:reverse(Url)).
-
-rem_slash([]) ->
-    [];
-rem_slash([$\s | RevUrl]) ->
-    rem_slash(RevUrl);
-rem_slash([$\t | RevUrl]) ->
-    rem_slash(RevUrl);
-rem_slash([$\r | RevUrl]) ->
-    rem_slash(RevUrl);
-rem_slash([$\n | RevUrl]) ->
-    rem_slash(RevUrl);
-rem_slash([$/ | RevUrl]) ->
-    rem_slash(RevUrl);
-rem_slash(RevUrl) ->
-    lists:reverse(RevUrl).
-
-
-stream_chunked_body({init, MReq, 0}) ->
-    % First chunk, do expect-continue dance.
-    init_body_stream(MReq),
-    stream_chunked_body({stream, MReq, 0, [], ?PKT_SIZE});
-stream_chunked_body({stream, MReq, 0, Buf, BRem}) ->
-    % Finished a chunk, get next length. If next length
-    % is 0, its time to try and read trailers.
-    {CRem, Data} = read_chunk_length(MReq),
-    case CRem of
-        0 ->
-            BodyData = lists:reverse(Buf, Data),
-            {ok, BodyData, {trailers, MReq, [], ?PKT_SIZE}};
-        _ ->
-            stream_chunked_body(
-                {stream, MReq, CRem, [Data | Buf], BRem-size(Data)}
-            )
-    end;
-stream_chunked_body({stream, MReq, CRem, Buf, BRem}) when BRem =< 0 ->
-    % Time to empty our buffers to the upstream socket.
-    BodyData = lists:reverse(Buf),
-    {ok, BodyData, {stream, MReq, CRem, [], ?PKT_SIZE}};
-stream_chunked_body({stream, MReq, CRem, Buf, BRem}) ->
-    % Buffer some more data from the client.
-    Length = lists:min([CRem, BRem]),
-    Socket = MReq:get(socket),
-    NewState = case mochiweb_socket:recv(Socket, Length, ?TIMEOUT) of
-        {ok, Data} when size(Data) == CRem ->
-            case mochiweb_socket:recv(Socket, 2, ?TIMEOUT) of
-                {ok, <<"\r\n">>} ->
-                    {stream, MReq, 0, [<<"\r\n">>, Data | Buf], BRem-Length-2};
-                _ ->
-                    exit(normal)
-            end;
-        {ok, Data} ->
-            {stream, MReq, CRem-Length, [Data | Buf], BRem-Length};
-        _ ->
-            exit(normal)
-    end,
-    stream_chunked_body(NewState);
-stream_chunked_body({trailers, MReq, Buf, BRem}) when BRem =< 0 ->
-    % Empty our buffers and send data upstream.
-    BodyData = lists:reverse(Buf),
-    {ok, BodyData, {trailers, MReq, [], ?PKT_SIZE}};
-stream_chunked_body({trailers, MReq, Buf, BRem}) ->
-    % Read another trailer into the buffer or stop on an
-    % empty line.
-    Socket = MReq:get(socket),
-    mochiweb_socket:setopts(Socket, [{packet, line}]),
-    case mochiweb_socket:recv(Socket, 0, ?TIMEOUT) of
-        {ok, <<"\r\n">>} ->
-            mochiweb_socket:setopts(Socket, [{packet, raw}]),
-            BodyData = lists:reverse(Buf, <<"\r\n">>),
-            {ok, BodyData, eof};
-        {ok, Footer} ->
-            mochiweb_socket:setopts(Socket, [{packet, raw}]),
-            NewState = {trailers, MReq, [Footer | Buf], BRem-size(Footer)},
-            stream_chunked_body(NewState);
-        _ ->
-            exit(normal)
-    end;
-stream_chunked_body(eof) ->
-    % Tell ibrowse we're done sending data.
-    eof.
-
-
-stream_length_body({init, MochiReq, Length}) ->
-    % Do the expect-continue dance
-    init_body_stream(MochiReq),
-    stream_length_body({stream, MochiReq, Length});
-stream_length_body({stream, _MochiReq, 0}) ->
-    % Finished streaming.
-    eof;
-stream_length_body({stream, MochiReq, Length}) ->
-    BufLen = lists:min([Length, ?PKT_SIZE]),
-    case MochiReq:recv(BufLen) of
-        <<>> -> eof;
-        Bin -> {ok, Bin, {stream, MochiReq, Length-BufLen}}
-    end.
-
-
-init_body_stream(MochiReq) ->
-    Expect = case MochiReq:get_header_value("expect") of
-        undefined ->
-            undefined;
-        Value when is_list(Value) ->
-            string:to_lower(Value)
-    end,
-    case Expect of
-        "100-continue" ->
-            MochiReq:start_raw_response({100, gb_trees:empty()});
-        _Else ->
-            ok
-    end.
-
-
-read_chunk_length(MochiReq) ->
-    Socket = MochiReq:get(socket),
-    mochiweb_socket:setopts(Socket, [{packet, line}]),
-    case mochiweb_socket:recv(Socket, 0, ?TIMEOUT) of
-        {ok, Header} ->
-            mochiweb_socket:setopts(Socket, [{packet, raw}]),
-            Splitter = fun(C) ->
-                C =/= $\r andalso C =/= $\n andalso C =/= $\s
-            end,
-            {Hex, _Rest} = lists:splitwith(Splitter, ?b2l(Header)),
-            {mochihex:to_int(Hex), Header};
-        _ ->
-            exit(normal)
-    end.
-
-
-stream_response(Req, ProxyDest, ReqId) ->
-    receive
-        {ibrowse_async_headers, ReqId, "100", _} ->
-            % ibrowse doesn't handle 100 Continue responses which
-            % means we have to discard them so the proxy client
-            % doesn't get confused.
-            ibrowse:stream_next(ReqId),
-            stream_response(Req, ProxyDest, ReqId);
-        {ibrowse_async_headers, ReqId, Status, Headers} ->
-            {Source, Dest} = get_urls(Req, ProxyDest),
-            FixedHeaders = fix_headers(Source, Dest, Headers, []),
-            case body_length(FixedHeaders) of
-                chunked ->
-                    {ok, Resp} = couch_httpd:start_chunked_response(
-                        Req, list_to_integer(Status), FixedHeaders
-                    ),
-                    ibrowse:stream_next(ReqId),
-                    stream_chunked_response(Req, ReqId, Resp),
-                    {ok, Resp};
-                Length when is_integer(Length) ->
-                    {ok, Resp} = couch_httpd:start_response_length(
-                        Req, list_to_integer(Status), FixedHeaders, Length
-                    ),
-                    ibrowse:stream_next(ReqId),
-                    stream_length_response(Req, ReqId, Resp),
-                    {ok, Resp};
-                _ ->
-                    {ok, Resp} = couch_httpd:start_response(
-                        Req, list_to_integer(Status), FixedHeaders
-                    ),
-                    ibrowse:stream_next(ReqId),
-                    stream_length_response(Req, ReqId, Resp),
-                    % XXX: MochiWeb apparently doesn't look at the
-                    % response to see if it must force close the
-                    % connection. So we help it out here.
-                    erlang:put(mochiweb_request_force_close, true),
-                    {ok, Resp}
-            end
-    end.
-
-
-stream_chunked_response(Req, ReqId, Resp) ->
-    receive
-        {ibrowse_async_response, ReqId, {error, Reason}} ->
-            throw({error, Reason});
-        {ibrowse_async_response, ReqId, Chunk} ->
-            couch_httpd:send_chunk(Resp, Chunk),
-            ibrowse:stream_next(ReqId),
-            stream_chunked_response(Req, ReqId, Resp);
-        {ibrowse_async_response_end, ReqId} ->
-            couch_httpd:last_chunk(Resp)
-    end.
-
-
-stream_length_response(Req, ReqId, Resp) ->
-    receive
-        {ibrowse_async_response, ReqId, {error, Reason}} ->
-            throw({error, Reason});
-        {ibrowse_async_response, ReqId, Chunk} ->
-            couch_httpd:send(Resp, Chunk),
-            ibrowse:stream_next(ReqId),
-            stream_length_response(Req, ReqId, Resp);
-        {ibrowse_async_response_end, ReqId} ->
-            ok
-    end.
-
-
-get_urls(Req, ProxyDest) ->
-    SourceUrl = couch_httpd:absolute_uri(Req, "/" ++ hd(Req#httpd.path_parts)),
-    Source = parse_url(?b2l(iolist_to_binary(SourceUrl))),
-    case (catch parse_url(ProxyDest)) of
-        Dest when is_record(Dest, url) ->
-            {Source, Dest};
-        _ ->
-            DestUrl = couch_httpd:absolute_uri(Req, ProxyDest),
-            {Source, parse_url(DestUrl)}
-    end.
-
-
-fix_headers(_, _, [], Acc) ->
-    lists:reverse(Acc);
-fix_headers(Source, Dest, [{K, V} | Rest], Acc) ->
-    Fixed = case string:to_lower(K) of
-        "location" -> rewrite_location(Source, Dest, V);
-        "content-location" -> rewrite_location(Source, Dest, V);
-        "uri" -> rewrite_location(Source, Dest, V);
-        "destination" -> rewrite_location(Source, Dest, V);
-        "set-cookie" -> rewrite_cookie(Source, Dest, V);
-        _ -> V
-    end,
-    fix_headers(Source, Dest, Rest, [{K, Fixed} | Acc]).
-
-
-rewrite_location(Source, #url{host=Host, port=Port, protocol=Proto}, Url) ->
-    case (catch parse_url(Url)) of
-        #url{host=Host, port=Port, protocol=Proto} = Location ->
-            DestLoc = #url{
-                protocol=Source#url.protocol,
-                host=Source#url.host,
-                port=Source#url.port,
-                path=join_url_path(Source#url.path, Location#url.path)
-            },
-            url_to_url(DestLoc);
-        #url{} ->
-            Url;
-        _ ->
-            url_to_url(Source#url{path=join_url_path(Source#url.path, Url)})
-    end.
-
-
-rewrite_cookie(_Source, _Dest, Cookie) ->
-    Cookie.
-
-
-parse_url(Url) when is_binary(Url) ->
-    ibrowse_lib:parse_url(?b2l(Url));
-parse_url(Url) when is_list(Url) ->
-    ibrowse_lib:parse_url(?b2l(iolist_to_binary(Url))).
-
-
-join_url_path(Src, Dst) ->
-    Src2 = case lists:reverse(Src) of
-        "/" ++ RestSrc -> lists:reverse(RestSrc);
-        _ -> Src
-    end,
-    Dst2 = case Dst of
-        "/" ++ RestDst -> RestDst;
-        _ -> Dst
-    end,
-    Src2 ++ "/" ++ Dst2.
-
-
-url_to_url(#url{host=Host, port=Port, path=Path, protocol=Proto} = Url) ->
-    LPort = case {Proto, Port} of
-        {http, 80} -> "";
-        {https, 443} -> "";
-        _ -> ":" ++ integer_to_list(Port)
-    end,
-    LPath = case Path of
-        "/" ++ _RestPath -> Path;
-        _ -> "/" ++ Path
-    end,
-    HostPart = case Url#url.host_type of
-        ipv6_address ->
-            "[" ++ Host ++ "]";
-        _ ->
-            Host
-    end,
-    atom_to_list(Proto) ++ "://" ++ HostPart ++ LPort ++ LPath.
-
-
-body_length(Headers) ->
-    case is_chunked(Headers) of
-        true -> chunked;
-        _ -> content_length(Headers)
-    end.
-
-
-is_chunked([]) ->
-    false;
-is_chunked([{K, V} | Rest]) ->
-    case string:to_lower(K) of
-        "transfer-encoding" ->
-            string:to_lower(V) == "chunked";
-        _ ->
-            is_chunked(Rest)
-    end.
-
-content_length([]) ->
-    undefined;
-content_length([{K, V} | Rest]) ->
-    case string:to_lower(K) of
-        "content-length" ->
-            list_to_integer(V);
-        _ ->
-            content_length(Rest)
-    end.
-

http://git-wip-us.apache.org/repos/asf/couchdb/blob/c0855434/apps/couch_httpd/src/couch_httpd_rewrite.erl
----------------------------------------------------------------------
diff --git a/apps/couch_httpd/src/couch_httpd_rewrite.erl b/apps/couch_httpd/src/couch_httpd_rewrite.erl
deleted file mode 100644
index 011c3c8..0000000
--- a/apps/couch_httpd/src/couch_httpd_rewrite.erl
+++ /dev/null
@@ -1,480 +0,0 @@
-% Licensed under the Apache License, Version 2.0 (the "License"); you may not
-% use this file except in compliance with the License. You may obtain a copy of
-% the License at
-%
-% http://www.apache.org/licenses/LICENSE-2.0
-%
-% Unless required by applicable law or agreed to in writing, software
-% distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-% WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-% License for the specific language governing permissions and limitations under
-% the License.
-%
-% bind_path is based on bind method from Webmachine
-
-
-%% @doc Module for URL rewriting by pattern matching.
-
--module(couch_httpd_rewrite).
--export([handle_rewrite_req/3]).
--include_lib("couch/include/couch_db.hrl").
-
--define(SEPARATOR, $\/).
--define(MATCH_ALL, {bind, <<"*">>}).
-
-
-%% doc The http rewrite handler. All rewriting is done from
-%% /dbname/_design/ddocname/_rewrite by default.
-%%
-%% each rules should be in rewrites member of the design doc.
-%% Ex of a complete rule :
-%%
-%%  {
-%%      ....
-%%      "rewrites": [
-%%      {
-%%          "from": "",
-%%          "to": "index.html",
-%%          "method": "GET",
-%%          "query": {}
-%%      }
-%%      ]
-%%  }
-%%
-%%  from: is the path rule used to bind current uri to the rule. It
-%% use pattern matching for that.
-%%
-%%  to: rule to rewrite an url. It can contain variables depending on binding
-%% variables discovered during pattern matching and query args (url args and from
-%% the query member.)
-%%
-%%  method: method to bind the request method to the rule. by default "*"
-%%  query: query args you want to define they can contain dynamic variable
-%% by binding the key to the bindings
-%%
-%%
-%% to and from are path with  patterns. pattern can be string starting with ":" or
-%% "*". ex:
-%% /somepath/:var/*
-%%
-%% This path is converted in erlang list by splitting "/". Each var are
-%% converted in atom. "*" is converted to '*' atom. The pattern matching is done
-%% by splitting "/" in request url in a list of token. A string pattern will
-%% match equal token. The star atom ('*' in single quotes) will match any number
-%% of tokens, but may only be present as the last pathtern in a pathspec. If all
-%% tokens are matched and all pathterms are used, then the pathspec matches. It works
-%% like webmachine. Each identified token will be reused in to rule and in query
-%%
-%% The pattern matching is done by first matching the request method to a rule. by
-%% default all methods match a rule. (method is equal to "*" by default). Then
-%% It will try to match the path to one rule. If no rule match, then a 404 error
-%% is displayed.
-%%
-%% Once a rule is found we rewrite the request url using the "to" and
-%% "query" members. The identified token are matched to the rule and
-%% will replace var. if '*' is found in the rule it will contain the remaining
-%% part if it exists.
-%%
-%% Examples:
-%%
-%% Dispatch rule            URL             TO                  Tokens
-%%
-%% {"from": "/a/b",         /a/b?k=v        /some/b?k=v         var =:= b
-%% "to": "/some/"}                                              k = v
-%%
-%% {"from": "/a/b",         /a/b            /some/b?var=b       var =:= b
-%% "to": "/some/:var"}
-%%
-%% {"from": "/a",           /a              /some
-%% "to": "/some/*"}
-%%
-%% {"from": "/a/*",         /a/b/c          /some/b/c
-%% "to": "/some/*"}
-%%
-%% {"from": "/a",           /a              /some
-%% "to": "/some/*"}
-%%
-%% {"from": "/a/:foo/*",    /a/b/c          /some/b/c?foo=b     foo =:= b
-%% "to": "/some/:foo/*"}
-%%
-%% {"from": "/a/:foo",     /a/b             /some/?k=b&foo=b    foo =:= b
-%% "to": "/some",
-%%  "query": {
-%%      "k": ":foo"
-%%  }}
-%%
-%% {"from": "/a",           /a?foo=b        /some/b             foo =:= b
-%% "to": "/some/:foo",
-%%  }}
-
-
-
-handle_rewrite_req(#httpd{
-        path_parts=[DbName, <<"_design">>, DesignName, _Rewrite|PathParts],
-        method=Method,
-        mochi_req=MochiReq}=Req, _Db, DDoc) ->
-
-    % we are in a design handler
-    DesignId = <<"_design/", DesignName/binary>>,
-    Prefix = <<"/", (?l2b(couch_util:url_encode(DbName)))/binary, "/", DesignId/binary>>,
-    QueryList = lists:map(fun decode_query_value/1, couch_httpd:qs(Req)),
-
-    RewritesSoFar = erlang:get(?REWRITE_COUNT),
-    MaxRewrites = list_to_integer(couch_config:get("httpd", "rewrite_limit", "100")),
-    case RewritesSoFar >= MaxRewrites of
-        true ->
-            throw({bad_request, <<"Exceeded rewrite recursion limit">>});
-        false ->
-            erlang:put(?REWRITE_COUNT, RewritesSoFar + 1)
-    end,
-
-    #doc{body={Props}} = DDoc,
-
-    % get rules from ddoc
-    case couch_util:get_value(<<"rewrites">>, Props) of
-        undefined ->
-            couch_httpd:send_error(Req, 404, <<"rewrite_error">>,
-                <<"Invalid path.">>);
-        Bin when is_binary(Bin) ->
-            couch_httpd:send_error(Req, 400, <<"rewrite_error">>,
-                <<"Rewrite rules are a String. They must be a JSON Array.">>);
-        Rules ->
-            % create dispatch list from rules
-            DispatchList =  [make_rule(Rule) || {Rule} <- Rules],
-            Method1 = couch_util:to_binary(Method),
-
-            % get raw path by matching url to a rule. Throws not_found.
-            {NewPathParts0, Bindings0} =
-                try_bind_path(DispatchList, Method1, PathParts, QueryList),
-            NewPathParts = [quote_plus(X) || X <- NewPathParts0],
-            Bindings = maybe_encode_bindings(Bindings0),
-
-            Path0 = string:join(NewPathParts, [?SEPARATOR]),
-
-            % if path is relative detect it and rewrite path
-            Path1 = case mochiweb_util:safe_relative_path(Path0) of
-                undefined ->
-                    ?b2l(Prefix) ++ "/" ++ Path0;
-                P1 ->
-                    ?b2l(Prefix) ++ "/" ++ P1
-            end,
-
-            Path2 = normalize_path(Path1),
-
-            Path3 = case Bindings of
-                [] ->
-                    Path2;
-                _ ->
-                    [Path2, "?", mochiweb_util:urlencode(Bindings)]
-            end,
-
-            RawPath1 = ?b2l(iolist_to_binary(Path3)),
-
-            % In order to do OAuth correctly, we have to save the
-            % requested path. We use default so chained rewriting
-            % wont replace the original header.
-            Headers = mochiweb_headers:default("x-couchdb-requested-path",
-                                             MochiReq:get(raw_path),
-                                             MochiReq:get(headers)),
-
-            ?LOG_DEBUG("rewrite to ~p ~n", [RawPath1]),
-
-            % build a new mochiweb request
-            MochiReq1 = mochiweb_request:new(MochiReq:get(socket),
-                                             MochiReq:get(method),
-                                             RawPath1,
-                                             MochiReq:get(version),
-                                             Headers),
-
-            % cleanup, It force mochiweb to reparse raw uri.
-            MochiReq1:cleanup(),
-
-            #httpd{
-                db_url_handlers = DbUrlHandlers,
-                design_url_handlers = DesignUrlHandlers,
-                default_fun = DefaultFun,
-                url_handlers = UrlHandlers,
-                user_ctx = UserCtx,
-               auth = Auth
-            } = Req,
-
-            erlang:put(pre_rewrite_auth, Auth),
-            erlang:put(pre_rewrite_user_ctx, UserCtx),
-            couch_httpd:handle_request_int(MochiReq1, DefaultFun,
-                    UrlHandlers, DbUrlHandlers, DesignUrlHandlers)
-        end.
-
-quote_plus({bind, X}) ->
-    mochiweb_util:quote_plus(X);
-quote_plus(X) ->
-    mochiweb_util:quote_plus(X).
-
-%% @doc Try to find a rule matching current url. If none is found
-%% 404 error not_found is raised
-try_bind_path([], _Method, _PathParts, _QueryList) ->
-    throw(not_found);
-try_bind_path([Dispatch|Rest], Method, PathParts, QueryList) ->
-    [{PathParts1, Method1}, RedirectPath, QueryArgs, Formats] = Dispatch,
-    case bind_method(Method1, Method) of
-        true ->
-            case bind_path(PathParts1, PathParts, []) of
-                {ok, Remaining, Bindings} ->
-                    Bindings1 = Bindings ++ QueryList,
-                    % we parse query args from the rule and fill
-                    % it eventually with bindings vars
-                    QueryArgs1 = make_query_list(QueryArgs, Bindings1,
-                        Formats, []),
-                    % remove params in QueryLists1 that are already in
-                    % QueryArgs1
-                    Bindings2 = lists:foldl(fun({K, V}, Acc) ->
-                        K1 = to_binding(K),
-                        KV = case couch_util:get_value(K1, QueryArgs1) of
-                            undefined -> [{K1, V}];
-                            _V1 -> []
-                        end,
-                        Acc ++ KV
-                    end, [], Bindings1),
-
-                    FinalBindings = Bindings2 ++ QueryArgs1,
-                    NewPathParts = make_new_path(RedirectPath, FinalBindings,
-                                    Remaining, []),
-                    {NewPathParts, FinalBindings};
-                fail ->
-                    try_bind_path(Rest, Method, PathParts, QueryList)
-            end;
-        false ->
-            try_bind_path(Rest, Method, PathParts, QueryList)
-    end.
-
-%% rewriting dynamically the quey list given as query member in
-%% rewrites. Each value is replaced by one binding or an argument
-%% passed in url.
-make_query_list([], _Bindings, _Formats, Acc) ->
-    Acc;
-make_query_list([{Key, {Value}}|Rest], Bindings, Formats, Acc) ->
-    Value1 = {Value},
-    make_query_list(Rest, Bindings, Formats, [{to_binding(Key), Value1}|Acc]);
-make_query_list([{Key, Value}|Rest], Bindings, Formats, Acc) when is_binary(Value) ->
-    Value1 = replace_var(Value, Bindings, Formats),
-    make_query_list(Rest, Bindings, Formats, [{to_binding(Key), Value1}|Acc]);
-make_query_list([{Key, Value}|Rest], Bindings, Formats, Acc) when is_list(Value) ->
-    Value1 = replace_var(Value, Bindings, Formats),
-    make_query_list(Rest, Bindings, Formats, [{to_binding(Key), Value1}|Acc]);
-make_query_list([{Key, Value}|Rest], Bindings, Formats, Acc) ->
-    make_query_list(Rest, Bindings, Formats, [{to_binding(Key), Value}|Acc]).
-
-replace_var(<<"*">>=Value, Bindings, Formats) ->
-    get_var(Value, Bindings, Value, Formats);
-replace_var(<<":", Var/binary>> = Value, Bindings, Formats) ->
-    get_var(Var, Bindings, Value, Formats);
-replace_var(Value, _Bindings, _Formats) when is_binary(Value) ->
-    Value;
-replace_var(Value, Bindings, Formats) when is_list(Value) ->
-    lists:reverse(lists:foldl(fun
-                (<<":", Var/binary>>=Value1, Acc) ->
-                    [get_var(Var, Bindings, Value1, Formats)|Acc];
-                (Value1, Acc) ->
-                    [Value1|Acc]
-            end, [], Value));
-replace_var(Value, _Bindings, _Formats) ->
-    Value.
-
-maybe_json(Key, Value) ->
-    case lists:member(Key, [<<"key">>, <<"startkey">>, <<"start_key">>,
-                <<"endkey">>, <<"end_key">>, <<"keys">>]) of
-        true ->
-            ?JSON_ENCODE(Value);
-        false ->
-            Value
-    end.
-
-get_var(VarName, Props, Default, Formats) ->
-    VarName1 = to_binding(VarName),
-    Val = couch_util:get_value(VarName1, Props, Default),
-    maybe_format(VarName, Val, Formats).
-
-maybe_format(VarName, Value, Formats) ->
-    case couch_util:get_value(VarName, Formats) of
-        undefined ->
-             Value;
-        Format ->
-            format(Format, Value)
-    end.
-
-format(<<"int">>, Value) when is_integer(Value) ->
-    Value;
-format(<<"int">>, Value) when is_binary(Value) ->
-    format(<<"int">>, ?b2l(Value));
-format(<<"int">>, Value) when is_list(Value) ->
-    case (catch list_to_integer(Value)) of
-        IntVal when is_integer(IntVal) ->
-            IntVal;
-        _ ->
-            Value
-    end;
-format(<<"bool">>, Value) when is_binary(Value) ->
-    format(<<"bool">>, ?b2l(Value));
-format(<<"bool">>, Value) when is_list(Value) ->
-    case string:to_lower(Value) of
-        "true" -> true;
-        "false" -> false;
-        _ -> Value
-    end;
-format(_Format, Value) ->
-   Value.
-
-%% doc: build new patch from bindings. bindings are query args
-%% (+ dynamic query rewritten if needed) and bindings found in
-%% bind_path step.
-make_new_path([], _Bindings, _Remaining, Acc) ->
-    lists:reverse(Acc);
-make_new_path([?MATCH_ALL], _Bindings, Remaining, Acc) ->
-    Acc1 = lists:reverse(Acc) ++ Remaining,
-    Acc1;
-make_new_path([?MATCH_ALL|_Rest], _Bindings, Remaining, Acc) ->
-    Acc1 = lists:reverse(Acc) ++ Remaining,
-    Acc1;
-make_new_path([{bind, P}|Rest], Bindings, Remaining, Acc) ->
-    P2 = case couch_util:get_value({bind, P}, Bindings) of
-        undefined -> << "undefined">>;
-        P1 ->
-            iolist_to_binary(P1)
-    end,
-    make_new_path(Rest, Bindings, Remaining, [P2|Acc]);
-make_new_path([P|Rest], Bindings, Remaining, Acc) ->
-    make_new_path(Rest, Bindings, Remaining, [P|Acc]).
-
-
-%% @doc If method of the query fith the rule method. If the
-%% method rule is '*', which is the default, all
-%% request method will bind. It allows us to make rules
-%% depending on HTTP method.
-bind_method(?MATCH_ALL, _Method ) ->
-    true;
-bind_method({bind, Method}, Method) ->
-    true;
-bind_method(_, _) ->
-    false.
-
-
-%% @doc bind path. Using the rule from we try to bind variables given
-%% to the current url by pattern matching
-bind_path([], [], Bindings) ->
-    {ok, [], Bindings};
-bind_path([?MATCH_ALL], [Match|_RestMatch]=Rest, Bindings) ->
-    {ok, Rest, [{?MATCH_ALL, Match}|Bindings]};
-bind_path(_, [], _) ->
-    fail;
-bind_path([{bind, Token}|RestToken],[Match|RestMatch],Bindings) ->
-    bind_path(RestToken, RestMatch, [{{bind, Token}, Match}|Bindings]);
-bind_path([Token|RestToken], [Token|RestMatch], Bindings) ->
-    bind_path(RestToken, RestMatch, Bindings);
-bind_path(_, _, _) ->
-    fail.
-
-
-%% normalize path.
-normalize_path(Path)  ->
-    "/" ++ string:join(normalize_path1(string:tokens(Path,
-                "/"), []), [?SEPARATOR]).
-
-
-normalize_path1([], Acc) ->
-    lists:reverse(Acc);
-normalize_path1([".."|Rest], Acc) ->
-    Acc1 = case Acc of
-        [] -> [".."|Acc];
-        [T|_] when T =:= ".." -> [".."|Acc];
-        [_|R] -> R
-    end,
-    normalize_path1(Rest, Acc1);
-normalize_path1(["."|Rest], Acc) ->
-    normalize_path1(Rest, Acc);
-normalize_path1([Path|Rest], Acc) ->
-    normalize_path1(Rest, [Path|Acc]).
-
-
-%% @doc transform json rule in erlang for pattern matching
-make_rule(Rule) ->
-    Method = case couch_util:get_value(<<"method">>, Rule) of
-        undefined -> ?MATCH_ALL;
-        M -> to_binding(M)
-    end,
-    QueryArgs = case couch_util:get_value(<<"query">>, Rule) of
-        undefined -> [];
-        {Args} -> Args
-        end,
-    FromParts  = case couch_util:get_value(<<"from">>, Rule) of
-        undefined -> [?MATCH_ALL];
-        From ->
-            parse_path(From)
-        end,
-    ToParts  = case couch_util:get_value(<<"to">>, Rule) of
-        undefined ->
-            throw({error, invalid_rewrite_target});
-        To ->
-            parse_path(To)
-        end,
-    Formats = case couch_util:get_value(<<"formats">>, Rule) of
-        undefined -> [];
-        {Fmts} -> Fmts
-    end,
-    [{FromParts, Method}, ToParts, QueryArgs, Formats].
-
-parse_path(Path) ->
-    {ok, SlashRE} = re:compile(<<"\\/">>),
-    path_to_list(re:split(Path, SlashRE), [], 0).
-
-%% @doc convert a path rule (from or to) to an erlang list
-%% * and path variable starting by ":" are converted
-%% in erlang atom.
-path_to_list([], Acc, _DotDotCount) ->
-    lists:reverse(Acc);
-path_to_list([<<>>|R], Acc, DotDotCount) ->
-    path_to_list(R, Acc, DotDotCount);
-path_to_list([<<"*">>|R], Acc, DotDotCount) ->
-    path_to_list(R, [?MATCH_ALL|Acc], DotDotCount);
-path_to_list([<<"..">>|R], Acc, DotDotCount) when DotDotCount == 2 ->
-    case couch_config:get("httpd", "secure_rewrites", "true") of
-    "false" ->
-        path_to_list(R, [<<"..">>|Acc], DotDotCount+1);
-    _Else ->
-        ?LOG_INFO("insecure_rewrite_rule ~p blocked", [lists:reverse(Acc) ++ [<<"..">>] ++ R]),
-        throw({insecure_rewrite_rule, "too many ../.. segments"})
-    end;
-path_to_list([<<"..">>|R], Acc, DotDotCount) ->
-    path_to_list(R, [<<"..">>|Acc], DotDotCount+1);
-path_to_list([P|R], Acc, DotDotCount) ->
-    P1 = case P of
-        <<":", Var/binary>> ->
-            to_binding(Var);
-        _ -> P
-    end,
-    path_to_list(R, [P1|Acc], DotDotCount).
-
-maybe_encode_bindings([]) ->
-    [];
-maybe_encode_bindings(Props) ->
-    lists:foldl(fun
-            ({{bind, <<"*">>}, _V}, Acc) ->
-                Acc;
-            ({{bind, K}, V}, Acc) ->
-                V1 = iolist_to_binary(maybe_json(K, V)),
-                [{K, V1}|Acc]
-        end, [], Props).
-
-decode_query_value({K,V}) ->
-    case lists:member(K, ["key", "startkey", "start_key",
-                "endkey", "end_key", "keys"]) of
-        true ->
-            {to_binding(K), ?JSON_DECODE(V)};
-        false ->
-            {to_binding(K), ?l2b(V)}
-    end.
-
-to_binding({bind, V}) ->
-    {bind, V};
-to_binding(V) when is_list(V) ->
-    to_binding(?l2b(V));
-to_binding(V) ->
-    {bind, V}.

http://git-wip-us.apache.org/repos/asf/couchdb/blob/c0855434/apps/couch_httpd/src/couch_httpd_stats_handlers.erl
----------------------------------------------------------------------
diff --git a/apps/couch_httpd/src/couch_httpd_stats_handlers.erl b/apps/couch_httpd/src/couch_httpd_stats_handlers.erl
deleted file mode 100644
index cd357ea..0000000
--- a/apps/couch_httpd/src/couch_httpd_stats_handlers.erl
+++ /dev/null
@@ -1,56 +0,0 @@
-% Licensed under the Apache License, Version 2.0 (the "License"); you may not
-% use this file except in compliance with the License. You may obtain a copy of
-% the License at
-%
-%   http://www.apache.org/licenses/LICENSE-2.0
-%
-% Unless required by applicable law or agreed to in writing, software
-% distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-% WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-% License for the specific language governing permissions and limitations under
-% the License.
-
--module(couch_httpd_stats_handlers).
--include_lib("couch/include/couch_db.hrl").
-
--export([handle_stats_req/1]).
--import(couch_httpd, [
-    send_json/2, send_json/3, send_json/4, send_method_not_allowed/2,
-    start_json_response/2, send_chunk/2, end_json_response/1,
-    start_chunked_response/3, send_error/4
-]).
-
-handle_stats_req(#httpd{method='GET', path_parts=[_]}=Req) ->
-    flush(Req),
-    send_json(Req, couch_stats_aggregator:all(range(Req)));
-
-handle_stats_req(#httpd{method='GET', path_parts=[_, _Mod]}) ->
-    throw({bad_request, <<"Stat names must have exactly two parts.">>});
-
-handle_stats_req(#httpd{method='GET', path_parts=[_, Mod, Key]}=Req) ->
-    flush(Req),
-    Stats = couch_stats_aggregator:get_json({list_to_atom(binary_to_list(Mod)),
-        list_to_atom(binary_to_list(Key))}, range(Req)),
-    send_json(Req, {[{Mod, {[{Key, Stats}]}}]});
-
-handle_stats_req(#httpd{method='GET', path_parts=[_, _Mod, _Key | _Extra]}) ->
-    throw({bad_request, <<"Stat names must have exactly two parts.">>});
-
-handle_stats_req(Req) ->
-    send_method_not_allowed(Req, "GET").
-
-range(Req) ->
-    case couch_util:get_value("range", couch_httpd:qs(Req)) of
-        undefined ->
-            0;
-        Value ->
-            list_to_integer(Value)
-    end.
-
-flush(Req) ->
-    case couch_util:get_value("flush", couch_httpd:qs(Req)) of
-        "true" ->
-            couch_stats_aggregator:collect_sample();
-        _Else ->
-            ok
-    end.

http://git-wip-us.apache.org/repos/asf/couchdb/blob/c0855434/apps/couch_httpd/src/couch_httpd_sup.erl
----------------------------------------------------------------------
diff --git a/apps/couch_httpd/src/couch_httpd_sup.erl b/apps/couch_httpd/src/couch_httpd_sup.erl
deleted file mode 100644
index 1ce53f1..0000000
--- a/apps/couch_httpd/src/couch_httpd_sup.erl
+++ /dev/null
@@ -1,112 +0,0 @@
-% Licensed under the Apache License, Version 2.0 (the "License"); you may not
-% use this file except in compliance with the License. You may obtain a copy of
-% the License at
-%
-%   http://www.apache.org/licenses/LICENSE-2.0
-%
-% Unless required by applicable law or agreed to in writing, software
-% distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-% WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-% License for the specific language governing permissions and limitations under
-% the License.
-%
--module(couch_httpd_sup).
--behaviour(supervisor).
-
--export([start_link/0]).
--export([upgrade/0]).
--export([reload_listener/1,
-         reload_listeners/0]).
-
-
-%% internal API
--export([init/1]).
--export([config_change/2]).
-
-
--spec start_link() -> ignore | {error, term()} | {ok, pid()}.
-start_link() ->
-    {ok, Pid} = supervisor:start_link({local, ?MODULE}, ?MODULE, []),
-
-    %% register to config events
-    ok  = couch_config:register(fun ?MODULE:config_change/2, Pid),
-
-    %% display uris
-    couch_httpd_util:display_uris(),
-
-    %% write_uris
-    couch_httpd_util:write_uri_file(),
-
-    {ok, Pid}.
-
-
-%% @spec upgrade() -> ok
-%% @doc Add processes if necessary.
-upgrade() ->
-    {ok, {_, Specs}} = init([]),
-
-    Old = sets:from_list(
-            [Name || {Name, _, _, _} <- supervisor:which_children(?MODULE)]),
-    New = sets:from_list([Name || {Name, _, _, _, _, _} <- Specs]),
-    Kill = sets:subtract(Old, New),
-
-    sets:fold(fun (Id, ok) ->
-                      supervisor:terminate_child(?MODULE, Id),
-                      supervisor:delete_child(?MODULE, Id),
-                      ok
-              end, ok, Kill),
-
-    [supervisor:start_child(?MODULE, Spec) || Spec <- Specs],
-    ok.
-
-%% @doc upgrade  a listener
--spec reload_listener(atom()) -> {ok, pid()} | {error, term()}.
-reload_listener(Id) ->
-    %% stop the listener and remove it from the supervision temporarely
-    supervisor:terminate_child(?MODULE, Id),
-    supervisor:delete_child(?MODULE, Id),
-
-    %% restart the listener
-    supervisor:start_child(?MODULE, listener_spec(Id)),
-    couch_httpd_util:display_uris([Id]),
-    ok.
-
-%% upgrade all listeners
--spec reload_listeners() -> ok.
-reload_listeners() ->
-    [reload_listener(Id) || Id <- couch_httpd_util:get_listeners()],
-    ok.
-
-
--spec init([]) -> {ok, {{one_for_one, 5, 10}, [supervisor:child_spec()]}}.
-init([]) ->
-    Listeners = [listener_spec(Id) || Id <- couch_httpd_util:get_listeners()],
-    Vhost = {couch_httpd_vhost,
-             {couch_httpd_vhost, start_link, []},
-             permanent, brutal_kill, worker, [couch_httpd_vhost]},
-    {ok, {{one_for_one, 9, 10}, Listeners ++ [Vhost]}}.
-
-
-listener_spec(Id) ->
-    {Id,
-     {couch_httpd, start_link, [Id]},
-     permanent, brutal_kill, worker, [couch_httpd]}.
-
-config_change("httpd", "bind_address") ->
-    ?MODULE:reload_listeners();
-config_change("httpd", "port") ->
-    ?MODULE:reload_listener(couch_http);
-config_change("httpd", "default_handler") ->
-    ?MODULE:reload_listeners();
-config_change("httpd", "server_options") ->
-    ?MODULE:reload_listeners();
-config_change("httpd", "socket_options") ->
-    ?MODULE:reload_listeners();
-config_change("httpd", "authentication_handlers") ->
-    couch_httpd:set_auth_handlers();
-config_change("httpd_global_handlers", _) ->
-    ?MODULE:reload_listeners();
-config_change("httpd_db_handlers", _) ->
-    ?MODULE:reload_listeners();
-config_change("ssl", _) ->
-    ?MODULE:reload_listener(couch_https).

http://git-wip-us.apache.org/repos/asf/couchdb/blob/c0855434/apps/couch_httpd/src/couch_httpd_util.erl
----------------------------------------------------------------------
diff --git a/apps/couch_httpd/src/couch_httpd_util.erl b/apps/couch_httpd/src/couch_httpd_util.erl
deleted file mode 100644
index a3ba075..0000000
--- a/apps/couch_httpd/src/couch_httpd_util.erl
+++ /dev/null
@@ -1,80 +0,0 @@
-% Licensed under the Apache License, Version 2.0 (the "License"); you may not
-% use this file except in compliance with the License. You may obtain a copy of
-% the License at
-%
-%   http://www.apache.org/licenses/LICENSE-2.0
-%
-% Unless required by applicable law or agreed to in writing, software
-% distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-% WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-% License for the specific language governing permissions and limitations under
-% the License.
-%
--module(couch_httpd_util).
-
--export([display_uris/0, display_uris/1,
-         write_uri_file/0,
-         get_listeners/0,
-         get_uri/2,
-         get_scheme/1,
-         get_port/1]).
-
--include_lib("couch/include/couch_db.hrl").
-
-
-display_uris() ->
-    display_uris(get_listeners()).
-
-display_uris(Bindings) ->
-    Ip = couch_config:get("httpd", "bind_address"),
-    lists:foreach(fun(Binding) ->
-                Uri = get_uri(Binding, Ip),
-                ?LOG_INFO("HTTP API started on ~p~n", [Uri])
-        end, Bindings).
-
-write_uri_file() ->
-    Ip = couch_config:get("httpd", "bind_address"),
-    Listeners = get_listeners(),
-    Uris = [get_uri(Name, Ip) || Name <- Listeners],
-    case couch_config:get("couchdb", "uri_file", null) of
-        null -> ok;
-        UriFile ->
-            Lines = [begin case Uri of
-                            undefined -> [];
-                            Uri -> io_lib:format("~s~n", [Uri])
-                        end end || Uri <- Uris],
-            case file:write_file(UriFile, Lines) of
-                ok -> ok;
-                {error, eacces} ->
-                    ?LOG_INFO("Permission error when writing to URI file ~s",
-                              [UriFile]),
-                    throw({file_permission_error, UriFile});
-                Error2 ->
-                    ?LOG_INFO("Failed to write to URI file ~s: ~p~n",
-                              [UriFile, Error2]),
-                    throw(Error2)
-            end
-    end.
-
-get_listeners() ->
-    SchemeStr = couch_config:get("httpd", "scheme", "http"),
-    SchemeList = re:split(SchemeStr, "\\s*,\\s*",[{return, list}]),
-
-    lists:foldl(fun(S, Acc) ->
-                [list_to_atom("couch_" ++ S) | Acc]
-        end, [], lists:reverse(SchemeList)).
-
-get_uri(Name, Ip) ->
-    Port = get_port(Name),
-    Scheme = get_scheme(Name),
-    Scheme ++ "://" ++ Ip ++ ":" ++ integer_to_list(Port) ++ "/".
-
-get_scheme(couch_http) -> "http";
-get_scheme(couch_https) -> "https".
-
-get_port(Ref) ->
-    try
-        mochiweb_socket_server:get(Ref, port)
-    catch
-        exit:{noproc, _} -> undefined
-    end.

http://git-wip-us.apache.org/repos/asf/couchdb/blob/c0855434/apps/couch_httpd/src/couch_httpd_vhost.erl
----------------------------------------------------------------------
diff --git a/apps/couch_httpd/src/couch_httpd_vhost.erl b/apps/couch_httpd/src/couch_httpd_vhost.erl
deleted file mode 100644
index 258f4eb..0000000
--- a/apps/couch_httpd/src/couch_httpd_vhost.erl
+++ /dev/null
@@ -1,383 +0,0 @@
-% Licensed under the Apache License, Version 2.0 (the "License"); you may not
-% use this file except in compliance with the License. You may obtain a copy of
-% the License at
-%
-%   http://www.apache.org/licenses/LICENSE-2.0
-%
-% Unless required by applicable law or agreed to in writing, software
-% distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
-% WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
-% License for the specific language governing permissions and limitations under
-% the License.
-
--module(couch_httpd_vhost).
--behaviour(gen_server).
-
--export([start_link/0, config_change/2, reload/0, get_state/0, dispatch_host/1]).
--export([urlsplit_netloc/2, redirect_to_vhost/2]).
--export([host/1, split_host_port/1]).
-
--export([init/1, handle_call/3, handle_cast/2, handle_info/2, terminate/2, code_change/3]).
-
--include_lib("couch/include/couch_db.hrl").
-
--define(SEPARATOR, $\/).
--define(MATCH_ALL, {bind, '*'}).
-
--record(vhosts_state, {
-        vhosts,
-        vhost_globals,
-        vhosts_fun}).
-
-%% doc the vhost manager.
-%% This gen_server keep state of vhosts added to the ini and try to
-%% match the Host header (or forwarded) against rules built against
-%% vhost list.
-%%
-%% Declaration of vhosts take place in the configuration file :
-%%
-%% [vhosts]
-%% example.com = /example
-%% *.example.com = /example
-%%
-%% The first line will rewrite the rquest to display the content of the
-%% example database. This rule works only if the Host header is
-%% 'example.com' and won't work for CNAMEs. Second rule on the other hand
-%% match all CNAMES to example db. So www.example.com or db.example.com
-%% will work.
-%%
-%% The wildcard ('*') should always be the last in the cnames:
-%%
-%%      "*.db.example.com = /"  will match all cname on top of db
-%% examples to the root of the machine.
-%%
-%%
-%% Rewriting Hosts to path
-%% -----------------------
-%%
-%% Like in the _rewrite handler you could match some variable and use
-%them to create the target path. Some examples:
-%%
-%%    [vhosts]
-%%    *.example.com = /*
-%%    :dbname.example.com = /:dbname
-%%    :ddocname.:dbname.example.com = /:dbname/_design/:ddocname/_rewrite
-%%
-%% First rule pass wildcard as dbname, second do the same but use a
-%% variable name and the third one allows you to use any app with
-%% @ddocname in any db with @dbname .
-%%
-%% You could also change the default function to handle request by
-%% changing the setting `redirect_vhost_handler` in `httpd` section of
-%% the Ini:
-%%
-%%    [httpd]
-%%    redirect_vhost_handler = {Module, Fun}
-%%
-%% The function take 2 args : the mochiweb request object and the target
-%%% path.
-
-start_link() ->
-    gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).
-
-%% @doc reload vhosts rules
-reload() ->
-    gen_server:call(?MODULE, reload).
-
-get_state() ->
-    gen_server:call(?MODULE, get_state).
-
-%% @doc Try to find a rule matching current Host heade. some rule is
-%% found it rewrite the Mochiweb Request else it return current Request.
-dispatch_host(MochiReq) ->
-    #vhosts_state{
-        vhost_globals = VHostGlobals,
-        vhosts = VHosts,
-        vhosts_fun=Fun} = get_state(),
-
-    {"/" ++ VPath, Query, Fragment} = mochiweb_util:urlsplit_path(MochiReq:get(raw_path)),
-    VPathParts =  string:tokens(VPath, "/"),
-
-    VHost = host(MochiReq),
-    {VHostParts, VhostPort} = split_host_port(VHost),
-    FinalMochiReq = case try_bind_vhost(VHosts, lists:reverse(VHostParts),
-            VhostPort, VPathParts) of
-        no_vhost_matched -> MochiReq;
-        {VhostTarget, NewPath} ->
-            case vhost_global(VHostGlobals, MochiReq) of
-                true ->
-                    MochiReq;
-                _Else ->
-                    NewPath1 = mochiweb_util:urlunsplit_path({NewPath, Query,
-                                          Fragment}),
-                    MochiReq1 = mochiweb_request:new(MochiReq:get(socket),
-                                      MochiReq:get(method),
-                                      NewPath1,
-                                      MochiReq:get(version),
-                                      MochiReq:get(headers)),
-                    Fun(MochiReq1, VhostTarget)
-            end
-    end,
-    FinalMochiReq.
-
-append_path("/"=_Target, "/"=_Path) ->
-    "/";
-append_path(Target, Path) ->
-    Target ++ Path.
-
-% default redirect vhost handler
-redirect_to_vhost(MochiReq, VhostTarget) ->
-    Path = MochiReq:get(raw_path),
-    Target = append_path(VhostTarget, Path),
-
-    ?LOG_DEBUG("Vhost Target: '~p'~n", [Target]),
-
-    Headers = mochiweb_headers:enter("x-couchdb-vhost-path", Path,
-        MochiReq:get(headers)),
-
-    % build a new mochiweb request
-    MochiReq1 = mochiweb_request:new(MochiReq:get(socket),
-                                      MochiReq:get(method),
-                                      Target,
-                                      MochiReq:get(version),
-                                      Headers),
-    % cleanup, It force mochiweb to reparse raw uri.
-    MochiReq1:cleanup(),
-    MochiReq1.
-
-%% if so, then it will not be rewritten, but will run as a normal couchdb request.
-%* normally you'd use this for _uuids _utils and a few of the others you want to
-%% keep available on vhosts. You can also use it to make databases 'global'.
-vhost_global( VhostGlobals, MochiReq) ->
-    RawUri = MochiReq:get(raw_path),
-    {"/" ++ Path, _, _} = mochiweb_util:urlsplit_path(RawUri),
-
-    Front = case couch_httpd:partition(Path) of
-    {"", "", ""} ->
-        "/"; % Special case the root url handler
-    {FirstPart, _, _} ->
-        FirstPart
-    end,
-    [true] == [true||V <- VhostGlobals, V == Front].
-
-%% bind host
-%% first it try to bind the port then the hostname.
-try_bind_vhost([], _HostParts, _Port, _PathParts) ->
-    no_vhost_matched;
-try_bind_vhost([VhostSpec|Rest], HostParts, Port, PathParts) ->
-    {{VHostParts, VPort, VPath}, Path} = VhostSpec,
-    case bind_port(VPort, Port) of
-        ok ->
-            case bind_vhost(lists:reverse(VHostParts), HostParts, []) of
-                {ok, Bindings, Remainings} ->
-                    case bind_path(VPath, PathParts) of
-                        {ok, PathParts1} ->
-                            Path1 = make_target(Path, Bindings, Remainings, []),
-                            {make_path(Path1), make_path(PathParts1)};
-                        fail ->
-                            try_bind_vhost(Rest, HostParts, Port,
-                                PathParts)
-                    end;
-                fail -> try_bind_vhost(Rest, HostParts, Port, PathParts)
-            end;
-        fail ->  try_bind_vhost(Rest, HostParts, Port, PathParts)
-    end.
-
-%% doc: build new patch from bindings. bindings are query args
-%% (+ dynamic query rewritten if needed) and bindings found in
-%% bind_path step.
-%% TODO: merge code with rewrite. But we need to make sure we are
-%% in string here.
-make_target([], _Bindings, _Remaining, Acc) ->
-    lists:reverse(Acc);
-make_target([?MATCH_ALL], _Bindings, Remaining, Acc) ->
-    Acc1 = lists:reverse(Acc) ++ Remaining,
-    Acc1;
-make_target([?MATCH_ALL|_Rest], _Bindings, Remaining, Acc) ->
-    Acc1 = lists:reverse(Acc) ++ Remaining,
-    Acc1;
-make_target([{bind, P}|Rest], Bindings, Remaining, Acc) ->
-    P2 = case couch_util:get_value({bind, P}, Bindings) of
-        undefined ->  "undefined";
-        P1 -> P1
-    end,
-    make_target(Rest, Bindings, Remaining, [P2|Acc]);
-make_target([P|Rest], Bindings, Remaining, Acc) ->
-    make_target(Rest, Bindings, Remaining, [P|Acc]).
-
-%% bind port
-bind_port(Port, Port) -> ok;
-bind_port('*', _) -> ok;
-bind_port(_,_) -> fail.
-
-%% bind bhost
-bind_vhost([],[], Bindings) -> {ok, Bindings, []};
-bind_vhost([?MATCH_ALL], [], _Bindings) -> fail;
-bind_vhost([?MATCH_ALL], Rest, Bindings) -> {ok, Bindings, Rest};
-bind_vhost([], _HostParts, _Bindings) -> fail;
-bind_vhost([{bind, Token}|Rest], [Match|RestHost], Bindings) ->
-    bind_vhost(Rest, RestHost, [{{bind, Token}, Match}|Bindings]);
-bind_vhost([Cname|Rest], [Cname|RestHost], Bindings) ->
-    bind_vhost(Rest, RestHost, Bindings);
-bind_vhost(_, _, _) -> fail.
-
-%% bind path
-bind_path([], PathParts) ->
-    {ok, PathParts};
-bind_path(_VPathParts, []) ->
-    fail;
-bind_path([Path|VRest],[Path|Rest]) ->
-   bind_path(VRest, Rest);
-bind_path(_, _) ->
-    fail.
-
-% utilities
-
-
-%% create vhost list from ini
-
-host(MochiReq) ->
-    XHost = couch_config:get("httpd", "x_forwarded_host",
-                             "X-Forwarded-Host"),
-    case MochiReq:get_header_value(XHost) of
-        undefined ->
-            case MochiReq:get_header_value("Host") of
-                undefined -> [];
-                Value1 -> Value1
-            end;
-        Value -> Value
-    end.
-
-make_vhosts() ->
-    Vhosts = lists:foldl(fun
-                ({_, ""}, Acc) ->
-                    Acc;
-                ({Vhost, Path}, Acc) ->
-                    [{parse_vhost(Vhost), split_path(Path)}|Acc]
-            end, [], couch_config:get("vhosts")),
-
-    lists:reverse(lists:usort(Vhosts)).
-
-
-parse_vhost(Vhost) ->
-    case urlsplit_netloc(Vhost, []) of
-        {[], Path} ->
-            {make_spec("*", []), '*', Path};
-        {HostPort, []} ->
-            {H, P} = split_host_port(HostPort),
-            H1 = make_spec(H, []),
-            {H1, P, []};
-        {HostPort, Path} ->
-            {H, P} = split_host_port(HostPort),
-            H1 = make_spec(H, []),
-            {H1, P, string:tokens(Path, "/")}
-    end.
-
-
-split_host_port(HostAsString) ->
-    case string:rchr(HostAsString, $:) of
-        0 ->
-            {split_host(HostAsString), '*'};
-        N ->
-            HostPart = string:substr(HostAsString, 1, N-1),
-            case (catch erlang:list_to_integer(string:substr(HostAsString,
-                            N+1, length(HostAsString)))) of
-                {'EXIT', _} ->
-                    {split_host(HostAsString), '*'};
-                Port ->
-                    {split_host(HostPart), Port}
-            end
-    end.
-
-split_host(HostAsString) ->
-    string:tokens(HostAsString, "\.").
-
-split_path(Path) ->
-    make_spec(string:tokens(Path, "/"), []).
-
-
-make_spec([], Acc) ->
-    lists:reverse(Acc);
-make_spec([""|R], Acc) ->
-    make_spec(R, Acc);
-make_spec(["*"|R], Acc) ->
-    make_spec(R, [?MATCH_ALL|Acc]);
-make_spec([P|R], Acc) ->
-    P1 = parse_var(P),
-    make_spec(R, [P1|Acc]).
-
-
-parse_var(P) ->
-    case P of
-        ":" ++ Var ->
-            {bind, Var};
-        _ -> P
-    end.
-
-
-% mochiweb doesn't export it.
-urlsplit_netloc("", Acc) ->
-    {lists:reverse(Acc), ""};
-urlsplit_netloc(Rest=[C | _], Acc) when C =:= $/; C =:= $?; C =:= $# ->
-    {lists:reverse(Acc), Rest};
-urlsplit_netloc([C | Rest], Acc) ->
-    urlsplit_netloc(Rest, [C | Acc]).
-
-make_path(Parts) ->
-     "/" ++ string:join(Parts,[?SEPARATOR]).
-
-init(_) ->
-    ok = couch_config:register(fun ?MODULE:config_change/2),
-
-    %% load configuration
-    {VHostGlobals, VHosts, Fun} = load_conf(),
-    State = #vhosts_state{
-        vhost_globals=VHostGlobals,
-        vhosts=VHosts,
-        vhosts_fun=Fun},
-    {ok, State}.
-
-handle_call(reload, _From, _State) ->
-    {VHostGlobals, VHosts, Fun} = load_conf(),
-    {reply, ok, #vhosts_state{
-            vhost_globals=VHostGlobals,
-            vhosts=VHosts,
-            vhosts_fun=Fun}};
-handle_call(get_state, _From, State) ->
-    {reply, State, State};
-handle_call(_Msg, _From, State) ->
-    {noreply, State}.
-
-handle_cast(_Msg, State) ->
-    {noreply, State}.
-
-handle_info(_Info, State) ->
-    {noreply, State}.
-
-terminate(_Reason, _State) ->
-    ok.
-
-code_change(_OldVsn, State, _Extra) ->
-    {ok, State}.
-
-config_change("httpd", "vhost_global_handlers") ->
-    ?MODULE:reload();
-config_change("httpd", "redirect_vhost_handler") ->
-    ?MODULE:reload();
-config_change("vhosts", _) ->
-    ?MODULE:reload().
-
-load_conf() ->
-    %% get vhost globals
-    VHostGlobals = re:split(couch_config:get("httpd",
-            "vhost_global_handlers",""), "\\s*,\\s*",[{return, list}]),
-
-    %% build vhosts matching rules
-    VHosts = make_vhosts(),
-
-    %% build vhosts handler fun
-    DefaultVHostFun = "{couch_httpd_vhost, redirect_to_vhost}",
-    Fun = couch_httpd:make_arity_2_fun(couch_config:get("httpd",
-            "redirect_vhost_handler", DefaultVHostFun)),
-
-    {VHostGlobals, VHosts, Fun}.