diff --git a/.gitignore b/.gitignore index 5c8ed763b8332f..aca7dab2b72e3f 100644 --- a/.gitignore +++ b/.gitignore @@ -8,3 +8,5 @@ gensrc/build fe/target thirdparty/src *.so.tmp +.DS_Store +*.iml diff --git a/be/src/http/http_client.cpp b/be/src/http/http_client.cpp index 6e93a02ffdf072..88c4374fe6f618 100644 --- a/be/src/http/http_client.cpp +++ b/be/src/http/http_client.cpp @@ -27,6 +27,10 @@ HttpClient::~HttpClient() { curl_easy_cleanup(_curl); _curl = nullptr; } + if(_header_list != nullptr) { + curl_slist_free_all(_header_list); + _header_list = nullptr; + } } Status HttpClient::init(const std::string& url) { @@ -39,6 +43,10 @@ Status HttpClient::init(const std::string& url) { curl_easy_reset(_curl); } + if(_header_list != nullptr) { + curl_slist_free_all(_header_list); + _header_list = nullptr; + } // set error_buf _error_buf[0] = 0; auto code = curl_easy_setopt(_curl, CURLOPT_ERRORBUFFER, _error_buf); @@ -131,6 +139,18 @@ size_t HttpClient::on_response_data(const void* data, size_t length) { return length; } +// Status HttpClient::execute_post_request(const std::string& post_data, const std::function& callback = {}) { +// _callback = &callback; +// set_post_body(post_data); +// return execute(callback); +// } + +Status HttpClient::execute_post_request(const std::string& post_data, std::string* response) { + set_method(POST); + set_post_body(post_data); + return execute(response); +} + Status HttpClient::execute(const std::function& callback) { _callback = &callback; auto code = curl_easy_perform(_curl); diff --git a/be/src/http/http_client.h b/be/src/http/http_client.h index 4f8c29cfb7a117..83a27b8d63646f 100644 --- a/be/src/http/http_client.h +++ b/be/src/http/http_client.h @@ -26,7 +26,7 @@ #include "http/http_headers.h" #include "http/http_method.h" #include "http/utils.h" - +#include "http/http_response.h" namespace doris { // Helper class to access HTTP resource @@ -54,6 +54,19 @@ class HttpClient { curl_easy_setopt(_curl, CURLOPT_PASSWORD, passwd.c_str()); } + // content_type such as "application/json" + void set_content_type(const std::string content_type) { + std::string scratch_str = "Content-Type: " + content_type; + _header_list = curl_slist_append(_header_list, scratch_str.c_str()); + curl_easy_setopt(_curl, CURLOPT_HTTPHEADER, _header_list); + } + + // you must set CURLOPT_POSTFIELDSIZE before CURLOPT_COPYPOSTFIELDS options, otherwise will cause request hanging up + void set_post_body(const std::string& post_body) { + curl_easy_setopt(_curl, CURLOPT_POSTFIELDSIZE, (long)post_body.length()); + curl_easy_setopt(_curl, CURLOPT_COPYPOSTFIELDS, post_body.c_str()); + } + // TODO(zc): support set header // void set_header(const std::string& key, const std::string& value) { // _cntl.http_request().SetHeader(key, value); @@ -85,6 +98,12 @@ class HttpClient { return cl; } + long get_http_status() const { + long code; + curl_easy_getinfo(_curl, CURLINFO_RESPONSE_CODE, &code); + return code; + } + // execute a head method Status head() { set_method(HEAD); @@ -95,6 +114,8 @@ class HttpClient { // a file to local_path Status download(const std::string& local_path); + Status execute_post_request(const std::string& post_data, std::string* response); + // execute a simple method, and its response is saved in response argument Status execute(std::string* response); @@ -111,6 +132,7 @@ class HttpClient { using HttpCallback = std::function; const HttpCallback* _callback = nullptr; char _error_buf[CURL_ERROR_SIZE]; + curl_slist *_header_list = nullptr; }; } diff --git a/be/test/http/http_client_test.cpp b/be/test/http/http_client_test.cpp index f33c3060744635..343c60614466a8 100644 --- a/be/test/http/http_client_test.cpp +++ b/be/test/http/http_client_test.cpp @@ -47,7 +47,28 @@ class HttpClientTestSimpleGetHandler : public HttpHandler { } }; +class HttpClientTestSimplePostHandler : public HttpHandler { +public: + void handle(HttpRequest* req) override { + std::string user; + std::string passwd; + if (!parse_basic_auth(*req, &user, &passwd) || user != "test1") { + HttpChannel::send_basic_challenge(req, "abc"); + return; + } + if (req->method() == HttpMethod::POST) { + std::string post_body = req->get_request_body(); + if (!post_body.empty()) { + HttpChannel::send_reply(req, post_body); + } else { + HttpChannel::send_reply(req, "empty"); + } + } + } +}; + static HttpClientTestSimpleGetHandler s_simple_get_handler = HttpClientTestSimpleGetHandler(); +static HttpClientTestSimplePostHandler s_simple_post_handler = HttpClientTestSimplePostHandler(); static EvHttpServer* s_server = nullptr; class HttpClientTest : public testing::Test { @@ -59,6 +80,7 @@ class HttpClientTest : public testing::Test { s_server = new EvHttpServer(29386); s_server->register_handler(GET, "/simple_get", &s_simple_get_handler); s_server->register_handler(HEAD, "/simple_get", &s_simple_get_handler); + s_server->register_handler(POST, "/simple_post", &s_simple_post_handler); s_server->start(); } @@ -109,9 +131,24 @@ TEST_F(HttpClientTest, get_failed) { auto st = client.init("http://127.0.0.1:29386/simple_get"); ASSERT_TRUE(st.ok()); client.set_method(GET); + client.set_basic_auth("test1", ""); std::string response; st = client.execute(&response); - ASSERT_FALSE(st.ok()); + ASSERT_FALSE(!st.ok()); +} + +TEST_F(HttpClientTest, post_normal) { + HttpClient client; + auto st = client.init("http://127.0.0.1:29386/simple_post"); + ASSERT_TRUE(st.ok()); + client.set_method(POST); + client.set_basic_auth("test1", ""); + std::string response; + std::string request_body = "simple post body query"; + st = client.execute_post_request(request_body, &response); + ASSERT_TRUE(st.ok()); + ASSERT_EQ(response.length(), request_body.length()); + ASSERT_STREQ(response.c_str(), request_body.c_str()); } }