You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@kudu.apache.org by al...@apache.org on 2019/10/17 23:02:35 UTC

[kudu] branch branch-1.11.x updated: webserver: support HTTP OPTIONS requests

This is an automated email from the ASF dual-hosted git repository.

alexey pushed a commit to branch branch-1.11.x
in repository https://gitbox.apache.org/repos/asf/kudu.git


The following commit(s) were added to refs/heads/branch-1.11.x by this push:
     new fa5c92a  webserver: support HTTP OPTIONS requests
fa5c92a is described below

commit fa5c92a18e1e37a3e29bb99bcb024f55d9a37cfd
Author: Adar Dembo <ad...@cloudera.com>
AuthorDate: Mon Oct 14 18:14:28 2019 -0700

    webserver: support HTTP OPTIONS requests
    
    This is a port of IMPALA-8982 to Kudu, including:
    - Updating squeasel to a newer revision that handles OPTIONS rather than
      responding with an HTTP 500 error code.
    - Modifying the webserver to let squeasel handle OPTIONS, bypassing SPNEGO
      authentication.
    
    Change-Id: Icc2328d41ebb30c9ab1b25cb28fcc7e56cc8a25b
    Reviewed-on: http://gerrit.cloudera.org:8080/14439
    Reviewed-by: Alexey Serbin <as...@cloudera.com>
    Tested-by: Adar Dembo <ad...@cloudera.com>
    Reviewed-by: Thomas Tauber-Marshall <tm...@cloudera.com>
    (cherry picked from commit d6a6b54b175032c7d13d231ef6d156ef9d0e3ab0)
    Reviewed-on: http://gerrit.cloudera.org:8080/14475
    Tested-by: Alexey Serbin <as...@cloudera.com>
    Reviewed-by: Adar Dembo <ad...@cloudera.com>
---
 src/kudu/server/webserver-test.cc | 19 +++++++++++++++++++
 src/kudu/server/webserver.cc      |  6 ++++++
 src/kudu/util/curl_util.cc        | 28 +++++++++++++++++++++-------
 src/kudu/util/curl_util.h         |  8 ++++++++
 thirdparty/vars.sh                |  2 +-
 5 files changed, 55 insertions(+), 8 deletions(-)

diff --git a/src/kudu/server/webserver-test.cc b/src/kudu/server/webserver-test.cc
index cb6253f..82ee16b 100644
--- a/src/kudu/server/webserver-test.cc
+++ b/src/kudu/server/webserver-test.cc
@@ -110,6 +110,14 @@ class WebserverTest : public KuduTest {
     url_ = Substitute("http://$0", addr_.ToString());
   }
 
+  void RunTestOptions() {
+    curl_.set_custom_method("OPTIONS");
+    curl_.set_return_headers(true);
+    ASSERT_OK(curl_.FetchURL(url_, &buf_));
+    ASSERT_STR_CONTAINS(buf_.ToString(),
+                        "Allow: GET, POST, HEAD, OPTIONS, PROPFIND, MKCOL");
+  }
+
  protected:
   virtual void MaybeSetupSpnego(WebserverOptions* /*opts*/) {}
 
@@ -285,6 +293,12 @@ TEST_F(SpnegoWebserverTest, TestTruncatedTokens) {
   } while (!token.empty());
 }
 
