couchdb-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From Adam Kocoloski <kocol...@apache.org>
Subject Re: git commit: handle CORS. fix #COUCHDB-431
Date Thu, 01 Nov 2012 05:33:03 GMT
Benoit, sorry to keep nagging, but one of the specific conclusions of the thread on branch naming that you started earlier today was that the branch name should indicate whether it's a *feature* or a *bugfix*.  In this case the syntax would be 431-feature-CORS.

Adam

On Nov 1, 2012, at 1:30 AM, Benoit Chesneau <bchesneau@gmail.com> wrote:

> The branch have been renamed to 431_cors. I'm not sure it will be renamed
> on github too.
> 
> - benoit
> 
> 
> On Thu, Nov 1, 2012 at 5:34 AM, Paul Davis <paul.joseph.davis@gmail.com>wrote:
> 
>> On Wed, Oct 31, 2012 at 8:14 PM, Adam Kocoloski <kocolosk@apache.org>
>> wrote:
>>> Right, the wiki page for this stuff is
>> http://wiki.apache.org/couchdb/Merge_Procedure which now reads
>>> 
>>>> Please use the ticket number, the type of the branch, along with a very
>> short descriptive phrase, for your branch name.
>>>> 
>>>> If the ticket was COUCHDB-1234, and the ticket title was My Cool
>> Feature, your branch should be called 1234-feature-cool. If the issue is a
>> bug and the branch includes the bug fix, it should be called 1234-fix-cool.
>>> 
>>> Perhaps we should kill this branch and re-upload to follow the naming
>> scheme?  Cheers,
>>> 
>> 
>> I think there's a git syntax for renaming on a remote.
>> 
>>> Adam
>>> 
>>> On Oct 31, 2012, at 7:53 PM, Benoit Chesneau <bchesneau@gmail.com>
>> wrote:
>>> 
>>>> hrmmmm i thought it was ticketnumber_shortdescr.... I didn't read last
>>>> update of the wiki though ..
>>>> 
>>>> - benoƮt
>>>> 
>>>> 
>>>> On Thu, Nov 1, 2012 at 12:50 AM, Adam Kocoloski <kocolosk@apache.org>
>> wrote:
>>>> 
>>>>> A minor thing -- didn't we just propose earlier today to use a naming
>>>>> convention like 431-feature-CORS for these topic branches?
>>>>> 
>>>>> Adam
>>>>> 
>>>>> On Oct 31, 2012, at 7:43 PM, benoitc@apache.org wrote:
>>>>> 
>>>>>> Updated Branches:
>>>>>> refs/heads/COUCHDB-431_cors [created] 0777262fa
>>>>>> 
>>>>>> 
>>>>>> handle CORS. fix #COUCHDB-431
>>>>>> 
>>>>>> This patch as support of CORS requests and preflights request as a
>> node
>>>>>> level. vhosts are supported
>>>>>> 
>>>>>> 
>>>>>> Project: http://git-wip-us.apache.org/repos/asf/couchdb/repo
>>>>>> Commit:
>> http://git-wip-us.apache.org/repos/asf/couchdb/commit/0777262f
>>>>>> Tree: http://git-wip-us.apache.org/repos/asf/couchdb/tree/0777262f
>>>>>> Diff: http://git-wip-us.apache.org/repos/asf/couchdb/diff/0777262f
>>>>>> 
>>>>>> Branch: refs/heads/COUCHDB-431_cors
>>>>>> Commit: 0777262fa291a79555ea23f2ff203d1ae7654547
>>>>>> Parents: 88c52b2
>>>>>> Author: benoitc <bchesneau@gmail.com>
>>>>>> Authored: Thu Nov 1 00:41:00 2012 +0100
>>>>>> Committer: benoitc <bchesneau@gmail.com>
>>>>>> Committed: Thu Nov 1 00:41:00 2012 +0100
>>>>>> 
>>>>>> ----------------------------------------------------------------------
>>>>>> etc/couchdb/default.ini.tpl.in    |   23 +++-
>>>>>> src/couchdb/Makefile.am           |    4 +-
>>>>>> src/couchdb/couch_httpd.erl       |   53 ++++++--
>>>>>> src/couchdb/couch_httpd_cors.erl  |  230
>> ++++++++++++++++++++++++++++++++
>>>>>> src/couchdb/couch_httpd_vhost.erl |   55 ++++----
>>>>>> test/etap/231_cors.t              |  230
>> ++++++++++++++++++++++++++++++++
>>>>>> 6 files changed, 553 insertions(+), 42 deletions(-)
>>>>>> ----------------------------------------------------------------------
>>>>>> 
>>>>>> 
>>>>>> 
>>>>> 
>> http://git-wip-us.apache.org/repos/asf/couchdb/blob/0777262f/etc/couchdb/default.ini.tpl.in
>>>>>> ----------------------------------------------------------------------
>>>>>> diff --git a/etc/couchdb/default.ini.tpl.in b/etc/couchdb/
>>>>> default.ini.tpl.in
>>>>>> index 79ece5c..6a32f65 100644
>>>>>> --- a/etc/couchdb/default.ini.tpl.in
>>>>>> +++ b/etc/couchdb/default.ini.tpl.in
>>>>>> @@ -49,6 +49,7 @@ allow_jsonp = false
>>>>>> ; For more socket options, consult Erlang's module 'inet' man page.
>>>>>> ;socket_options = [{recbuf, 262144}, {sndbuf, 262144}, {nodelay,
>> true}]
>>>>>> log_max_chunk_size = 1000000
>>>>>> +cors_enable = false
>>>>>> 
>>>>>> [ssl]
>>>>>> port = 6984
>>>>>> @@ -67,6 +68,26 @@ auth_cache_size = 50 ; size is number of cache
>> entries
>>>>>> allow_persistent_cookies = false ; set to true to allow persistent
>>>>> cookies
>>>>>> iterations = 10000 ; iterations for password hashing
>>>>>> 
>>>>>> +[cors]
>>>>>> +allows_credentials = false
>>>>>> +; List of origins separated by a comma
>>>>>> +;origins =
>>>>>> +; List of accepted headers separated by a comma
>>>>>> +; headers =
>>>>>> +; List of accepted methods
>>>>>> +; methods =
>>>>>> +
>>>>>> +
>>>>>> +; Configuration for a vhost
>>>>>> +:[cors:example.com]
>>>>>> +; allows_credentials = false
>>>>>> +; List of origins separated by a comma
>>>>>> +;origins =
>>>>>> +; List of accepted headers separated by a comma
>>>>>> +; headers =
>>>>>> +; List of accepted methods
>>>>>> +; methods =
>>>>>> +
>>>>>> [couch_httpd_oauth]
>>>>>> ; If set to 'true', oauth token and consumer secrets will be looked up
>>>>>> ; in the authentication database (_users). These secrets are stored in
>>>>>> @@ -224,7 +245,7 @@ socket_options = [{keepalive, true}, {nodelay,
>>>>> false}]
>>>>>> ;cert_file = /full/path/to/server_cert.pem
>>>>>> ; Path to file containing user's private PEM encoded key.
>>>>>> ;key_file = /full/path/to/server_key.pem
>>>>>> -; String containing the user's password. Only used if the private
>>>>> keyfile is password protected.
>>>>>> +; String containing the user's password. Only used if the private
>>>>> keyfile is password protected.
>>>>>> ;password = somepassword
>>>>>> ; Set to true to validate peer certificates.
>>>>>> verify_ssl_certificates = false
>>>>>> 
>>>>>> 
>>>>> 
>> http://git-wip-us.apache.org/repos/asf/couchdb/blob/0777262f/src/couchdb/Makefile.am
>>>>>> ----------------------------------------------------------------------
>>>>>> diff --git a/src/couchdb/Makefile.am b/src/couchdb/Makefile.am
>>>>>> index 5705976..9fe19bc 100644
>>>>>> --- a/src/couchdb/Makefile.am
>>>>>> +++ b/src/couchdb/Makefile.am
>>>>>> @@ -49,6 +49,7 @@ source_files = \
>>>>>>   couch_httpd.erl \
>>>>>>   couch_httpd_db.erl \
>>>>>>   couch_httpd_auth.erl \
>>>>>> +    couch_httpd_cors.erl \
>>>>>>   couch_httpd_oauth.erl \
>>>>>>   couch_httpd_external.erl \
>>>>>>   couch_httpd_misc_handlers.erl \
>>>>>> @@ -79,7 +80,7 @@ source_files = \
>>>>>>   couch_work_queue.erl \
>>>>>>   json_stream_parse.erl
>>>>>> 
>>>>>> -EXTRA_DIST = $(source_files) couch_db.hrl couch_js_functions.hrl
>>>>>> +EXTRA_DIST = $(source_files) couch_db.hrl couch_js_functions.hrl
>>>>>> 
>>>>>> compiled_files = \
>>>>>>   couch.app \
>>>>>> @@ -106,6 +107,7 @@ compiled_files = \
>>>>>>   couch_httpd_db.beam \
>>>>>>   couch_httpd_auth.beam \
>>>>>>   couch_httpd_oauth.beam \
>>>>>> +    couch_httpd_cors.beam \
>>>>>>   couch_httpd_proxy.beam \
>>>>>>   couch_httpd_external.beam \
>>>>>>   couch_httpd_misc_handlers.beam \
>>>>>> 
>>>>>> 
>>>>> 
>> http://git-wip-us.apache.org/repos/asf/couchdb/blob/0777262f/src/couchdb/couch_httpd.erl
>>>>>> ----------------------------------------------------------------------
>>>>>> diff --git a/src/couchdb/couch_httpd.erl b/src/couchdb/couch_httpd.erl
>>>>>> index 45ceebc..6bba871 100644
>>>>>> --- a/src/couchdb/couch_httpd.erl
>>>>>> +++ b/src/couchdb/couch_httpd.erl
>>>>>> @@ -275,7 +275,10 @@ handle_request_int(MochiReq, DefaultFun,
>>>>>> 
>>>>>>   % allow broken HTTP clients to fake a full method vocabulary with
>> an
>>>>> X-HTTP-METHOD-OVERRIDE header
>>>>>>   MethodOverride =
>>>>> MochiReq:get_primary_header_value("X-HTTP-Method-Override"),
>>>>>> -    Method2 = case lists:member(MethodOverride, ["GET", "HEAD",
>> "POST",
>>>>> "PUT", "DELETE", "TRACE", "CONNECT", "COPY"]) of
>>>>>> +    Method2 = case lists:member(MethodOverride, ["GET", "HEAD",
>> "POST",
>>>>>> +                                                 "PUT", "DELETE",
>>>>>> +                                                 "TRACE", "CONNECT",
>>>>>> +                                                 "COPY"]) of
>>>>>>   true ->
>>>>>>       ?LOG_INFO("MethodOverride: ~s (real method was ~s)",
>>>>> [MethodOverride, Method1]),
>>>>>>       case Method1 of
>>>>>> @@ -312,13 +315,19 @@ handle_request_int(MochiReq, DefaultFun,
>>>>>>   HandlerFun = couch_util:dict_find(HandlerKey, UrlHandlers,
>>>>> DefaultFun),
>>>>>>   {ok, AuthHandlers} = application:get_env(couch, auth_handlers),
>>>>>> 
>>>>>> +    ?LOG_INFO("fuck you ~p~n", [Method]),
>>>>>>   {ok, Resp} =
>>>>>>   try
>>>>>> -        case authenticate_request(HttpReq, AuthHandlers) of
>>>>>> -        #httpd{} = Req ->
>>>>>> -            HandlerFun(Req);
>>>>>> -        Response ->
>>>>>> -            Response
>>>>>> +        case couch_httpd_cors:is_preflight_request(HttpReq) of
>>>>>> +            #httpd{} ->
>>>>>> +                case authenticate_request(HttpReq, AuthHandlers) of
>>>>>> +                #httpd{} = Req ->
>>>>>> +                    HandlerFun(Req);
>>>>>> +                Response ->
>>>>>> +                    Response
>>>>>> +                end;
>>>>>> +            Response ->
>>>>>> +                Response
>>>>>>       end
>>>>>>   catch
>>>>>>       throw:{http_head_abort, Resp0} ->
>>>>>> @@ -450,10 +459,13 @@ accepted_encodings(#httpd{mochi_req=MochiReq})
>> ->
>>>>>> serve_file(Req, RelativePath, DocumentRoot) ->
>>>>>>   serve_file(Req, RelativePath, DocumentRoot, []).
>>>>>> 
>>>>>> -serve_file(#httpd{mochi_req=MochiReq}=Req, RelativePath,
>> DocumentRoot,
>>>>> ExtraHeaders) ->
>>>>>> +serve_file(#httpd{mochi_req=MochiReq}=Req, RelativePath,
>> DocumentRoot,
>>>>>> +           ExtraHeaders) ->
>>>>>>   log_request(Req, 200),
>>>>>> -    {ok, MochiReq:serve_file(RelativePath, DocumentRoot,
>>>>>> -        server_header() ++ couch_httpd_auth:cookie_auth_header(Req,
>> [])
>>>>> ++ ExtraHeaders)}.
>>>>>> +    {ok, MochiReq:serve_file(RelativePath, DocumentRoot,
>>>>> server_header() ++
>>>>>> +                             couch_httpd_cors:cors_headers(Req) ++
>>>>>> +                             couch_httpd_auth:cookie_auth_header(Req,
>>>>> []) ++
>>>>>> +                             ExtraHeaders)}.
>>>>>> 
>>>>>> qs_value(Req, Key) ->
>>>>>>   qs_value(Req, Key, undefined).
>>>>>> @@ -603,7 +615,10 @@ log_request(#httpd{mochi_req=MochiReq,peer=Peer},
>>>>> Code) ->
>>>>>> start_response_length(#httpd{mochi_req=MochiReq}=Req, Code, Headers,
>>>>> Length) ->
>>>>>>   log_request(Req, Code),
>>>>>>   couch_stats_collector:increment({httpd_status_codes, Code}),
>>>>>> -    Resp = MochiReq:start_response_length({Code, Headers ++
>>>>> server_header() ++ couch_httpd_auth:cookie_auth_header(Req, Headers),
>>>>> Length}),
>>>>>> +    Headers1 = Headers ++ server_header() ++
>>>>>> +               couch_httpd_auth:cookie_auth_header(Req, Headers) ++
>>>>>> +               couch_httpd_cors:cors_headers(Req),
>>>>>> +    Resp = MochiReq:start_response_length({Code, Headers1, Length}),
>>>>>>   case MochiReq:get(method) of
>>>>>>   'HEAD' -> throw({http_head_abort, Resp});
>>>>>>   _ -> ok
>>>>>> @@ -614,7 +629,8 @@ start_response(#httpd{mochi_req=MochiReq}=Req,
>> Code,
>>>>> Headers) ->
>>>>>>   log_request(Req, Code),
>>>>>>   couch_stats_collector:increment({httpd_status_codes, Code}),
>>>>>>   CookieHeader = couch_httpd_auth:cookie_auth_header(Req, Headers),
>>>>>> -    Headers2 = Headers ++ server_header() ++ CookieHeader,
>>>>>> +    Headers2 = Headers ++ server_header() ++ CookieHeader ++
>>>>>> +               couch_httpd_cors:cors_headers(Req),
>>>>>>   Resp = MochiReq:start_response({Code, Headers2}),
>>>>>>   case MochiReq:get(method) of
>>>>>>       'HEAD' -> throw({http_head_abort, Resp});
>>>>>> @@ -646,8 +662,11 @@ http_1_0_keep_alive(Req, Headers) ->
>>>>>> start_chunked_response(#httpd{mochi_req=MochiReq}=Req, Code, Headers)
>> ->
>>>>>>   log_request(Req, Code),
>>>>>>   couch_stats_collector:increment({httpd_status_codes, Code}),
>>>>>> -    Headers2 = http_1_0_keep_alive(MochiReq, Headers),
>>>>>> -    Resp = MochiReq:respond({Code, Headers2 ++ server_header() ++
>>>>> couch_httpd_auth:cookie_auth_header(Req, Headers2), chunked}),
>>>>>> +    Headers1 = http_1_0_keep_alive(MochiReq, Headers),
>>>>>> +    Headers2 = Headers1 ++ server_header() ++
>>>>>> +               couch_httpd_auth:cookie_auth_header(Req, Headers1) ++
>>>>>> +               couch_httpd_cors:cors_headers(Req),
>>>>>> +    Resp = MochiReq:respond({Code, Headers2, chunked}),
>>>>>>   case MochiReq:get(method) of
>>>>>>   'HEAD' -> throw({http_head_abort, Resp});
>>>>>>   _ -> ok
>>>>>> @@ -668,14 +687,18 @@ last_chunk(Resp) ->
>>>>>> send_response(#httpd{mochi_req=MochiReq}=Req, Code, Headers, Body) ->
>>>>>>   log_request(Req, Code),
>>>>>>   couch_stats_collector:increment({httpd_status_codes, Code}),
>>>>>> -    Headers2 = http_1_0_keep_alive(MochiReq, Headers),
>>>>>> +    Headers1 = http_1_0_keep_alive(MochiReq, Headers),
>>>>>>   if Code >= 500 ->
>>>>>>       ?LOG_ERROR("httpd ~p error response:~n ~s", [Code, Body]);
>>>>>>   Code >= 400 ->
>>>>>>       ?LOG_DEBUG("httpd ~p error response:~n ~s", [Code, Body]);
>>>>>>   true -> ok
>>>>>>   end,
>>>>>> -    {ok, MochiReq:respond({Code, Headers2 ++ server_header() ++
>>>>> couch_httpd_auth:cookie_auth_header(Req, Headers2), Body})}.
>>>>>> +    Headers2 = Headers1 ++ server_header() ++
>>>>>> +               couch_httpd_cors:cors_headers(Req) ++
>>>>>> +               couch_httpd_auth:cookie_auth_header(Req, Headers1),
>>>>>> +
>>>>>> +    {ok, MochiReq:respond({Code, Headers2, Body})}.
>>>>>> 
>>>>>> send_method_not_allowed(Req, Methods) ->
>>>>>>   send_error(Req, 405, [{"Allow", Methods}],
>> <<"method_not_allowed">>,
>>>>> ?l2b("Only " ++ Methods ++ " allowed")).
>>>>>> 
>>>>>> 
>>>>> 
>> http://git-wip-us.apache.org/repos/asf/couchdb/blob/0777262f/src/couchdb/couch_httpd_cors.erl
>>>>>> ----------------------------------------------------------------------
>>>>>> diff --git a/src/couchdb/couch_httpd_cors.erl
>>>>> b/src/couchdb/couch_httpd_cors.erl
>>>>>> new file mode 100644
>>>>>> index 0000000..69f57ed
>>>>>> --- /dev/null
>>>>>> +++ b/src/couchdb/couch_httpd_cors.erl
>>>>>> @@ -0,0 +1,230 @@
>>>>>> +% 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.
>>>>>> +
>>>>>> +%% @doc module to handle Cross-Origin Resource Sharing
>>>>>> +%%
>>>>>> +%% This module handles CROSS requests and preflight request for a
>>>>>> +%% couchdb Node. The config is done in the ini file.
>>>>>> +
>>>>>> +
>>>>>> +-module(couch_httpd_cors).
>>>>>> +
>>>>>> +-include("couch_db.hrl").
>>>>>> +
>>>>>> +-export([is_preflight_request/1, cors_headers/1]).
>>>>>> +
>>>>>> +-define(SUPPORTED_HEADERS, "Accept, Accept-Language, Content-Type,"
>> ++
>>>>>> +        "Expires, Last-Modified, Pragma, Origin, Content-Length," ++
>>>>>> +        "If-Match, Destination, X-Requested-With, " ++
>>>>>> +        "X-Http-Method-Override, Content-Range").
>>>>>> +
>>>>>> +-define(SUPPORTED_METHODS, "GET, HEAD, POST, PUT, DELETE," ++
>>>>>> +        "TRACE, CONNECT, COPY, OPTIONS").
>>>>>> +
>>>>>> +is_preflight_request(#httpd{method=Method}=Req) when Method /=
>>>>> 'OPTIONS' ->
>>>>>> +    Req;
>>>>>> +is_preflight_request(#httpd{mochi_req=MochiReq}=Req) ->
>>>>>> +    case get_bool_config("httpd", "enable_cors", false) of
>>>>>> +        true ->
>>>>>> +            case preflight_request(MochiReq) of
>>>>>> +                {ok, PreflightHeaders} ->
>>>>>> +                    couch_httpd:send_response(Req, 204,
>>>>> PreflightHeaders, <<>>);
>>>>>> +                _ ->
>>>>>> +                    Req
>>>>>> +            end;
>>>>>> +        false ->
>>>>>> +            Req
>>>>>> +    end.
>>>>>> +
>>>>>> +
>>>>>> +cors_headers(#httpd{mochi_req=MochiReq}) ->
>>>>>> +    Host = couch_httpd_vhost:host(MochiReq),
>>>>>> +    case get_bool_config("httpd", "enable_cors", false) of
>>>>>> +        true ->
>>>>>> +            AcceptedOrigins = re:split(cors_config(Host, "origins",
>> []),
>>>>>> +                                       "\\s*,\\s*",
>>>>>> +                                       [trim, {return, list}]),
>>>>>> +            case MochiReq:get_header_value("Origin") of
>>>>>> +                undefined ->
>>>>>> +                    [];
>>>>>> +                <<"*">> ->
>>>>>> +                    handle_cors_headers("*", Host, AcceptedOrigins);
>>>>>> +                <<"null">> ->
>>>>>> +                    handle_cors_headers("*", Host, AcceptedOrigins);
>>>>>> +                Origin ->
>>>>>> +                    handle_cors_headers(couch_util:to_list(Origin),
>>>>>> +                                        Host, AcceptedOrigins)
>>>>>> +            end;
>>>>>> +        false ->
>>>>>> +            []
>>>>>> +    end.
>>>>>> +
>>>>>> +handle_cors_headers("*", _Host, _AcceptedOrigins) ->
>>>>>> +    [{"Access-Control-Allow-Origin", "*"}];
>>>>>> +handle_cors_headers(Origin, Host, []) ->
>>>>>> +    case allows_credentials(Origin, Host) of
>>>>>> +        true ->
>>>>>> +            [{"Access-Control-Allow-Origin", Origin},
>>>>>> +             {"Access-Control-Allow-Credentials", "true"}];
>>>>>> +        false ->
>>>>>> +            [{"Access-Control-Allow-Origin", Origin}]
>>>>>> +    end;
>>>>>> +handle_cors_headers(Origin, Host, AcceptedOrigins) ->
>>>>>> +    AllowsCredentials = allows_credentials(Origin, Host),
>>>>>> +    case lists:member(Origin, AcceptedOrigins) of
>>>>>> +        true when AllowsCredentials =:= true ->
>>>>>> +            [{"Access-Control-Allow-Origin", Origin},
>>>>>> +             {"Access-Control-Allow-Credentials", "true"}];
>>>>>> +        true ->
>>>>>> +            [{"Access-Control-Allow-Origin", Origin}];
>>>>>> +        _ ->
>>>>>> +            []
>>>>>> +    end.
>>>>>> +
>>>>>> +
>>>>>> +preflight_request(MochiReq) ->
>>>>>> +    Host = couch_httpd_vhost:host(MochiReq),
>>>>>> +    case MochiReq:get_header_value("Origin") of
>>>>>> +        undefined ->
>>>>>> +            MochiReq;
>>>>>> +        <<"*">>  ->
>>>>>> +            handle_preflight_request("*", Host, MochiReq);
>>>>>> +        <<"null">> ->
>>>>>> +            handle_preflight_request("*", Host, MochiReq);
>>>>>> +        Origin ->
>>>>>> +            AcceptedOrigins = re:split(cors_config(Host, "origins",
>> []),
>>>>>> +                                       "\\s*,\\s*",
>>>>>> +                                       [trim, {return, list}]),
>>>>>> +            case AcceptedOrigins of
>>>>>> +                [] ->
>>>>>> +
>> handle_preflight_request(couch_util:to_list(Origin),
>>>>>> +                                             Host, MochiReq);
>>>>>> +                _ ->
>>>>>> +                    case lists:member(Origin, AcceptedOrigins) of
>>>>>> +                        true ->
>>>>>> +
>>>>> handle_preflight_request(couch_util:to_list(Origin),
>>>>>> +                                                     Host, MochiReq);
>>>>>> +                        false ->
>>>>>> +                            false
>>>>>> +                    end
>>>>>> +            end
>>>>>> +    end.
>>>>>> +
>>>>>> +handle_preflight_request(Origin, Host, MochiReq) ->
>>>>>> +    %% get supported methods
>>>>>> +    SupportedMethods = split_list(cors_config(Host, "methods",
>>>>>> +                                              ?SUPPORTED_METHODS)),
>>>>>> +
>>>>>> +    % get supported headers
>>>>>> +    AllSupportedHeaders = split_list(cors_config(Host, "headers",
>>>>>> +
>> ?SUPPORTED_HEADERS)),
>>>>>> +
>>>>>> +    SupportedHeaders = [string:to_lower(H) || H <-
>> AllSupportedHeaders],
>>>>>> +
>>>>>> +    % get max age
>>>>>> +    MaxAge = cors_config(Host, "max_age", "12345"),
>>>>>> +
>>>>>> +    PreflightHeaders0 = case allows_credentials(Origin, Host) of
>>>>>> +        true ->
>>>>>> +            [{"Access-Control-Allow-Origin", Origin},
>>>>>> +             {"Access-Control-Allow-Credentials", "true"},
>>>>>> +             {"Access-Control-Max-Age", MaxAge},
>>>>>> +             {"Access-Control-Allow-Methods",
>>>>> string:join(SupportedMethods,
>>>>>> +                                                          ", ")}];
>>>>>> +        false ->
>>>>>> +            [{"Access-Control-Allow-Origin", Origin},
>>>>>> +             {"Access-Control-Max-Age", MaxAge},
>>>>>> +             {"Access-Control-Allow-Methods",
>>>>> string:join(SupportedMethods,
>>>>>> +                                                          ", ")}]
>>>>>> +    end,
>>>>>> +
>>>>>> +    case MochiReq:get_header_value("Access-Control-Request-Method")
>> of
>>>>>> +        undefined ->
>>>>>> +            {ok, PreflightHeaders0};
>>>>>> +        Method ->
>>>>>> +            case lists:member(Method, SupportedMethods) of
>>>>>> +                true ->
>>>>>> +                    % method ok , check headers
>>>>>> +                    AccessHeaders = MochiReq:get_header_value(
>>>>>> +                            "Access-Control-Request-Headers"),
>>>>>> +                    {FinalReqHeaders, ReqHeaders} = case
>> AccessHeaders
>>>>> of
>>>>>> +                        undefined -> {"", []};
>>>>>> +                        Headers ->
>>>>>> +                            % transform header list in something we
>>>>>> +                            % could check. make sure everything is a
>>>>>> +                            % list
>>>>>> +                            RH = [string:to_lower(H)
>>>>>> +                                  || H <- re:split(Headers, ",\\s*",
>>>>>> +
>>>>> [{return,list},trim])],
>>>>>> +                            {Headers, RH}
>>>>>> +                    end,
>>>>>> +                    % check if headers are supported
>>>>>> +                    case ReqHeaders -- SupportedHeaders of
>>>>>> +                        [] ->
>>>>>> +                            PreflightHeaders = PreflightHeaders0 ++
>>>>>> +
>>>>> [{"Access-Control-Allow-Headers",
>>>>>> +                                                 FinalReqHeaders}],
>>>>>> +                            {ok, PreflightHeaders};
>>>>>> +                        _ ->
>>>>>> +                            false
>>>>>> +                    end;
>>>>>> +                false ->
>>>>>> +                    false
>>>>>> +            end
>>>>>> +    end.
>>>>>> +
>>>>>> +
>>>>>> +allows_credentials("*", _Host) ->
>>>>>> +    false;
>>>>>> +allows_credentials(_Origin, Host) ->
>>>>>> +    Default = get_bool_config("cors", "allows_credentials",
>>>>>> +                              false),
>>>>>> +
>>>>>> +    get_bool_config(cors_section(Host), "allows_credentials",
>>>>>> +                    Default).
>>>>>> +
>>>>>> +
>>>>>> +cors_config(Host, Key, Default) ->
>>>>>> +    couch_config:get(cors_section(Host), Key,
>>>>>> +                     couch_config:get("cors", Key, Default)).
>>>>>> +
>>>>>> +cors_section(Host0) ->
>>>>>> +    {Host, _Port} = split_host_port(Host0),
>>>>>> +    "cors:" ++ Host.
>>>>>> +
>>>>>> +get_bool_config(Section, Key, Default) ->
>>>>>> +    case couch_config:get(Section, Key) of
>>>>>> +        undefined ->
>>>>>> +            Default;
>>>>>> +        "true" ->
>>>>>> +            true;
>>>>>> +        "false" ->
>>>>>> +            false
>>>>>> +    end.
>>>>>> +
>>>>>> +split_list(S) ->
>>>>>> +    re:split(S, "\\s*,\\s*", [trim, {return, list}]).
>>>>>> +
>>>>>> +split_host_port(HostAsString) ->
>>>>>> +    case string:rchr(HostAsString, $:) of
>>>>>> +        0 ->
>>>>>> +            {HostAsString, '*'};
>>>>>> +        N ->
>>>>>> +            HostPart = string:substr(HostAsString, 1, N-1),
>>>>>> +            case (catch
>>>>> erlang:list_to_integer(string:substr(HostAsString,
>>>>>> +                            N+1, length(HostAsString)))) of
>>>>>> +                {'EXIT', _} ->
>>>>>> +                    {HostAsString, '*'};
>>>>>> +                Port ->
>>>>>> +                    {HostPart, Port}
>>>>>> +            end
>>>>>> +    end.
>>>>>> 
>>>>>> 
>>>>> 
>> http://git-wip-us.apache.org/repos/asf/couchdb/blob/0777262f/src/couchdb/couch_httpd_vhost.erl
>>>>>> ----------------------------------------------------------------------
>>>>>> diff --git a/src/couchdb/couch_httpd_vhost.erl
>>>>> b/src/couchdb/couch_httpd_vhost.erl
>>>>>> index 59f05ce..4c3ebfe 100644
>>>>>> --- a/src/couchdb/couch_httpd_vhost.erl
>>>>>> +++ b/src/couchdb/couch_httpd_vhost.erl
>>>>>> @@ -15,7 +15,7 @@
>>>>>> 
>>>>>> -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]).
>>>>>> 
>>>>>> @@ -32,7 +32,7 @@
>>>>>> %% 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.
>>>>>> +%% vhost list.
>>>>>> %%
>>>>>> %% Declaration of vhosts take place in the configuration file :
>>>>>> %%
>>>>>> @@ -51,7 +51,7 @@
>>>>>> %%      "*.db.example.com = /"  will match all cname on top of db
>>>>>> %% examples to the root of the machine.
>>>>>> %%
>>>>>> -%%
>>>>>> +%%
>>>>>> %% Rewriting Hosts to path
>>>>>> %% -----------------------
>>>>>> %%
>>>>>> @@ -75,7 +75,7 @@
>>>>>> %%    redirect_vhost_handler = {Module, Fun}
>>>>>> %%
>>>>>> %% The function take 2 args : the mochiweb request object and the
>> target
>>>>>> -%%% path.
>>>>>> +%%% path.
>>>>>> 
>>>>>> start_link() ->
>>>>>>   gen_server:start_link({local, ?MODULE}, ?MODULE, [], []).
>>>>>> @@ -98,15 +98,7 @@ dispatch_host(MochiReq) ->
>>>>>>   {"/" ++ VPath, Query, Fragment} =
>>>>> mochiweb_util:urlsplit_path(MochiReq:get(raw_path)),
>>>>>>   VPathParts =  string:tokens(VPath, "/"),
>>>>>> 
>>>>>> -    XHost = couch_config:get("httpd", "x_forwarded_host",
>>>>> "X-Forwarded-Host"),
>>>>>> -    VHost = case MochiReq:get_header_value(XHost) of
>>>>>> -        undefined ->
>>>>>> -            case MochiReq:get_header_value("Host") of
>>>>>> -                undefined -> [];
>>>>>> -                Value1 -> Value1
>>>>>> -            end;
>>>>>> -        Value -> Value
>>>>>> -    end,
>>>>>> +    VHost = host(MochiReq),
>>>>>>   {VHostParts, VhostPort} = split_host_port(VHost),
>>>>>>   FinalMochiReq = case try_bind_vhost(VHosts,
>>>>> lists:reverse(VHostParts),
>>>>>>           VhostPort, VPathParts) of
>>>>>> @@ -133,14 +125,14 @@ append_path("/"=_Target, "/"=_Path) ->
>>>>>> append_path(Target, Path) ->
>>>>>>   Target ++ Path.
>>>>>> 
>>>>>> -% default redirect vhost handler
>>>>>> +% 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,
>>>>>> +    Headers = mochiweb_headers:enter("x-couchdb-vhost-path", Path,
>>>>>>       MochiReq:get(headers)),
>>>>>> 
>>>>>>   % build a new mochiweb request
>>>>>> @@ -154,7 +146,7 @@ redirect_to_vhost(MochiReq, VhostTarget) ->
>>>>>>   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
>>>>>> +%* 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),
>>>>>> @@ -175,14 +167,14 @@ try_bind_vhost([], _HostParts, _Port,
>> _PathParts)
>>>>> ->
>>>>>> try_bind_vhost([VhostSpec|Rest], HostParts, Port, PathParts) ->
>>>>>>   {{VHostParts, VPort, VPath}, Path} = VhostSpec,
>>>>>>   case bind_port(VPort, Port) of
>>>>>> -        ok ->
>>>>>> +        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 ->
>>>>>> +                        fail ->
>>>>>>                           try_bind_vhost(Rest, HostParts, Port,
>>>>>>                               PathParts)
>>>>>>                   end;
>>>>>> @@ -193,7 +185,7 @@ try_bind_vhost([VhostSpec|Rest], HostParts, Port,
>>>>> PathParts) ->
>>>>>> 
>>>>>> %% doc: build new patch from bindings. bindings are query args
>>>>>> %% (+ dynamic query rewritten if needed) and bindings found in
>>>>>> -%% bind_path step.
>>>>>> +%% bind_path step.
>>>>>> %% TODO: merge code with rewrite. But we need to make sure we are
>>>>>> %% in string here.
>>>>>> make_target([], _Bindings, _Remaining, Acc) ->
>>>>>> @@ -223,7 +215,7 @@ 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([{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);
>>>>>> @@ -243,6 +235,19 @@ bind_path(_, _) ->
>>>>>> 
>>>>>> 
>>>>>> %% 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) ->
>>>>>> @@ -267,15 +272,15 @@ parse_vhost(Vhost) ->
>>>>>>           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,
>>>>>> +            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), '*'};
>>>>>> @@ -303,7 +308,7 @@ make_spec([P|R], Acc) ->
>>>>>> 
>>>>>> 
>>>>>> parse_var(P) ->
>>>>>> -    case P of
>>>>>> +    case P of
>>>>>>       ":" ++ Var ->
>>>>>>           {bind, Var};
>>>>>>       _ -> P
>>>>>> @@ -323,7 +328,7 @@ make_path(Parts) ->
>>>>>> 
>>>>>> init(_) ->
>>>>>>   ok = couch_config:register(fun ?MODULE:config_change/2),
>>>>>> -
>>>>>> +
>>>>>>   %% load configuration
>>>>>>   {VHostGlobals, VHosts, Fun} = load_conf(),
>>>>>>   State = #vhosts_state{
>>>>>> 
>>>>>> 
>>>>> 
>> http://git-wip-us.apache.org/repos/asf/couchdb/blob/0777262f/test/etap/231_cors.t
>>>>>> ----------------------------------------------------------------------
>>>>>> diff --git a/test/etap/231_cors.t b/test/etap/231_cors.t
>>>>>> new file mode 100644
>>>>>> index 0000000..72fc3df
>>>>>> --- /dev/null
>>>>>> +++ b/test/etap/231_cors.t
>>>>>> @@ -0,0 +1,230 @@
>>>>>> +#!/usr/bin/env escript
>>>>>> +%% -*- erlang -*-
>>>>>> +
>>>>>> +% 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.
>>>>>> +
>>>>>> +-record(user_ctx, {
>>>>>> +    name = null,
>>>>>> +    roles = [],
>>>>>> +    handler
>>>>>> +}).
>>>>>> +
>>>>>> +
>>>>>> +-define(SUPPORTED_METHODS, "GET, HEAD, POST, PUT, DELETE, TRACE,
>>>>> CONNECT, COPY, OPTIONS").
>>>>>> +server() ->
>>>>>> +    lists:concat([
>>>>>> +        "http://127.0.0.1:",
>>>>>> +        mochiweb_socket_server:get(couch_httpd, port),
>>>>>> +        "/"
>>>>>> +    ]).
>>>>>> +
>>>>>> +
>>>>>> +main(_) ->
>>>>>> +    test_util:init_code_path(),
>>>>>> +
>>>>>> +    etap:plan(11),
>>>>>> +    case (catch test()) of
>>>>>> +        ok ->
>>>>>> +            etap:end_tests();
>>>>>> +        Other ->
>>>>>> +            etap:diag(io_lib:format("Test died abnormally: ~p",
>>>>> [Other])),
>>>>>> +            etap:bail(Other)
>>>>>> +    end,
>>>>>> +    ok.
>>>>>> +
>>>>>> +dbname() -> "etap-test-db".
>>>>>> +dbname1() -> "etap-test-db1".
>>>>>> +dbname2() -> "etap-test-db2".
>>>>>> +
>>>>>> +admin_user_ctx() -> {user_ctx, #user_ctx{roles=[<<"_admin">>]}}.
>>>>>> +
>>>>>> +set_admin_password(UserName, Password) ->
>>>>>> +    Salt = binary_to_list(couch_uuids:random()),
>>>>>> +    Hashed = couch_util:to_hex(crypto:sha(Password ++ Salt)),
>>>>>> +    couch_config:set("admins", UserName,
>>>>>> +        "-hashed-" ++ Hashed ++ "," ++ Salt, false).
>>>>>> +
>>>>>> +test() ->
>>>>>> +
>>>>>> +    ibrowse:start(),
>>>>>> +    crypto:start(),
>>>>>> +
>>>>>> +    %% launch couchdb
>>>>>> +    couch_server_sup:start_link(test_util:config_files()),
>>>>>> +
>>>>>> +    %% initialize db
>>>>>> +    timer:sleep(1000),
>>>>>> +    couch_server:delete(list_to_binary(dbname()),
>> [admin_user_ctx()]),
>>>>>> +    couch_server:delete(list_to_binary(dbname1()),
>> [admin_user_ctx()]),
>>>>>> +    couch_server:delete(list_to_binary(dbname2()),
>> [admin_user_ctx()]),
>>>>>> +    {ok, Db} = couch_db:create(list_to_binary(dbname()),
>>>>> [admin_user_ctx()]),
>>>>>> +    {ok, Db1} = couch_db:create(list_to_binary(dbname1()),
>>>>> [admin_user_ctx()]),
>>>>>> +    {ok, Db2} = couch_db:create(list_to_binary(dbname2()),
>>>>> [admin_user_ctx()]),
>>>>>> +
>>>>>> +    % CORS is disabled by default
>>>>>> +    test_no_headers_server(),
>>>>>> +    test_no_headers_db(),
>>>>>> +
>>>>>> +    % Now enable CORS
>>>>>> +    ok = couch_config:set("httpd", "enable_cors", "true", false),
>>>>>> +    ok = couch_config:set("cors", "origins", "http://example.com",
>>>>> false),
>>>>>> +
>>>>>> +    %% do tests
>>>>>> +    test_incorrect_origin_simple_request(),
>>>>>> +    test_incorrect_origin_preflight_request(),
>>>>>> +
>>>>>> +    test_preflight_request(),
>>>>>> +    test_db_request(),
>>>>>> +    test_db_preflight_request(),
>>>>>> +    test_db_origin_request(),
>>>>>> +    test_db1_origin_request(),
>>>>>> +
>>>>>> +    %% do tests with auth
>>>>>> +    ok = set_admin_password("test", "test"),
>>>>>> +
>>>>>> +    test_db_preflight_auth_request(),
>>>>>> +    test_db_origin_auth_request(),
>>>>>> +
>>>>>> +    %% restart boilerplate
>>>>>> +    catch couch_db:close(Db),
>>>>>> +    catch couch_db:close(Db1),
>>>>>> +    catch couch_db:close(Db2),
>>>>>> +
>>>>>> +    couch_server:delete(list_to_binary(dbname()),
>> [admin_user_ctx()]),
>>>>>> +    couch_server:delete(list_to_binary(dbname1()),
>> [admin_user_ctx()]),
>>>>>> +    couch_server:delete(list_to_binary(dbname2()),
>> [admin_user_ctx()]),
>>>>>> +
>>>>>> +    timer:sleep(3000),
>>>>>> +    couch_server_sup:stop(),
>>>>>> +    ok.
>>>>>> +
>>>>>> +%% Cors is disabled, should not return Access-Control-Allow-Origin
>>>>>> +test_no_headers_server() ->
>>>>>> +    Headers = [{"Origin", "http://127.0.0.1"}],
>>>>>> +    {ok, _, Resp, _} = ibrowse:send_req(server(), Headers, get, []),
>>>>>> +    etap:is(proplists:get_value("Access-Control-Allow-Origin", Resp),
>>>>>> +            undefined, "No CORS Headers when disabled").
>>>>>> +
>>>>>> +%% Cors is disabled, should not return Access-Control-Allow-Origin
>>>>>> +test_no_headers_db() ->
>>>>>> +    Headers = [{"Origin", "http://127.0.0.1"}],
>>>>>> +    Url = server() ++ "etap-test-db",
>>>>>> +    {ok, _, Resp, _} = ibrowse:send_req(Url, Headers, get, []),
>>>>>> +    etap:is(proplists:get_value("Access-Control-Allow-Origin", Resp),
>>>>>> +            undefined, "No CORS Headers when disabled").
>>>>>> +
>>>>>> +test_incorrect_origin_simple_request() ->
>>>>>> +    Headers = [{"Origin", "http://127.0.0.1"}],
>>>>>> +    {ok, _, RespHeaders, _} = ibrowse:send_req(server(), Headers,
>> get,
>>>>> []),
>>>>>> +    etap:is(proplists:get_value("Access-Control-Allow-Origin",
>>>>> RespHeaders),
>>>>>> +            undefined,
>>>>>> +            "Specified invalid origin, no Access").
>>>>>> +
>>>>>> +test_incorrect_origin_preflight_request() ->
>>>>>> +    Headers = [{"Origin", "http://127.0.0.1"},
>>>>>> +               {"Access-Control-Request-Method", "GET"}],
>>>>>> +    {ok, _, RespHeaders, _} = ibrowse:send_req(server(), Headers,
>>>>> options, []),
>>>>>> +    etap:is(proplists:get_value("Access-Control-Allow-Origin",
>>>>> RespHeaders),
>>>>>> +            undefined,
>>>>>> +            "invalid origin").
>>>>>> +
>>>>>> +test_preflight_request() ->
>>>>>> +    Headers = [{"Origin", "http://example.com"},
>>>>>> +               {"Access-Control-Request-Method", "GET"}],
>>>>>> +    case ibrowse:send_req(server(), Headers, options, []) of
>>>>>> +    {ok, _, RespHeaders, _}  ->
>>>>>> +        etap:is(proplists:get_value("Access-Control-Allow-Methods",
>>>>> RespHeaders),
>>>>>> +            ?SUPPORTED_METHODS,
>>>>>> +            "test_preflight_request Access-Control-Allow-Methods
>> ok");
>>>>>> +    _ ->
>>>>>> +        etap:is(false, true, "ibrowse failed")
>>>>>> +    end.
>>>>>> +
>>>>>> +test_db_request() ->
>>>>>> +    Headers = [{"Origin", "http://example.com"}],
>>>>>> +    Url = server() ++ "etap-test-db",
>>>>>> +    case ibrowse:send_req(Url, Headers, get, []) of
>>>>>> +    {ok, _, RespHeaders, _Body} ->
>>>>>> +        etap:is(proplists:get_value("Access-Control-Allow-Origin",
>>>>> RespHeaders),
>>>>>> +            "http://example.com",
>>>>>> +            "db Access-Control-Allow-Origin ok");
>>>>>> +    _ ->
>>>>>> +        etap:is(false, true, "ibrowse failed")
>>>>>> +    end.
>>>>>> +
>>>>>> +test_db_preflight_request() ->
>>>>>> +    Url = server() ++ "etap-test-db",
>>>>>> +    Headers = [{"Origin", "http://example.com"},
>>>>>> +               {"Access-Control-Request-Method", "GET"}],
>>>>>> +    case ibrowse:send_req(Url, Headers, options, []) of
>>>>>> +    {ok, _, RespHeaders, _} ->
>>>>>> +        etap:is(proplists:get_value("Access-Control-Allow-Methods",
>>>>> RespHeaders),
>>>>>> +                ?SUPPORTED_METHODS,
>>>>>> +                "db Access-Control-Allow-Methods ok");
>>>>>> +    _ ->
>>>>>> +        etap:is(false, true, "ibrowse failed")
>>>>>> +    end.
>>>>>> +
>>>>>> +
>>>>>> +test_db_origin_request() ->
>>>>>> +    Headers = [{"Origin", "http://example.com"}],
>>>>>> +    Url = server() ++ "etap-test-db",
>>>>>> +    case ibrowse:send_req(Url, Headers, get, []) of
>>>>>> +    {ok, _, RespHeaders, _Body} ->
>>>>>> +        etap:is(proplists:get_value("Access-Control-Allow-Origin",
>>>>> RespHeaders),
>>>>>> +            "http://example.com",
>>>>>> +            "db origin ok");
>>>>>> +    _ ->
>>>>>> +        etap:is(false, true, "ibrowse failed")
>>>>>> +    end.
>>>>>> +
>>>>>> +test_db1_origin_request() ->
>>>>>> +    Headers = [{"Origin", "http://example.com"}],
>>>>>> +    Url = server() ++ "etap-test-db1",
>>>>>> +    case ibrowse:send_req(Url, Headers, get, [], [{host_header, "
>>>>> example.com"}]) of
>>>>>> +    {ok, _, RespHeaders, _Body} ->
>>>>>> +        etap:is(proplists:get_value("Access-Control-Allow-Origin",
>>>>> RespHeaders),
>>>>>> +            "http://example.com",
>>>>>> +            "db origin ok");
>>>>>> +    _Else ->
>>>>>> +        io:format("else ~p~n", [_Else]),
>>>>>> +        etap:is(false, true, "ibrowse failed")
>>>>>> +    end.
>>>>>> +
>>>>>> +test_db_preflight_auth_request() ->
>>>>>> +    Url = server() ++ "etap-test-db2",
>>>>>> +    Headers = [{"Origin", "http://example.com"},
>>>>>> +               {"Access-Control-Request-Method", "GET"}],
>>>>>> +    case ibrowse:send_req(Url, Headers, options, []) of
>>>>>> +    {ok, _Status, RespHeaders, _} ->
>>>>>> +        etap:is(proplists:get_value("Access-Control-Allow-Methods",
>>>>> RespHeaders),
>>>>>> +                ?SUPPORTED_METHODS,
>>>>>> +                "db Access-Control-Allow-Methods ok");
>>>>>> +    _ ->
>>>>>> +        etap:is(false, true, "ibrowse failed")
>>>>>> +    end.
>>>>>> +
>>>>>> +
>>>>>> +test_db_origin_auth_request() ->
>>>>>> +    Headers = [{"Origin", "http://example.com"}],
>>>>>> +    Url = server() ++ "etap-test-db2",
>>>>>> +
>>>>>> +    case ibrowse:send_req(Url, Headers, get, [],
>>>>>> +        [{basic_auth, {"test", "test"}}]) of
>>>>>> +    {ok, _, RespHeaders, _Body} ->
>>>>>> +        etap:is(proplists:get_value("Access-Control-Allow-Origin",
>>>>> RespHeaders),
>>>>>> +            "http://example.com",
>>>>>> +            "db origin ok");
>>>>>> +    _ ->
>>>>>> +        etap:is(false, true, "ibrowse failed")
>>>>>> +    end.
>>>>>> 
>>>>> 
>>>>> 
>>> 
>> 


Mime
View raw message