From 898102237e416d900dc49223227b96f897d5a7ae Mon Sep 17 00:00:00 2001 From: Fujimoto Seiji Date: Fri, 5 Oct 2018 14:45:04 +0900 Subject: [PATCH] in_http: Implement support for CORS preflight requests To receive a 'application/json' request from another origin, we need to be able to handle CORS preflight requests. Otherwise, browsers will refuse to interact with us. https://developer.mozilla.org/en-US/docs/Web/HTTP/CORS#Preflighted_requests This patch adds support for the feature. Signed-off-by: Fujimoto Seiji --- lib/fluent/plugin/in_http.rb | 38 ++++++++++++++++++++++++++++++++++++ test/plugin/test_in_http.rb | 23 ++++++++++++++++++++++ 2 files changed, 61 insertions(+) diff --git a/lib/fluent/plugin/in_http.rb b/lib/fluent/plugin/in_http.rb index 23431334ef..e04a782e7a 100644 --- a/lib/fluent/plugin/in_http.rb +++ b/lib/fluent/plugin/in_http.rb @@ -342,6 +342,10 @@ def on_headers_complete(headers) # For multiple X-Forwarded-For headers. Use first header value. v = v.first if v.is_a?(Array) @remote_addr = v.split(",").first + when /Access-Control-Request-Method/i + @access_control_request_method = v + when /Access-Control-Request-Headers/i + @access_control_request_headers = v end } if expect @@ -367,9 +371,43 @@ def on_body(chunk) @body << chunk end + # Web browsers can send an OPTIONS request before performing POST + # to check if cross-origin requests are supported. + def handle_options_request + # Is CORS enabled in the first place? + if @cors_allow_origins.nil? + return send_response_and_close("403 Forbidden", {}, "") + end + + # in_http does not support HTTP methods except POST + if @access_control_request_method != 'POST' + return send_response_and_close("403 Forbidden", {}, "") + end + + header = { + "Access-Control-Allow-Methods": "POST", + "Access-Control-Allow-Headers": @access_control_request_headers || "", + } + + # Check the origin and send back a CORS response + if @cors_allow_origins.include?('*') + header["Access-Control-Allow-Origin"] = "*" + send_response_and_close("200 OK", header, "") + elsif @cors_allow_origins.include?(@origin) + header["Access-Control-Allow-Origin"] = @origin + send_response_and_close("200 OK", header, "") + else + send_response_and_close("403 Forbidden", {}, "") + end + end + def on_message_complete return if closing? + if @parser.http_method == 'OPTIONS' + return handle_options_request() + end + # CORS check # ========== # For every incoming request, we check if we have some CORS diff --git a/test/plugin/test_in_http.rb b/test/plugin/test_in_http.rb index ea837abdb0..951e0dc8f0 100644 --- a/test/plugin/test_in_http.rb +++ b/test/plugin/test_in_http.rb @@ -624,6 +624,23 @@ def test_cors_allowed_wildcard end end + def test_cors_preflight + d = create_driver(CONFIG + 'cors_allow_origins ["*"]') + + d.run do + header = { + "Origin" => "http://foo.com", + "Access-Control-Request-Method" => "POST", + "Access-Control-Request-Headers" => "Content-Type", + } + res = options("/cors.test", {}, header) + + assert_equal "200", res.code + assert_equal "*", res["Access-Control-Allow-Origin"] + assert_equal "POST", res["Access-Control-Allow-Methods"] + end + end + def test_content_encoding_gzip d = create_driver @@ -744,6 +761,12 @@ def test_if_content_type_is_initialized_properly assert_equal $test_in_http_connection_object_ids[0], $test_in_http_connection_object_ids[1] end + def options(path, params, header = {}) + http = Net::HTTP.new("127.0.0.1", PORT) + req = Net::HTTP::Options.new(path, header) + http.request(req) + end + def post(path, params, header = {}, &block) http = Net::HTTP.new("127.0.0.1", PORT) req = Net::HTTP::Post.new(path, header)