+// Tests that even if we don't provide adequate authentication information in
+// an OPTIONS request, the server still honors it.
+TEST_F(SpnegoWebserverTest, TestAuthNotRequiredForOptions) {
+  NO_FATALS(RunTestOptions());
+}
+
 TEST_F(WebserverTest, TestIndexPage) {
   curl_.set_return_headers(true);
   ASSERT_OK(curl_.FetchURL(url_, &buf_));
@@ -449,6 +463,11 @@ TEST_F(WebserverTest, TestStaticFiles) {
   ASSERT_EQ("Remote error: HTTP 403", s.ToString());
 }
 
+// Test that HTTP OPTIONS requests are permitted.
+TEST_F(WebserverTest, TestHttpOptions) {
+  NO_FATALS(RunTestOptions());
+}
+
 class WebserverAdvertisedAddressesTest : public KuduTest {
  public:
   void SetUp() override {
diff --git a/src/kudu/server/webserver.cc b/src/kudu/server/webserver.cc
index 7e34cd5..5efcf1d 100644
--- a/src/kudu/server/webserver.cc
+++ b/src/kudu/server/webserver.cc
@@ -459,6 +459,12 @@ sq_callback_result_t Webserver::BeginRequestCallbackStatic(
 sq_callback_result_t Webserver::BeginRequestCallback(
     struct sq_connection* connection,
     struct sq_request_info* request_info) {
+  if (strncmp("OPTIONS", request_info->request_method, 7) == 0) {
+    // Let Squeasel deal with the request. OPTIONS requests should not require
+    // authentication, so do this before doing SPNEGO.
+    return SQ_CONTINUE_HANDLING;
+  }
+
   if (opts_.require_spnego) {
     const char* authz_header = sq_get_header(connection, "Authorization");
     string resp_header, authn_princ;
diff --git a/src/kudu/util/curl_util.cc b/src/kudu/util/curl_util.cc
index e80d481..14e247c 100644
--- a/src/kudu/util/curl_util.cc
+++ b/src/kudu/util/curl_util.cc
@@ -21,6 +21,8 @@
 #include <cstdint>
 #include <mutex>
 #include <ostream>
+#include <string>
+#include <vector>
 
 #include <curl/curl.h>
 #include <glog/logging.h>
@@ -30,6 +32,9 @@
 #include "kudu/util/faststring.h"
 #include "kudu/util/scoped_cleanup.h"
 
+using std::string;
+using std::vector;
+
 namespace kudu {
 
 namespace {
@@ -74,21 +79,21 @@ EasyCurl::~EasyCurl() {
   curl_easy_cleanup(curl_);
 }
 
-Status EasyCurl::FetchURL(const std::string& url, faststring* dst,
-                          const std::vector<std::string>& headers) {
+Status EasyCurl::FetchURL(const string& url, faststring* dst,
+                          const vector<string>& headers) {
   return DoRequest(url, nullptr, dst, headers);
 }
 
-Status EasyCurl::PostToURL(const std::string& url,
-                           const std::string& post_data,
+Status EasyCurl::PostToURL(const string& url,
+                           const string& post_data,
                            faststring* dst) {
   return DoRequest(url, &post_data, dst);
 }
 
-Status EasyCurl::DoRequest(const std::string& url,
-                           const std::string* post_data,
+Status EasyCurl::DoRequest(const string& url,
+                           const string* post_data,
                            faststring* dst,
-                           const std::vector<std::string>& headers) {
+                           const vector<string>& headers) {
   CHECK_NOTNULL(dst)->clear();
 
   if (!verify_peer_) {
@@ -135,6 +140,15 @@ Status EasyCurl::DoRequest(const std::string& url,
                                                   post_data->c_str())));
   }
 
+  // Done after CURLOPT_POSTFIELDS in case that resets the method (the docs[1]
+  // are unclear on whether that happens).
+  //
+  // 1. https://curl.haxx.se/libcurl/c/CURLOPT_POSTFIELDS.html
+  if (!custom_method_.empty()) {
+    RETURN_NOT_OK(TranslateError(curl_easy_setopt(curl_, CURLOPT_CUSTOMREQUEST,
+                                                  custom_method_.c_str())));
+  }
+
   RETURN_NOT_OK(TranslateError(curl_easy_setopt(curl_, CURLOPT_HTTPAUTH, CURLAUTH_ANY)));
   if (timeout_.Initialized()) {
     RETURN_NOT_OK(TranslateError(curl_easy_setopt(curl_, CURLOPT_NOSIGNAL, 1)));
diff --git a/src/kudu/util/curl_util.h b/src/kudu/util/curl_util.h
index 6b4ea9e..86f01da 100644
--- a/src/kudu/util/curl_util.h
+++ b/src/kudu/util/curl_util.h
@@ -18,6 +18,7 @@
 #define KUDU_UTIL_CURL_UTIL_H
 
 #include <string>
+#include <utility>
 #include <vector>
 
 #include "kudu/gutil/macros.h"
@@ -77,6 +78,11 @@ class EasyCurl {
     verbose_ = v;
   }
 
+  // Overrides curl's HTTP method handling with a custom method string.
+  void set_custom_method(std::string m) {
+    custom_method_ = std::move(m);
+  }
+
  private:
   // Do a request. If 'post_data' is non-NULL, does a POST.
   // Otherwise, does a GET.
@@ -86,6 +92,8 @@ class EasyCurl {
                    const std::vector<std::string>& headers = {});
   CURL* curl_;
 
+  std::string custom_method_;
+
   // Whether to verify the server certificate.
   bool verify_peer_ = true;
 
diff --git a/thirdparty/vars.sh b/thirdparty/vars.sh
index 1c386c0..1b007f7 100644
--- a/thirdparty/vars.sh
+++ b/thirdparty/vars.sh
@@ -97,7 +97,7 @@ RAPIDJSON_SOURCE=$TP_SOURCE_DIR/$RAPIDJSON_NAME
 #  export NAME=squeasel-$(git rev-parse HEAD)
 #  git archive HEAD --prefix=$NAME/ -o /tmp/$NAME.tar.gz
 #  s3cmd put -P /tmp/$NAME.tar.gz s3://cloudera-thirdparty-libs/$NAME.tar.gz
-SQUEASEL_VERSION=7973705170f4744d1806e32695f7ea1e8308ee95
+SQUEASEL_VERSION=36dc66b8723980feb32196a94b46734eb5eafbf9
 SQUEASEL_NAME=squeasel-$SQUEASEL_VERSION
 SQUEASEL_SOURCE=$TP_SOURCE_DIR/$SQUEASEL_NAME