Introduce vhosts configuration into CORS

In order to remove code duplication we move vhosts support
from couch_http_cors into chttpd_cors. We also dispatch
chttpd:send_response to couch_http which does call chttpd_cors:headers.
In order to avoid double injection of CORS headers we check for existance of
"Access-Control-Allow-Origin" in response headers.

COUCHDB-2945


Project: http://git-wip-us.apache.org/repos/asf/couchdb-couch-httpd/repo
Commit: 
http://git-wip-us.apache.org/repos/asf/couchdb-couch-httpd/commit/7e5ea869
Tree: http://git-wip-us.apache.org/repos/asf/couchdb-couch-httpd/tree/7e5ea869
Diff: http://git-wip-us.apache.org/repos/asf/couchdb-couch-httpd/diff/7e5ea869

Branch: refs/heads/split_out_httpd_stack
Commit: 7e5ea86950e7ccfc1cee17663e352a683c1da282
Parents: 265c82c
Author: ILYA Khlopotov <iil...@ca.ibm.com>
Authored: Tue Feb 9 13:22:05 2016 -0800
Committer: ILYA Khlopotov <iil...@ca.ibm.com>
Committed: Tue Mar 1 08:35:09 2016 -0800

----------------------------------------------------------------------
 src/couch_httpd_cors.erl | 67 ++++++++++++++++++++++++++++++++++---------
 1 file changed, 53 insertions(+), 14 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/couchdb-couch-httpd/blob/7e5ea869/src/couch_httpd_cors.erl
----------------------------------------------------------------------
diff --git a/src/couch_httpd_cors.erl b/src/couch_httpd_cors.erl
index 85669df..a1ebbd5 100644
--- a/src/couch_httpd_cors.erl
+++ b/src/couch_httpd_cors.erl
@@ -159,17 +159,24 @@ handle_preflight_request(Req, Config, Origin) ->
 
 
 headers(Req, RequestHeaders) ->
-    case get_origin(Req) of
-        undefined ->
-            %% If the Origin header is not present terminate
-            %% this set of steps. The request is outside the scope
-            %% of this specification.
-            %% http://www.w3.org/TR/cors/#resource-processing-model
-            RequestHeaders;
-        Origin ->
-            headers(Req, RequestHeaders, Origin, get_cors_config(Req))
+    case is_preflight_response(RequestHeaders) of
+        false ->
+            case get_origin(Req) of
+                undefined ->
+                    %% If the Origin header is not present terminate
+                    %% this set of steps. The request is outside the scope
+                    %% of this specification.
+                    %% http://www.w3.org/TR/cors/#resource-processing-model
+                    RequestHeaders;
+                Origin ->
+                    headers(Req, RequestHeaders, Origin, get_cors_config(Req))
+            end;
+        true ->
+            RequestHeaders
     end.
 
+is_preflight_response(Headers) ->
+    lists:keymember("Access-Control-Allow-Origin", 1, Headers).
 
 headers(_Req, RequestHeaders, undefined, _Config) ->
     RequestHeaders;
@@ -272,28 +279,31 @@ allow_credentials(Config, Origin) ->
     get_origin_config(Config, Origin, <<"allow_credentials">>,
         ?CORS_DEFAULT_ALLOW_CREDENTIALS).
 
-get_cors_config(#httpd{cors_config = undefined}) ->
+get_cors_config(#httpd{cors_config = undefined, mochi_req = MochiReq}) ->
+    Host = couch_httpd_vhost:host(MochiReq),
+
     EnableCors = config:get("httpd", "enable_cors", "false") =:= "true",
     AllowCredentials = config:get("cors", "credentials", "false") =:= "true",
-    AllowHeaders = case config:get("cors", "headers", undefined) of
+
+    AllowHeaders = case cors_config(Host, "headers", undefined) of
         undefined ->
             ?SUPPORTED_HEADERS;
         AllowHeaders0 ->
             [to_lower(H) || H <- split_list(AllowHeaders0)]
     end,
-    AllowMethods = case config:get("cors", "methods", undefined) of
+    AllowMethods = case cors_config(Host, "methods", undefined) of
         undefined ->
             ?SUPPORTED_METHODS;
         AllowMethods0 ->
             split_list(AllowMethods0)
     end,
-    ExposedHeaders = case config:get("cors", "exposed_headers", undefined) of
+    ExposedHeaders = case cors_config(Host, "exposed_headers", undefined) of
         undefined ->
             ?COUCH_HEADERS;
         ExposedHeaders0 ->
             [to_lower(H) || H <- split_list(ExposedHeaders0)]
     end,
-    Origins0 = binary_split_list(config:get("cors", "origins", [])),
+    Origins0 = binary_split_list(cors_config(Host, "origins", [])),
     Origins = [{O, {[]}} || O <- Origins0],
     [
         {<<"enable_cors">>, EnableCors},
@@ -306,6 +316,35 @@ get_cors_config(#httpd{cors_config = undefined}) ->
 get_cors_config(#httpd{cors_config = Config}) ->
     Config.
 
+cors_config(Host, Key, Default) ->
+    config:get(cors_section(Host), Key,
+                     config:get("cors", Key, Default)).
+
+cors_section(Host0) ->
+    {Host, _Port} = split_host_port(Host0),
+    "cors:" ++ Host.
+
+split_host_port(HostAsString) ->
+    % split at semicolon ":"
+    Split = string:rchr(HostAsString, $:),
+    split_host_port(HostAsString, Split).
+
+split_host_port(HostAsString, 0) ->
+    % no semicolon
+    {HostAsString, '*'};
+split_host_port(HostAsString, N) ->
+    HostPart = string:substr(HostAsString, 1, N-1),
+    % parse out port
+    % is there a nicer way?
+    case (catch erlang:list_to_integer(string:substr(HostAsString,
+                    N+1, length(HostAsString)))) of
+    {'EXIT', _} ->
+        {HostAsString, '*'};
+    Port ->
+        {HostPart, Port}
+    end.
+
+
 is_cors_enabled(Config) ->
     case get(disable_couch_httpd_cors) of
         undefined ->

Reply via email to