incubator-couchdb-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From Benoit Chesneau <bchesn...@gmail.com>
Subject Re: git commit: handle CORS. fix #COUCHDB-431
Date Thu, 01 Nov 2012 05:41:16 GMT
ok I will stop for now pushed 431_feature_cors (the removing of caps is
wanted here and I will be happy to just update the jira title if someone
really care) .

Side note: Imo adding feature in the naming here doesn't give anything  .
Prefixing by feature/ at least would optimize queries for rapid eye looking
or a bot going over the issues...


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

> that's naming is really awkward should be feature/431-cors  (why keeping
> caps?) . Anyway changing it again.
>
>
> On Thu, Nov 1, 2012 at 6:33 AM, Adam Kocoloski <kocolosk@apache.org>wrote:
>
>> 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
  • Unnamed multipart/alternative (inline, None, 0 bytes)
View raw message