This is an automated email from the ASF dual-hosted git repository.
alexey pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/kudu.git
The following commit(s) were added to refs/heads/master by this push:
new e371d80 [util] add error buffer for EasyCurl wrapper
e371d80 is described below
commit e371d80f3e2ab171f704db198125ae8f8a860a6d
Author: Alexey Serbin <[email protected]>
AuthorDate: Thu Jan 23 18:01:23 2020 -0800
[util] add error buffer for EasyCurl wrapper
Enabled CURLOPT_ERRORBUFFER option for the EasyCurl utility wrapper.
This is to help collecting additional information on the error occurred,
if any.
Change-Id: I6e4a8a8a42671f141842ff6051aa18820a8f7f5a
Reviewed-on: http://gerrit.cloudera.org:8080/15101
Tested-by: Kudu Jenkins
Reviewed-by: Adar Dembo <[email protected]>
Reviewed-by: Volodymyr Verovkin <[email protected]>
---
src/kudu/util/curl_util.cc | 81 +++++++++++++++++++++++++++-------------------
src/kudu/util/curl_util.h | 12 ++++---
2 files changed, 56 insertions(+), 37 deletions(-)
diff --git a/src/kudu/util/curl_util.cc b/src/kudu/util/curl_util.cc
index aeb61dd..d75cc54 100644
--- a/src/kudu/util/curl_util.cc
+++ b/src/kudu/util/curl_util.cc
@@ -17,8 +17,8 @@
#include "kudu/util/curl_util.h"
-#include <cstddef>
#include <cstdint>
+#include <cstring>
#include <mutex>
#include <ostream>
#include <string>
@@ -34,19 +34,26 @@
using std::string;
using std::vector;
+using strings::Substitute;
namespace kudu {
namespace {
-inline Status TranslateError(CURLcode code) {
+inline Status TranslateError(CURLcode code, const char* errbuf) {
if (code == CURLE_OK) {
return Status::OK();
}
+
+ string err_msg = curl_easy_strerror(code);
+ if (strlen(errbuf) != 0) {
+ err_msg += Substitute(": $0", errbuf);
+ }
+
if (code == CURLE_OPERATION_TIMEDOUT) {
- return Status::TimedOut("curl timeout", curl_easy_strerror(code));
+ return Status::TimedOut("curl timeout", err_msg);
}
- return Status::NetworkError("curl error", curl_easy_strerror(code));
+ return Status::NetworkError("curl error", err_msg);
}
extern "C" {
@@ -60,6 +67,10 @@ size_t WriteCallback(void* buffer, size_t size, size_t
nmemb, void* user_ptr) {
} // anonymous namespace
+// This is an internal EasyCurl's utility macro.
+#define CURL_RETURN_NOT_OK(expr) \
+ RETURN_NOT_OK(TranslateError((expr), errbuf_))
+
EasyCurl::EasyCurl() {
// Use our own SSL initialization, and disable curl's.
// Both of these calls are idempotent.
@@ -73,6 +84,12 @@ EasyCurl::EasyCurl() {
});
curl_ = curl_easy_init();
CHECK(curl_) << "Could not init curl";
+
+ // Set the error buffer to enhance error messages with more details, when
+ // available.
+ static_assert(kErrBufSize >= CURL_ERROR_SIZE, "kErrBufSize is too small");
+ const auto code = curl_easy_setopt(curl_, CURLOPT_ERRORBUFFER, errbuf_);
+ CHECK_EQ(CURLE_OK, code);
}
EasyCurl::~EasyCurl() {
@@ -96,25 +113,24 @@ Status EasyCurl::DoRequest(const string& url,
const vector<string>& headers) {
CHECK_NOTNULL(dst)->clear();
+ // Mark the error buffer as cleared.
+ errbuf_[0] = 0;
+
if (!verify_peer_) {
- RETURN_NOT_OK(TranslateError(curl_easy_setopt(
- curl_, CURLOPT_SSL_VERIFYHOST, 0)));
- RETURN_NOT_OK(TranslateError(curl_easy_setopt(
- curl_, CURLOPT_SSL_VERIFYPEER, 0)));
+ CURL_RETURN_NOT_OK(curl_easy_setopt(curl_, CURLOPT_SSL_VERIFYHOST, 0));
+ CURL_RETURN_NOT_OK(curl_easy_setopt(curl_, CURLOPT_SSL_VERIFYPEER, 0));
}
if (use_spnego_) {
- RETURN_NOT_OK(TranslateError(curl_easy_setopt(
- curl_, CURLOPT_HTTPAUTH, CURLAUTH_NEGOTIATE)));
+ CURL_RETURN_NOT_OK(curl_easy_setopt(
+ curl_, CURLOPT_HTTPAUTH, CURLAUTH_NEGOTIATE));
// It's necessary to pass an empty user/password to trigger the
authentication
// code paths in curl, even though SPNEGO doesn't use them.
- RETURN_NOT_OK(TranslateError(curl_easy_setopt(
- curl_, CURLOPT_USERPWD, ":")));
+ CURL_RETURN_NOT_OK(curl_easy_setopt(curl_, CURLOPT_USERPWD, ":"));
}
if (verbose_) {
- RETURN_NOT_OK(TranslateError(curl_easy_setopt(
- curl_, CURLOPT_VERBOSE, 1)));
+ CURL_RETURN_NOT_OK(curl_easy_setopt(curl_, CURLOPT_VERBOSE, 1));
}
// Add headers if specified.
@@ -126,18 +142,17 @@ Status EasyCurl::DoRequest(const string& url,
for (const auto& header : headers) {
curl_headers = CHECK_NOTNULL(curl_slist_append(curl_headers,
header.c_str()));
}
- RETURN_NOT_OK(TranslateError(curl_easy_setopt(curl_, CURLOPT_HTTPHEADER,
curl_headers)));
+ CURL_RETURN_NOT_OK(curl_easy_setopt(curl_, CURLOPT_HTTPHEADER,
curl_headers));
- RETURN_NOT_OK(TranslateError(curl_easy_setopt(curl_, CURLOPT_URL,
url.c_str())));
+ CURL_RETURN_NOT_OK(curl_easy_setopt(curl_, CURLOPT_URL, url.c_str()));
if (return_headers_) {
- RETURN_NOT_OK(TranslateError(curl_easy_setopt(curl_, CURLOPT_HEADER, 1)));
+ CURL_RETURN_NOT_OK(curl_easy_setopt(curl_, CURLOPT_HEADER, 1));
}
- RETURN_NOT_OK(TranslateError(curl_easy_setopt(curl_, CURLOPT_WRITEFUNCTION,
WriteCallback)));
- RETURN_NOT_OK(TranslateError(curl_easy_setopt(curl_, CURLOPT_WRITEDATA,
- static_cast<void *>(dst))));
+ CURL_RETURN_NOT_OK(curl_easy_setopt(curl_, CURLOPT_WRITEFUNCTION,
WriteCallback));
+ CURL_RETURN_NOT_OK(curl_easy_setopt(curl_, CURLOPT_WRITEDATA,
static_cast<void *>(dst)));
if (post_data) {
- RETURN_NOT_OK(TranslateError(curl_easy_setopt(curl_, CURLOPT_POSTFIELDS,
- post_data->c_str())));
+ CURL_RETURN_NOT_OK(curl_easy_setopt(
+ curl_, CURLOPT_POSTFIELDS, post_data->c_str()));
}
// Done after CURLOPT_POSTFIELDS in case that resets the method (the docs[1]
@@ -145,24 +160,24 @@ Status EasyCurl::DoRequest(const string& url,
//
// 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())));
+ CURL_RETURN_NOT_OK(curl_easy_setopt(
+ curl_, CURLOPT_CUSTOMREQUEST, custom_method_.c_str()));
}
- RETURN_NOT_OK(TranslateError(curl_easy_setopt(curl_, CURLOPT_HTTPAUTH,
CURLAUTH_ANY)));
+ CURL_RETURN_NOT_OK(curl_easy_setopt(curl_, CURLOPT_HTTPAUTH, CURLAUTH_ANY));
if (timeout_.Initialized()) {
- RETURN_NOT_OK(TranslateError(curl_easy_setopt(curl_, CURLOPT_NOSIGNAL,
1)));
- RETURN_NOT_OK(TranslateError(curl_easy_setopt(curl_, CURLOPT_TIMEOUT_MS,
- timeout_.ToMilliseconds())));
+ CURL_RETURN_NOT_OK(curl_easy_setopt(curl_, CURLOPT_NOSIGNAL, 1));
+ CURL_RETURN_NOT_OK(curl_easy_setopt(
+ curl_, CURLOPT_TIMEOUT_MS, timeout_.ToMilliseconds()));
}
- RETURN_NOT_OK(TranslateError(curl_easy_perform(curl_)));
+ CURL_RETURN_NOT_OK(curl_easy_perform(curl_));
long val; // NOLINT(*) curl wants a long
- RETURN_NOT_OK(TranslateError(curl_easy_getinfo(curl_, CURLINFO_NUM_CONNECTS,
&val)));
- num_connects_ = val;
+ CURL_RETURN_NOT_OK(curl_easy_getinfo(curl_, CURLINFO_NUM_CONNECTS, &val));
+ num_connects_ = static_cast<int>(val);
- RETURN_NOT_OK(TranslateError(curl_easy_getinfo(curl_,
CURLINFO_RESPONSE_CODE, &val)));
+ CURL_RETURN_NOT_OK(curl_easy_getinfo(curl_, CURLINFO_RESPONSE_CODE, &val));
if (val != 200) {
- return Status::RemoteError(strings::Substitute("HTTP $0", val));
+ return Status::RemoteError(Substitute("HTTP $0", val));
}
return Status::OK();
}
diff --git a/src/kudu/util/curl_util.h b/src/kudu/util/curl_util.h
index 2289e84..4ed07a0 100644
--- a/src/kudu/util/curl_util.h
+++ b/src/kudu/util/curl_util.h
@@ -14,9 +14,10 @@
// KIND, either express or implied. See the License for the
// specific language governing permissions and limitations
// under the License.
-#ifndef KUDU_UTIL_CURL_UTIL_H
-#define KUDU_UTIL_CURL_UTIL_H
+#pragma once
+
+#include <cstddef>
#include <string>
#include <utility>
#include <vector>
@@ -89,12 +90,15 @@ class EasyCurl {
}
private:
+ static const constexpr size_t kErrBufSize = 256;
+
// Do a request. If 'post_data' is non-NULL, does a POST.
// Otherwise, does a GET.
Status DoRequest(const std::string& url,
const std::string* post_data,
faststring* dst,
const std::vector<std::string>& headers = {});
+
CURL* curl_;
std::string custom_method_;
@@ -113,9 +117,9 @@ class EasyCurl {
int num_connects_ = 0;
+ char errbuf_[kErrBufSize];
+
DISALLOW_COPY_AND_ASSIGN(EasyCurl);
};
} // namespace kudu
-
-#endif /* KUDU_UTIL_CURL_UTIL_H */