From 2222193de8225fce45efd35a20238ce8a57ef23d Mon Sep 17 00:00:00 2001 From: Lalit Kumar Bhasin Date: Sat, 31 Oct 2020 23:12:34 +0530 Subject: [PATCH 01/32] initial commit --- .../common/http/curl_http_operation.h | 511 ++++++++++++++++++ .../exporters/common/http/http_client_curl.h | 242 +++++++++ .../common/http/http_client_httplib.h | 315 +++++++++++ .../exporters/common/http/main.cc | 6 + .../opentelemetry/sdk/common/http_client.h | 29 +- sdk/include/opentelemetry/sdk/common/main.cc | 3 + 6 files changed, 1092 insertions(+), 14 deletions(-) create mode 100644 exporters/include/opentelemtry/exporters/common/http/curl_http_operation.h create mode 100644 exporters/include/opentelemtry/exporters/common/http/http_client_curl.h create mode 100644 exporters/include/opentelemtry/exporters/common/http/http_client_httplib.h create mode 100644 exporters/include/opentelemtry/exporters/common/http/main.cc create mode 100644 sdk/include/opentelemetry/sdk/common/main.cc diff --git a/exporters/include/opentelemtry/exporters/common/http/curl_http_operation.h b/exporters/include/opentelemtry/exporters/common/http/curl_http_operation.h new file mode 100644 index 0000000000..bcf1f52bf5 --- /dev/null +++ b/exporters/include/opentelemtry/exporters/common/http/curl_http_operation.h @@ -0,0 +1,511 @@ +#pragma once + +#include "opentelemetry/sdk/common/http_client.h" + +#include +#include +#include +#include +#include +#include +#include +#ifdef _WIN32 +#include +#else +#include +#endif + + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace exporters +{ +namespace common +{ +namespace http +{ +namespace curl +{ + namespace http_sdk = opentelemetry::sdk::common::http; + const size_t default_http_conn_timeout = 5; + const std::string http_status_regexp = "HTTP\\/\\d\\.\\d (\\d+)\\ .*" ; + const std::string http_header_regexp = "(.*)\\: (.*)\\n*"; + + struct curl_ci { + bool operator() (const std::string & s1, const std::string & s2) const { + return std::lexicographical_compare( + s1.begin(), s1.end(), + s2.begin(), s2.end(), + [](char c1, char c2) { + return ::tolower(c1) < ::tolower(c2); + } + ); + } + }; + using Headers = std::multimap; + + +class HttpOperation { +public: + + void DispatchEvent(http_sdk::SessionState type, std::string reason = "") + { + if(callback_ != nullptr) + callback_->OnEvent(type, reason); + } + + std::atomic is_aborted_; // Set to 'true' when async callback is aborted + + /** + * Create local CURL instance for url and body + * + * @param url + * @param body + * @param httpConnTimeout HTTP connection timeout in seconds + * @param httpReadTimeout HTTP read timeout in seconds + */ + HttpOperation( + http_sdk::Method method, + std::string url, + http_sdk::EventHandler* callback, + // Default empty headers and empty request body + const Headers& request_headers = Headers(), + const http_sdk::Body& request_body = http_sdk::Body(), + // Default connectivity and response size options + bool raw_response = false, + size_t http_conn_timeout = default_http_conn_timeout) : + + // + method_(method), + url_(url), + callback_(callback), + + // Local vars + request_headers_(request_headers), + request_body_(request_body), + // Optional connection params + raw_response_(raw_response), + http_conn_timeout_(http_conn_timeout), + // Result + res_(CURLE_OK), + sockfd_(0), + is_aborted_(false), + nread_(0) + { + response_.memory = nullptr; + response_.size = 0; + + /* get a curl handle */ + curl_ = curl_easy_init(); + if(!curl_) + { + res_ = CURLE_FAILED_INIT; + DispatchEvent(http_sdk::SessionState::CreateFailed); + return; + } + + curl_easy_setopt(curl_, CURLOPT_VERBOSE, 0); + + // Specify target URL + curl_easy_setopt(curl_, CURLOPT_URL, url_.c_str()); + + // TODO: support ssl cert verification for https request + curl_easy_setopt(curl_, CURLOPT_SSL_VERIFYPEER, 0); // 1L + curl_easy_setopt(curl_, CURLOPT_SSL_VERIFYHOST, 0); // 2L + + // Specify our custom headers + for(auto &kv : this->request_headers_) + { + std::string header = kv.first(); + header += ": "; + header += kv.second(); + headers_chunk_ = curl_slist_append(headers_chunk_, header());; + } + + if(headers_chunk_ != nullptr) + { + curl_easy_setopt(curl_, CURLOPT_HTTPHEADER, headers_chunk_); + } + + DispatchEvent(http_sdk::SessionState::Created); + } + + /** + * Destroy CURL instance + */ + virtual ~HttpOperation() + { + // Given the request has not been aborted we should wait for completion here + // This guarantees the lifetime of this request. + if (result_.valid()) + { + result_.wait(); + } + DispatchEvent(http_sdk::SessionState::Destroy); + res_ = CURLE_OK; + curl_easy_cleanup(curl_); + curl_slist_free_all(headers_chunk_); + ReleaseResponse(); + } + + /** + * Finish CURL instance + */ + virtual void Finish() + { + if (result_.valid()) + { + result_.wait(); + } + } + + /** + * Send request synchronously + */ + long Send() + { + ReleaseResponse(); + // Request buffer + const void *request = (request_body_.empty())? NULL: &request_body_[0]; + const size_t req_size = request_body_.size(); + + if(!curl_) + { + res_ = CURLE_FAILED_INIT; + DispatchEvent(http_sdk::SessionState::SendFailed); + return res_; + } + + // TODO: control local port to use + // curl_easy_setopt(curl, CURLOPT_LOCALPORT, dcf_port); + + // Perform initial connect, handling the timeout if needed + curl_easy_setopt(curl_, CURLOPT_CONNECT_ONLY, 1L); + DispatchEvent(http_sdk::SessionState::Connecting); + res_ = curl_easy_perform(curl_); + if(CURLE_OK != res_) + { + DispatchEvent(http_sdk::SessionState::ConnectFailed, curl_easy_strerror(res_)); // couldn't connect - stage 1 + return res_; + } + + /* Extract the socket from the curl handle - we'll need it for waiting. + * Note that this API takes a pointer to a 'long' while we use + * curl_socket_t for sockets otherwise. + */ + long sockextr = 0 ; + res_ = curl_easy_getinfo(curl_, CURLINFO_LASTSOCKET, &sockextr); + if(CURLE_OK != res_) + { + DispatchEvent(http_sdk::SessionState::ConnectFailed, curl_easy_strerror(res_)); // couldn't connect - stage 2 + return res_; + } + + /* wait for the socket to become ready for sending */ + sockfd_ = sockextr; + if( !WaitOnSocket(sockfd_, 0, http_conn_timeout_ * 1000L) || is_aborted_) + { + res_ = CURLE_OPERATION_TIMEDOUT; + DispatchEvent(http_sdk::SessionState::ConnectFailed, " Is aborted: " + std::to_string(is_aborted_.load())); // couldn't connect - stage 3 + return res_; + } + + DispatchEvent(http_sdk::SessionState::Connected); + // once connection is there - switch back to easy perform for HTTP post + curl_easy_setopt(curl_, CURLOPT_CONNECT_ONLY, 0); + + // send all data to our callback function + if (raw_response_) + { + curl_easy_setopt(curl_, CURLOPT_HEADER, true); + curl_easy_setopt(curl_, CURLOPT_WRITEFUNCTION, (void *)&WriteMemoryCallback); + curl_easy_setopt(curl_, CURLOPT_WRITEDATA, (void *)&response_); + } else { + curl_easy_setopt(curl_, CURLOPT_WRITEFUNCTION, (void *)&WriteVectorCallback); + curl_easy_setopt(curl_, CURLOPT_HEADERDATA, (void *)&resp_headers_); + curl_easy_setopt(curl_, CURLOPT_WRITEDATA, (void *)&resp_body_); + } + + // TODO: only two methods supported for now - POST and GET + if (method_ == http_sdk::Method::Post) + { + // POST + curl_easy_setopt(curl_, CURLOPT_POST, true); + curl_easy_setopt(curl_, CURLOPT_POSTFIELDS, (const char *)request); + curl_easy_setopt(curl_, CURLOPT_POSTFIELDSIZE, req_size); + } else + if (method_ == http_sdk::Method::Get) + { + // GET + } else + { + res_ = CURLE_UNSUPPORTED_PROTOCOL; + return res_; + } + /* abort if slower than 4kb/sec during 30 seconds */ + curl_easy_setopt(curl_, CURLOPT_LOW_SPEED_TIME, 30L); + curl_easy_setopt(curl_, CURLOPT_LOW_SPEED_LIMIT, 4096); + DispatchEvent(http_sdk::SessionState::Sending); + res_ = curl_easy_perform(curl_); + if(CURLE_OK != res_) + { + DispatchEvent(http_sdk::SessionState::SendFailed, curl_easy_strerror(res_)); + return res_; + } + + /* Code snippet to parse raw HTTP response. This might come in handy + * if we ever consider to handle the raw upload instead of curl_easy_perform + ... + std::string resp((const char *)response); + std::regex http_status_regex(HTTP_STATUS_REGEXP); + std::smatch match; + if(std::regex_search(resp, match, http_status_regex)) + http_code = std::stol(match[1]); + ... + */ + + /* libcurl is nice enough to parse the http response code itself: */ + curl_easy_getinfo(curl_, CURLINFO_RESPONSE_CODE, &res_); + // We got some response from server. Dump the contents. + DispatchEvent(http_sdk::SessionState::Response); + + // This function returns: + // - on success: HTTP status code. + // - on failure: CURL error code. + // The two sets of enums (CURLE, HTTP codes) - do not intersect, so we collapse them in one set. + return res_; + } + + std::future & SendAsync(std::function callback = nullptr) { + result_ = std::async(std::launch::async, [this, callback] { + long result = Send(); + if (callback!=nullptr) + callback(*this); + return result; + }); + return result_; + } + + /** + * Get HTTP response code. This function returns CURL error code if HTTP response code is invalid. + */ + long GetResponseCode() + { + return res_; + } + + /** + * Get whether or not response was programmatically aborted + */ + bool WasAborted() + { + return is_aborted_.load(); + } + + /** + * Return a copy of resposne headers + * + * @return + */ + Headers GetResponseHeaders() + { + Headers result; + if (resp_headers_.size() == 0) + return result; + + std::stringstream ss; + std::string headers((const char *)&resp_headers_[0], resp_headers_.size()); + ss.str(headers); + + std::string header; + while (std::getline(ss, header, '\n')) { + std::smatch match; + std::regex http_headers_regex(http_header_regexp); + if (std::regex_search(header, match, http_headers_regex)) + result[match[1]] = match[2]; // Key: value + } + return result; + } + + /** + * Return a copy of response body + * + * @return + */ + std::vector GetResponseBody() + { + return resp_body_; + } + + /** + * Return a raw copy of response headers+body + * + * @return + */ + std::vector GetRawResponse() + { + std::vector result; + if ((response_.memory!=nullptr)&&(response_.size!=0)) + result.insert(result.end(), (const char *)response_.memory, ((const char *)response_.memory) + response_.size); + return result; + } + + /** + * Release memory allocated for response + */ + void ReleaseResponse() + { + if (response_.memory!=nullptr) { + free(response_.memory); + response_.memory = nullptr; + response_.size = 0; + } + resp_headers_.clear(); + resp_body_.clear(); + } + + /** + * Abort request in connecting or reading state. + */ + void Abort() + { + is_aborted_ = true; + if (curl_ != nullptr) + { + // Simply close the socket - connection reset by peer + if (sockfd_) + { + ::close(sockfd_); + sockfd_ = 0; + } + } + } + + CURL *GetHandle() + { + return curl_; + } + +protected: + const bool raw_response_; // Do not split response headers from response body + const size_t http_conn_timeout_; // Timeout for connect. Default: 5s + + CURL *curl_; // Local curl instance + CURLcode res_; // Curl result OR HTTP status code if successful + + http_sdk::EventHandler* callback_ = nullptr; + + // Request values + http_sdk::Method method_; + std::string url_; + const Headers& request_headers_; + const http_sdk::Body& request_body_; + struct curl_slist *headers_chunk_ = nullptr; + + // Processed response headers and body + std::vector resp_headers_; + std::vector resp_body_; + + // Socket parameters + curl_socket_t sockfd_; + + //long sockextr_ = 0; + + curl_off_t nread_; + size_t sendlen_ = 0; // # bytes sent by client + size_t acklen_ = 0; // # bytes ack by server + + std::future result_; + + /** + * Helper routine to wait for data on socket + * + * @param sockfd + * @param for_recv + * @param timeout_ms + * @return + */ + static int WaitOnSocket(curl_socket_t sockfd, int for_recv, long timeout_ms) + { + struct timeval tv; + fd_set infd, outfd, errfd; + int res; + + tv.tv_sec = timeout_ms / 1000; + tv.tv_usec = (timeout_ms % 1000) * 1000; + + FD_ZERO(&infd); + FD_ZERO(&outfd); + FD_ZERO(&errfd); + + FD_SET(sockfd, &errfd); /* always check for error */ + + if(for_recv) { + FD_SET(sockfd, &infd); + } else { + FD_SET(sockfd, &outfd); + } + + /* select() returns the number of signalled sockets or -1 */ + res = select((int)sockfd + 1, &infd, &outfd, &errfd, &tv); + return res; + } + + // Raw response buffer + struct MemoryStruct { + char *memory; + size_t size; + } response_; + + /** + * Old-school memory allocator + * + * @param contents + * @param size + * @param nmemb + * @param userp + * @return + */ + static size_t WriteMemoryCallback(void *contents, size_t size, size_t nmemb, void *userp) + { + size_t realsize = size * nmemb; + struct MemoryStruct *mem = (struct MemoryStruct *)userp; + + mem->memory = (char *)(realloc(mem->memory, mem->size + realsize + 1)); + if(mem->memory == NULL) { + /* out of memory! */ + return 0; + } + + memcpy(&(mem->memory[mem->size]), contents, realsize); + mem->size += realsize; + mem->memory[mem->size] = 0; + + return realsize; + } + + /** + * C++ STL std::string allocator + * + * @param ptr + * @param size + * @param nmemb + * @param data + * @return + */ + static size_t WriteVectorCallback(void *ptr, size_t size, size_t nmemb, std::vector* data) + { + if (data!=nullptr) { + const unsigned char * begin = (unsigned char *)(ptr); + const unsigned char * end = begin + size * nmemb; + data->insert( data->end(), begin, end); + } + return size * nmemb; + } + +}; +} // namespace curl +} // namespace http +} // namespace common +} // namespace exporters +OPENTELEMETRY_END_NAMESPACE + diff --git a/exporters/include/opentelemtry/exporters/common/http/http_client_curl.h b/exporters/include/opentelemtry/exporters/common/http/http_client_curl.h new file mode 100644 index 0000000000..3c3d95d13e --- /dev/null +++ b/exporters/include/opentelemtry/exporters/common/http/http_client_curl.h @@ -0,0 +1,242 @@ +#pragma once + +#include "curl_http_operation.h" + +#include +#include +#include + +//#include + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace exporters +{ +namespace common +{ +namespace http +{ +namespace curl +{ + +namespace http_sdk = opentelemetry::sdk::common::http; +const http_sdk::StatusCode Http_Ok = 200; + +class Request : public http_sdk::Request +{ + +public: + Request() : method_(http_sdk::Method::Get), uri_("/") {} + + void SetMethod(http_sdk::Method method) noexcept override { method_ = method; } + + void SetBody(http_sdk::Body &body) noexcept override { body_ = std::move(body); } + + void AddHeader(nostd::string_view name, nostd::string_view value) noexcept override + { + headers_.insert(std::pair(name, value)); + } + + void ReplaceHeader(nostd::string_view name, nostd::string_view value) noexcept override + { + // erase matching headers + auto range = headers_.equal_range(name); + headers_.erase(range.first, range.second); + AddHeader(name, value); + } + + virtual void SetUri(nostd::string_view uri) noexcept override { uri_ = uri; } + + void SetTimeoutMs(std::chrono::milliseconds timeout_ms) noexcept override + { + _timeout_ms = timeout_ms; + } + +public: + http_sdk::Method method_; + http_sdk::Body body_; + Headers headers_; + nostd::string_view uri_; + std::chrono::milliseconds _timeout_ms; +}; + +class Response : public http_sdk::Response +{ + +public: + Response() : status_code_(Http_Ok) {} + + virtual const http_sdk::Body &GetBody() const noexcept override { return body_; } + + virtual bool ForEachHeader(nostd::function_ref callable) const + noexcept override + { + for (const auto &header : headers_) + { + if (!callable(name, value)) + { + return false; + } + } + return true; + } + + virtual bool ForEachHeader(const std::string &key, + nostd::function_ref callable) const + noexcept override + { + auto range = headers_.equal_range(key); + for (auto it = range.first; it != range.second; ++it) + { + if (!callable(std::make_pair(name, value)) + { + return false; + } + } + return true; + } + + virtual http_sdk::StatusCode GetStatusCode() const noexcept override { return status_code_; } + + virtual const std::string &GetErrorMessage() const noexcept override { return error_message_; } + + virtual bool IsSuccess() const noexcept override { return (error_message_.length() == 0); } + +public: + Headers headers_; + http_sdk::Body body_; + http_sdk::StatusCode status_code_; + std::string error_message_; +}; + +class SessionManager; + +class Session : public http_sdk::Session +{ + +public: + Session(SessionManager &session_manager, std::string host, uint16_t port = 80) + : session_manager_(session_manager), session_state_(http_sdk::SessionState::CREATED) + { + if (host.rfind("http://", 0) != 0 && host.rfind("https://", 0) != 0) + { + host_ = "http://" + host; // TBD - https support + } + host_ = host + ":" + std::to_string(port); + //httplib_client_.reset(new httplib::Client(host.c_str())); + } + + std::shared_ptr CreateRequest() noexcept override + { + http_request_.reset(new Request()); + return http_request_; + } + + virtual void SendRequest(http_sdk::EventHandler &callback) noexcept override + { + std::string url = host_ + "/" + http_request_->uri_; + + curl_operation_->reset(new HttpOperation(http_request_->method_, url, http_request_->headers_, callback, http_request_->body_))); + + curl_operation_->SendAsync([this, callback](HttpOperation& operation){ + if (operation.WasAborted()) { + //Manually cancelled + callback->OnEvent(http_sdk::SessionState::Cancelled, ""); + } + + if (operation.GetStatusCode() == CURLE_OK){ + auto response = std::unique_ptr(new Response()); + //auto responseHeaders = operation.GetResponseHeaders(); + response->headers_ = operation.GetResponseHeaders(); //.insert(responseHeaders.begin(), responseHeaders.end()); + response->m_body = operation.GetResponseBody(); + callback->OnResponse(response.release()); + } + }); + } + + virtual http_sdk::SessionState GetSessionState() const noexcept override + { + return session_state_; + } + + virtual bool CancelSession() noexcept override + { + curl_operation_->Abort(); + return true; + } + + virtual bool FinishSession() noexcept override + { + curl_operation_->Finish(); + return true; + } + + void SetId(uint64_t session_id) { session_id_ = session_id; } + +private: + std::shared_ptr http_request_; + http_sdk::SessionState session_state_; + nostd::string_view host_; + std::unique_ptr curl_operation_; + uint64_t session_id_; + SessionManager &session_manager_; + //std::unique_ptr httplib_client_; +}; + +class SessionManager : public http_sdk::SessionManager +{ + +public: + + SessionManager() + { + curl_global_init(CURL_GLOBAL_ALL); + } + + std::shared_ptr createSession(std::string host, + uint16_t port = 80) noexcept override + { + auto session = std::make_shared(*this, host, port); + auto session_id = ++next_session_id_; + session->SetId(session_id); + sessions_.insert({session_id, session}); + return session; + } + + bool CancelAllSessions() noexcept override + { + for (auto &session : sessions_) + { + session.second->CancelSession(); + } + return true; + } + + bool FinishAllSessions() noexcept override + { + for (auto &session : sessions_) + { + session.second->FinishSession(); + } + return true; + } + + void CleanupSession(uint64_t session_id) + { + // TBD = Need to be thread safe + sessions_.erase(session_id); + } + + ~SessionManager() { + curl_global_cleanup(); + } + +private: + std::atomic next_session_id_; + std::map> sessions_; +}; + +} // namespace curl +} // namespace http +} // namespace common +} // namespace exporters +OPENTELEMETRY_END_NAMESPACE diff --git a/exporters/include/opentelemtry/exporters/common/http/http_client_httplib.h b/exporters/include/opentelemtry/exporters/common/http/http_client_httplib.h new file mode 100644 index 0000000000..24ff41b5a3 --- /dev/null +++ b/exporters/include/opentelemtry/exporters/common/http/http_client_httplib.h @@ -0,0 +1,315 @@ +#pragma once + +#include "httplib.h" +#include "opentelemetry/sdk/common/http_client.h" + +#include +#include +#include + +#include + +OPENTELEMETRY_BEGIN_NAMESPACE +namespace exporters +{ +namespace common +{ +namespace http +{ + +namespace http_sdk = opentelemetry::sdk::common::http; +using HttpHeaders = std::multimap; + +const http_sdk::HttpStatusCode HTTP_OK = 200; + +class HttpRequest : public http_sdk::HttpRequest +{ + +public: + HttpRequest() : method_(http_sdk::HttpMethod::HTTP_GET), uri_("/") {} + + void setMethod(http_sdk::HttpMethod method) noexcept override { method_ = method; } + + void SetBody(http_sdk::HttpBody &body) noexcept override { body_ = std::move(body); } + + void AddHeader(const http_sdk::HttpHeader &header) noexcept override + { + headers_.insert({header.first, header.second}); + } + + void ReplaceHeader(const http_sdk::HttpHeader &header) noexcept override + { + // erase matching headers + auto range = headers_.equal_range(header.first); + headers_.erase(range.first, range.second); + AddHeader(header); + } + + virtual void setURI(const std::string &uri) noexcept override { uri_ = uri; } + + void SetTimeoutMs(std::chrono::milliseconds timeout_ms) noexcept override + { + _timeout_ms = timeout_ms; + } + +public: + http_sdk::HttpMethod method_; + http_sdk::HttpBody body_; + HttpHeaders headers_; + std::string uri_; + std::chrono::milliseconds _timeout_ms; +}; + +class HttpResponse : public http_sdk::HttpResponse +{ + +public: + HttpResponse() : status_code_(HTTP_OK) {} + + virtual const http_sdk::HttpBody &GetBody() const noexcept override { return body_; } + + virtual bool GetNextHeader(nostd::function_ref callable) const + noexcept override + { + for (const auto &header : headers_) + { + if (!callable(std::make_pair(header.first, header.second))) + { + return false; + } + } + return true; + } + + virtual bool GetNextHeader(const std::string &key, + nostd::function_ref callable) const + noexcept override + { + auto range = headers_.equal_range(key); + for (auto it = range.first; it != range.second; ++it) + { + if (!callable(std::make_pair(it->first, it->second))) + { + return false; + } + } + return true; + } + + virtual http_sdk::HttpStatusCode GetStatusCode() const noexcept override { return status_code_; } + + virtual const std::string &GetErrorMessage() const noexcept override { return error_message_; } + + virtual bool IsSuccess() const noexcept override { return (error_message_.length() == 0); } + +public: + HttpHeaders headers_; + http_sdk::HttpBody body_; + http_sdk::HttpStatusCode status_code_; + std::string error_message_; +}; + +class HttpSessionManager; + +class HttpSession : public http_sdk::HttpSession +{ + +public: + HttpSession(HttpSessionManager &session_manager, std::string host, uint16_t port = 80) + : session_manager_(session_manager), session_state_(http_sdk::HttpSessionState::CREATED) + { + if (host.rfind("http://", 0) != 0 && host.rfind("https://", 0) != 0) + { + host = "http://" + host; // TBD - https support + } + host = host + ":" + std::to_string(port); + httplib_client_.reset(new httplib::Client(host.c_str())); + } + + std::shared_ptr CreateRequest() noexcept override + { + http_request_.reset(new HttpRequest()); + return http_request_; + } + + virtual void SendRequest(http_sdk::HttpResponseHandler &callback) noexcept override + { + HttpResponse res; + if (http_sdk::HttpMethod::HTTP_GET == http_request_->method_) + { + auto result_future = std::async(std::launch::async, [this, &callback, &res] { + return this->SendGetRequestAsync(callback, res); + }); + } + else + { + auto result_future = std::async(std::launch::async, [this, &callback, &res] { + return this->SendPostRequestAsync(callback, res); + }); + } + // &HttpSession::SendRequestAsync, this, callback); + } + + virtual http_sdk::HttpSessionState GetSessionState() const noexcept override + { + return session_state_; + } + + virtual bool CancelSession() noexcept override + { + // TBD - Not Implemented + return true; + } + + virtual bool FinishSession() noexcept override + { + // TBD - Not Implemented + return true; + } + + void SetId(uint64_t session_id) { session_id_ = session_id; } + +private: + void SendGetRequestAsync(http_sdk::HttpResponseHandler &callback, HttpResponse &res) + { + session_state_ = http_sdk::HttpSessionState::ONGOING; + httplib::Result httplib_res = + httplib_client_->Get(http_request_->uri_.c_str(), http_request_->headers_); + httplib::Error err = httplib_res.error(); + if (err == httplib::Error::Success) + { + res.status_code_ = httplib_res->status; + res.headers_ = std::move(httplib_res->headers); + std::move(httplib_res->body.begin(), httplib_res->body.end(), std::back_inserter(res.body_)); + session_state_ = http_sdk::HttpSessionState::FINISHED; + callback.OnHttpResponse(res); + } + else + { + res.error_message_ = get_error_message(err); + session_state_ = http_sdk::HttpSessionState::FINISHED; + callback.OnHttpResponse(res); + } + } + + void SendPostRequestAsync(http_sdk::HttpResponseHandler &callback, HttpResponse &res) + { + session_state_ = http_sdk::HttpSessionState::ONGOING; + std::string body(http_request_->body_.begin(), http_request_->body_.end()); + // find content type from headers; + auto search = http_request_->headers_.find("Content-Type"); + std::string content_type; + if (search != http_request_->headers_.end()) + { + content_type = search->second; + } + else + { + content_type = "application/json"; // defaults + } + httplib::Result httplib_res = + httplib_client_->Post(http_request_->uri_.c_str(), std::move(body), content_type.c_str()); + httplib::Error err = httplib_res.error(); + if (err == httplib::Error::Success) + { + + res.status_code_ = httplib_res->status; + res.headers_ = std::move(httplib_res->headers); + std::move(httplib_res->body.begin(), httplib_res->body.end(), std::back_inserter(res.body_)); + session_state_ = http_sdk::HttpSessionState::FINISHED; + callback.OnHttpResponse(res); + } + else + { + res.error_message_ = get_error_message(err); + session_state_ = http_sdk::HttpSessionState::FINISHED; + callback.OnHttpResponse(res); + } + } + + std::string get_error_message(httplib::Error err) + { + switch (err) + { + case httplib::Error::Unknown: + return "Unknown error"; + case httplib::Error::Connection: + return "Connection error"; + case httplib::Error::BindIPAddress: + return "Bind IP address error"; + case httplib::Error::Read: + return "Read error"; + case httplib::Error::Write: + return "Write error"; + case httplib::ExceedRedirectCount: + return "Redirect Count Exceeded error"; + case httplib::Canceled: + return "Cancelled Error"; + case httplib::SSLConnection: + return "SSL Connection error"; + case httplib::SSLLoadingCerts: + return "SSL Certificate loading error"; + case httplib::SSLServerVerification: + return "SSL Server Verification error"; + case httplib::UnsupportedMultipartBoundaryChars: + return "Unsupported Multiple Boundary Characters"; + default: + return "Unknown error"; + } + } + +private: + std::shared_ptr http_request_; + http_sdk::HttpSessionState session_state_; + uint64_t session_id_; + HttpSessionManager &session_manager_; + std::unique_ptr httplib_client_; +}; + +class HttpSessionManager : public http_sdk::HttpSessionManager +{ + +public: + std::shared_ptr createHttpSession(std::string host, + uint16_t port = 80) noexcept override + { + auto session = std::make_shared(*this, host, port); + auto session_id = ++next_session_id_; + session->SetId(session_id); + sessions_.insert({session_id, session}); + return session; + } + + bool CancelAllSessions() noexcept override + { + for (auto &session : sessions_) + { + session.second->CancelSession(); + } + return true; + } + + bool FinishAllSessions() noexcept override + { + for (auto &session : sessions_) + { + session.second->FinishSession(); + } + return true; + } + + void CleanupSession(uint64_t session_id) + { + // TBD = Need to be thread safe + sessions_.erase(session_id); + } + +private: + std::atomic next_session_id_; + std::map> sessions_; +}; + +} // namespace http +} // namespace common +} // namespace exporters +OPENTELEMETRY_END_NAMESPACE diff --git a/exporters/include/opentelemtry/exporters/common/http/main.cc b/exporters/include/opentelemtry/exporters/common/http/main.cc new file mode 100644 index 0000000000..89357c0720 --- /dev/null +++ b/exporters/include/opentelemtry/exporters/common/http/main.cc @@ -0,0 +1,6 @@ +#include "curl_http_operation.h" + + +int main() +{ +} diff --git a/sdk/include/opentelemetry/sdk/common/http_client.h b/sdk/include/opentelemetry/sdk/common/http_client.h index 73e54f0fe2..e315c66bc6 100644 --- a/sdk/include/opentelemetry/sdk/common/http_client.h +++ b/sdk/include/opentelemetry/sdk/common/http_client.h @@ -60,19 +60,20 @@ enum class Method enum class SessionState { - Created, // session object is created - Ongoing, // session is ongoing - Finished, // session is finished ( this needs to be the final state ) - Queued, // http request is queued - TimedOut, // Request timedout, no response received - Aborted, // http request aborted due to local error, - Cancelled, // http request cancelled, possibly due to session->CancelSession(); - SendingFailed, // http request sending failed - NetworkError, // network error - SSLHandshakeFailed, // ssl handshake failed - ReadError, // error while reading response - WriteError // error while writing rquest -}; + + CreateFailed, + Created, + SendFailed, + Connecting, + ConnectFailed, + Connected, + Sending, + Response, + Destroy, + Aborted, + Cancelled, + SSLHandshakeFailed +} ; using Byte = uint8_t; using StatusCode = uint16_t; @@ -121,7 +122,7 @@ class EventHandler public: virtual void OnResponse(Response &) noexcept = 0; - virtual void OnError(SessionState, nostd::string_view) noexcept = 0; + virtual void OnEvent(SessionState, nostd::string_view) noexcept = 0; virtual void OnConnecting(const SSLCertificate &) noexcept {} diff --git a/sdk/include/opentelemetry/sdk/common/main.cc b/sdk/include/opentelemetry/sdk/common/main.cc new file mode 100644 index 0000000000..54e40891eb --- /dev/null +++ b/sdk/include/opentelemetry/sdk/common/main.cc @@ -0,0 +1,3 @@ +#include "http_client.h" + +int main() {} From 59adb0e3edd53361c10edd872fdc0021a5045db5 Mon Sep 17 00:00:00 2001 From: Lalit Kumar Bhasin Date: Sun, 1 Nov 2020 22:41:00 +0530 Subject: [PATCH 02/32] more changes --- .../common/http/curl_http_operation.h | 11 ++-- .../exporters/common/http/http_client_curl.h | 54 +++++++++---------- .../opentelemetry/sdk/common/http_client.h | 5 +- 3 files changed, 35 insertions(+), 35 deletions(-) diff --git a/exporters/include/opentelemtry/exporters/common/http/curl_http_operation.h b/exporters/include/opentelemtry/exporters/common/http/curl_http_operation.h index bcf1f52bf5..08afa97c1b 100644 --- a/exporters/include/opentelemtry/exporters/common/http/curl_http_operation.h +++ b/exporters/include/opentelemtry/exporters/common/http/curl_http_operation.h @@ -31,7 +31,7 @@ namespace curl const std::string http_header_regexp = "(.*)\\: (.*)\\n*"; struct curl_ci { - bool operator() (const std::string & s1, const std::string & s2) const { + bool operator() (const nostd::string_view & s1, const nostd::string_view& s2) const { return std::lexicographical_compare( s1.begin(), s1.end(), s2.begin(), s2.end(), @@ -115,10 +115,10 @@ class HttpOperation { // Specify our custom headers for(auto &kv : this->request_headers_) { - std::string header = kv.first(); + std::string header = static_cast(kv.first); header += ": "; - header += kv.second(); - headers_chunk_ = curl_slist_append(headers_chunk_, header());; + header += static_cast(kv.second); + headers_chunk_ = curl_slist_append(headers_chunk_, header.c_str()); } if(headers_chunk_ != nullptr) @@ -321,7 +321,8 @@ class HttpOperation { std::smatch match; std::regex http_headers_regex(http_header_regexp); if (std::regex_search(header, match, http_headers_regex)) - result[match[1]] = match[2]; // Key: value + result.insert(std::pair(match[1], match[2])); + //result[match.str(1)] = match[2]; // Key: value } return result; } diff --git a/exporters/include/opentelemtry/exporters/common/http/http_client_curl.h b/exporters/include/opentelemtry/exporters/common/http/http_client_curl.h index 3c3d95d13e..13fdc1536b 100644 --- a/exporters/include/opentelemtry/exporters/common/http/http_client_curl.h +++ b/exporters/include/opentelemtry/exporters/common/http/http_client_curl.h @@ -67,12 +67,13 @@ class Response : public http_sdk::Response virtual const http_sdk::Body &GetBody() const noexcept override { return body_; } - virtual bool ForEachHeader(nostd::function_ref callable) const + virtual bool ForEachHeader( + nostd::function_ref callable) const noexcept override { for (const auto &header : headers_) { - if (!callable(name, value)) + if (!callable(header.first, header.second)) { return false; } @@ -80,14 +81,14 @@ class Response : public http_sdk::Response return true; } - virtual bool ForEachHeader(const std::string &key, + virtual bool ForEachHeader(const nostd::string_view &name, nostd::function_ref callable) const noexcept override { - auto range = headers_.equal_range(key); + auto range = headers_.equal_range(name); for (auto it = range.first; it != range.second; ++it) { - if (!callable(std::make_pair(name, value)) + if (!callable(it->first, it->second)) { return false; } @@ -97,15 +98,10 @@ class Response : public http_sdk::Response virtual http_sdk::StatusCode GetStatusCode() const noexcept override { return status_code_; } - virtual const std::string &GetErrorMessage() const noexcept override { return error_message_; } - - virtual bool IsSuccess() const noexcept override { return (error_message_.length() == 0); } - public: Headers headers_; http_sdk::Body body_; http_sdk::StatusCode status_code_; - std::string error_message_; }; class SessionManager; @@ -115,7 +111,7 @@ class Session : public http_sdk::Session public: Session(SessionManager &session_manager, std::string host, uint16_t port = 80) - : session_manager_(session_manager), session_state_(http_sdk::SessionState::CREATED) + : session_manager_(session_manager), is_session_active_(false) { if (host.rfind("http://", 0) != 0 && host.rfind("https://", 0) != 0) { @@ -133,29 +129,28 @@ class Session : public http_sdk::Session virtual void SendRequest(http_sdk::EventHandler &callback) noexcept override { - std::string url = host_ + "/" + http_request_->uri_; + is_session_active_ = true; + std::string url = static_cast(host_) + "/" + static_cast(http_request_->uri_); - curl_operation_->reset(new HttpOperation(http_request_->method_, url, http_request_->headers_, callback, http_request_->body_))); - - curl_operation_->SendAsync([this, callback](HttpOperation& operation){ + curl_operation_.reset(new HttpOperation(http_request_->method_, url, &callback, http_request_->headers_, http_request_->body_)); + auto callback_ptr = &callback; + curl_operation_->SendAsync([this, callback_ptr](HttpOperation& operation){ if (operation.WasAborted()) { //Manually cancelled - callback->OnEvent(http_sdk::SessionState::Cancelled, ""); + callback_ptr->OnEvent(http_sdk::SessionState::Cancelled, ""); } - if (operation.GetStatusCode() == CURLE_OK){ + if (operation.GetResponseCode() >= CURL_LAST){ + // we have a http response auto response = std::unique_ptr(new Response()); //auto responseHeaders = operation.GetResponseHeaders(); response->headers_ = operation.GetResponseHeaders(); //.insert(responseHeaders.begin(), responseHeaders.end()); - response->m_body = operation.GetResponseBody(); - callback->OnResponse(response.release()); + response->body_ = operation.GetResponseBody(); + callback_ptr->OnResponse(*response); } - }); - } - virtual http_sdk::SessionState GetSessionState() const noexcept override - { - return session_state_; + }); + is_session_active_ = false; } virtual bool CancelSession() noexcept override @@ -169,16 +164,21 @@ class Session : public http_sdk::Session curl_operation_->Finish(); return true; } + + virtual bool IsSessionActive() noexcept override + { + return is_session_active_; + } void SetId(uint64_t session_id) { session_id_ = session_id; } private: std::shared_ptr http_request_; - http_sdk::SessionState session_state_; nostd::string_view host_; std::unique_ptr curl_operation_; uint64_t session_id_; SessionManager &session_manager_; + bool is_session_active_; //std::unique_ptr httplib_client_; }; @@ -192,10 +192,10 @@ class SessionManager : public http_sdk::SessionManager curl_global_init(CURL_GLOBAL_ALL); } - std::shared_ptr createSession(std::string host, + std::shared_ptr CreateSession(nostd::string_view host, uint16_t port = 80) noexcept override { - auto session = std::make_shared(*this, host, port); + auto session = std::make_shared(*this, static_cast(host), port); auto session_id = ++next_session_id_; session->SetId(session_id); sessions_.insert({session_id, session}); diff --git a/sdk/include/opentelemetry/sdk/common/http_client.h b/sdk/include/opentelemetry/sdk/common/http_client.h index e315c66bc6..63b0dd4d30 100644 --- a/sdk/include/opentelemetry/sdk/common/http_client.h +++ b/sdk/include/opentelemetry/sdk/common/http_client.h @@ -70,7 +70,6 @@ enum class SessionState Sending, Response, Destroy, - Aborted, Cancelled, SSLHandshakeFailed } ; @@ -104,12 +103,12 @@ class Response virtual const Body &GetBody() const noexcept = 0; virtual bool ForEachHeader( - nostd::function_ref callable) const + nostd::function_ref callable) const noexcept = 0; virtual bool ForEachHeader( const nostd::string_view &key, - nostd::function_ref callable) const + nostd::function_ref callable) const noexcept = 0; virtual StatusCode GetStatusCode() const noexcept = 0; From 1d410083ee5fc762660422b446b38d48d7811bed Mon Sep 17 00:00:00 2001 From: Lalit Kumar Bhasin Date: Tue, 3 Nov 2020 09:33:37 +0530 Subject: [PATCH 03/32] curl http client implementation --- CMakeLists.txt | 15 +- ci/do_ci.sh | 3 + exporters/CMakeLists.txt | 4 + .../exporters/common/http/http_client_curl.h | 17 +- .../common/http/http_client_httplib.h | 2 - .../common/http/http_operation_curl.h} | 32 ++-- .../exporters/common/http/main.cc | 38 ++++ .../exporters/common/http/main.cc | 6 - exporters/test/CMakeLists.txt | 9 + exporters/test/curl_http_test.cc | 167 ++++++++++++++++++ .../ext/http/server/http_server.h | 11 +- .../opentelemetry/sdk/common/http_client.h | 28 +-- 12 files changed, 283 insertions(+), 49 deletions(-) rename exporters/include/{opentelemtry => opentelemetry}/exporters/common/http/http_client_curl.h (91%) rename exporters/include/{opentelemtry => opentelemetry}/exporters/common/http/http_client_httplib.h (99%) rename exporters/include/{opentelemtry/exporters/common/http/curl_http_operation.h => opentelemetry/exporters/common/http/http_operation_curl.h} (95%) create mode 100644 exporters/include/opentelemetry/exporters/common/http/main.cc delete mode 100644 exporters/include/opentelemtry/exporters/common/http/main.cc create mode 100644 exporters/test/CMakeLists.txt create mode 100644 exporters/test/curl_http_test.cc diff --git a/CMakeLists.txt b/CMakeLists.txt index e9b7cbc24f..73cbc5c159 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -16,6 +16,9 @@ option(WITH_OTPROTOCOL option(WITH_PROMETHEUS "Whether to include the Prometheus Client in the SDK" OFF) +option(WITH_CURL "Whether to include HTTP CURL Client in the SDK" + OFF) + option(WITH_TESTS "Whether to enable tests" ON) option(WITH_EXAMPLES "Whether to build examples" ON) @@ -28,6 +31,14 @@ if(WITH_TESTS) include(CTest) endif() +if (WITH_CURL) + set(CURL_LIBRARY "-lcurl") + find_package(CURL REQUIRED) + if (NOT CURL_FOUND) + message (FATAL_ERROR "libcurl not found! Have you installed deps?") + endif (NOT CURL_FOUND) +endif() + find_package(Threads) if(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") @@ -62,8 +73,10 @@ include_directories(sdk/include) include_directories(sdk) add_subdirectory(sdk) include_directories(.) -add_subdirectory(exporters) if(WITH_EXAMPLES) add_subdirectory(examples) endif() +include_directories(ext/include) add_subdirectory(ext) +include_directories(exporters/include) +add_subdirectory(exporters) diff --git a/ci/do_ci.sh b/ci/do_ci.sh index 4790daabf5..e73c32986f 100755 --- a/ci/do_ci.sh +++ b/ci/do_ci.sh @@ -16,6 +16,7 @@ if [[ "$1" == "cmake.test" ]]; then rm -rf * cmake -DCMAKE_BUILD_TYPE=Debug \ -DCMAKE_CXX_FLAGS="-Werror" \ + -DWITH_CURL=ON \ "${SRC_DIR}" make make test @@ -26,6 +27,7 @@ elif [[ "$1" == "cmake.c++20.test" ]]; then cmake -DCMAKE_BUILD_TYPE=Debug \ -DCMAKE_CXX_FLAGS="-Werror" \ -DCMAKE_CXX_STANDARD=20 \ + -DWITH_CURL=ON \ "${SRC_DIR}" make make test @@ -36,6 +38,7 @@ elif [[ "$1" == "cmake.legacy.test" ]]; then cmake -DCMAKE_BUILD_TYPE=Debug \ -DCMAKE_CXX_FLAGS="-Werror" \ -DCMAKE_CXX_STANDARD=11 \ + -DWITH_CURL=ON \ "${SRC_DIR}" make make test diff --git a/exporters/CMakeLists.txt b/exporters/CMakeLists.txt index fdb9776473..8166fa0ada 100644 --- a/exporters/CMakeLists.txt +++ b/exporters/CMakeLists.txt @@ -8,3 +8,7 @@ add_subdirectory(memory) if(WITH_PROMETHEUS) add_subdirectory(prometheus) endif() + +if(BUILD_TESTING) + add_subdirectory(test) +endif() diff --git a/exporters/include/opentelemtry/exporters/common/http/http_client_curl.h b/exporters/include/opentelemetry/exporters/common/http/http_client_curl.h similarity index 91% rename from exporters/include/opentelemtry/exporters/common/http/http_client_curl.h rename to exporters/include/opentelemetry/exporters/common/http/http_client_curl.h index 13fdc1536b..e211ecc93d 100644 --- a/exporters/include/opentelemtry/exporters/common/http/http_client_curl.h +++ b/exporters/include/opentelemetry/exporters/common/http/http_client_curl.h @@ -1,13 +1,11 @@ #pragma once -#include "curl_http_operation.h" +#include "http_operation_curl.h" #include #include #include -//#include - OPENTELEMETRY_BEGIN_NAMESPACE namespace exporters { @@ -48,7 +46,7 @@ class Request : public http_sdk::Request void SetTimeoutMs(std::chrono::milliseconds timeout_ms) noexcept override { - _timeout_ms = timeout_ms; + timeout_ms_ = timeout_ms; } public: @@ -56,7 +54,7 @@ class Request : public http_sdk::Request http_sdk::Body body_; Headers headers_; nostd::string_view uri_; - std::chrono::milliseconds _timeout_ms; + std::chrono::milliseconds timeout_ms_{5000}; //ms }; class Response : public http_sdk::Response @@ -118,7 +116,6 @@ class Session : public http_sdk::Session host_ = "http://" + host; // TBD - https support } host_ = host + ":" + std::to_string(port); - //httplib_client_.reset(new httplib::Client(host.c_str())); } std::shared_ptr CreateRequest() noexcept override @@ -131,9 +128,8 @@ class Session : public http_sdk::Session { is_session_active_ = true; std::string url = static_cast(host_) + "/" + static_cast(http_request_->uri_); - - curl_operation_.reset(new HttpOperation(http_request_->method_, url, &callback, http_request_->headers_, http_request_->body_)); auto callback_ptr = &callback; + curl_operation_.reset(new HttpOperation(http_request_->method_, url, callback_ptr, http_request_->headers_, http_request_->body_, false, http_request_->timeout_ms_.count())); curl_operation_->SendAsync([this, callback_ptr](HttpOperation& operation){ if (operation.WasAborted()) { //Manually cancelled @@ -143,8 +139,7 @@ class Session : public http_sdk::Session if (operation.GetResponseCode() >= CURL_LAST){ // we have a http response auto response = std::unique_ptr(new Response()); - //auto responseHeaders = operation.GetResponseHeaders(); - response->headers_ = operation.GetResponseHeaders(); //.insert(responseHeaders.begin(), responseHeaders.end()); + response->headers_ = operation.GetResponseHeaders(); response->body_ = operation.GetResponseBody(); callback_ptr->OnResponse(*response); } @@ -179,9 +174,9 @@ class Session : public http_sdk::Session uint64_t session_id_; SessionManager &session_manager_; bool is_session_active_; - //std::unique_ptr httplib_client_; }; + class SessionManager : public http_sdk::SessionManager { diff --git a/exporters/include/opentelemtry/exporters/common/http/http_client_httplib.h b/exporters/include/opentelemetry/exporters/common/http/http_client_httplib.h similarity index 99% rename from exporters/include/opentelemtry/exporters/common/http/http_client_httplib.h rename to exporters/include/opentelemetry/exporters/common/http/http_client_httplib.h index 24ff41b5a3..aff15b35a6 100644 --- a/exporters/include/opentelemtry/exporters/common/http/http_client_httplib.h +++ b/exporters/include/opentelemetry/exporters/common/http/http_client_httplib.h @@ -7,8 +7,6 @@ #include #include -#include - OPENTELEMETRY_BEGIN_NAMESPACE namespace exporters { diff --git a/exporters/include/opentelemtry/exporters/common/http/curl_http_operation.h b/exporters/include/opentelemetry/exporters/common/http/http_operation_curl.h similarity index 95% rename from exporters/include/opentelemtry/exporters/common/http/curl_http_operation.h rename to exporters/include/opentelemetry/exporters/common/http/http_operation_curl.h index 08afa97c1b..64d8392c14 100644 --- a/exporters/include/opentelemtry/exporters/common/http/curl_http_operation.h +++ b/exporters/include/opentelemetry/exporters/common/http/http_operation_curl.h @@ -15,7 +15,6 @@ #include #endif - OPENTELEMETRY_BEGIN_NAMESPACE namespace exporters { @@ -26,7 +25,7 @@ namespace http namespace curl { namespace http_sdk = opentelemetry::sdk::common::http; - const size_t default_http_conn_timeout = 5; + const size_t default_http_conn_timeout = 5000; //ms const std::string http_status_regexp = "HTTP\\/\\d\\.\\d (\\d+)\\ .*" ; const std::string http_header_regexp = "(.*)\\: (.*)\\n*"; @@ -49,11 +48,13 @@ class HttpOperation { void DispatchEvent(http_sdk::SessionState type, std::string reason = "") { - if(callback_ != nullptr) + if(callback_ != nullptr) { callback_->OnEvent(type, reason); + } } std::atomic is_aborted_; // Set to 'true' when async callback is aborted + std::atomic is_finished_; // Set to 'true' when async callback is finished. /** * Create local CURL instance for url and body @@ -89,6 +90,7 @@ class HttpOperation { res_(CURLE_OK), sockfd_(0), is_aborted_(false), + is_finished_(false), nread_(0) { response_.memory = nullptr; @@ -140,7 +142,8 @@ class HttpOperation { { result_.wait(); } - DispatchEvent(http_sdk::SessionState::Destroy); + //TBD - Need to be uncomment. This will callback instance is deleted. + //DispatchEvent(http_sdk::SessionState::Destroy); res_ = CURLE_OK; curl_easy_cleanup(curl_); curl_slist_free_all(headers_chunk_); @@ -152,9 +155,10 @@ class HttpOperation { */ virtual void Finish() { - if (result_.valid()) + if (result_.valid() && !is_finished_) { result_.wait(); + is_finished_ = true; } } @@ -167,7 +171,6 @@ class HttpOperation { // Request buffer const void *request = (request_body_.empty())? NULL: &request_body_[0]; const size_t req_size = request_body_.size(); - if(!curl_) { res_ = CURLE_FAILED_INIT; @@ -179,9 +182,11 @@ class HttpOperation { // curl_easy_setopt(curl, CURLOPT_LOCALPORT, dcf_port); // Perform initial connect, handling the timeout if needed + curl_easy_setopt(curl_, CURLOPT_CONNECT_ONLY, 1L); DispatchEvent(http_sdk::SessionState::Connecting); res_ = curl_easy_perform(curl_); + if(CURLE_OK != res_) { DispatchEvent(http_sdk::SessionState::ConnectFailed, curl_easy_strerror(res_)); // couldn't connect - stage 1 @@ -194,6 +199,7 @@ class HttpOperation { */ long sockextr = 0 ; res_ = curl_easy_getinfo(curl_, CURLINFO_LASTSOCKET, &sockextr); + if(CURLE_OK != res_) { DispatchEvent(http_sdk::SessionState::ConnectFailed, curl_easy_strerror(res_)); // couldn't connect - stage 2 @@ -202,7 +208,7 @@ class HttpOperation { /* wait for the socket to become ready for sending */ sockfd_ = sockextr; - if( !WaitOnSocket(sockfd_, 0, http_conn_timeout_ * 1000L) || is_aborted_) + if( !WaitOnSocket(sockfd_, 0, http_conn_timeout_ ) || is_aborted_) { res_ = CURLE_OPERATION_TIMEDOUT; DispatchEvent(http_sdk::SessionState::ConnectFailed, " Is aborted: " + std::to_string(is_aborted_.load())); // couldn't connect - stage 3 @@ -232,8 +238,7 @@ class HttpOperation { curl_easy_setopt(curl_, CURLOPT_POST, true); curl_easy_setopt(curl_, CURLOPT_POSTFIELDS, (const char *)request); curl_easy_setopt(curl_, CURLOPT_POSTFIELDSIZE, req_size); - } else - if (method_ == http_sdk::Method::Get) + } else if (method_ == http_sdk::Method::Get) { // GET } else @@ -241,10 +246,12 @@ class HttpOperation { res_ = CURLE_UNSUPPORTED_PROTOCOL; return res_; } - /* abort if slower than 4kb/sec during 30 seconds */ + + // abort if slower than 4kb/sec during 30 seconds curl_easy_setopt(curl_, CURLOPT_LOW_SPEED_TIME, 30L); curl_easy_setopt(curl_, CURLOPT_LOW_SPEED_LIMIT, 4096); DispatchEvent(http_sdk::SessionState::Sending); + res_ = curl_easy_perform(curl_); if(CURLE_OK != res_) { @@ -279,7 +286,9 @@ class HttpOperation { result_ = std::async(std::launch::async, [this, callback] { long result = Send(); if (callback!=nullptr) + { callback(*this); + } return result; }); return result_; @@ -388,7 +397,7 @@ class HttpOperation { protected: const bool raw_response_; // Do not split response headers from response body - const size_t http_conn_timeout_; // Timeout for connect. Default: 5s + const size_t http_conn_timeout_; // Timeout for connect. Default: 5000ms CURL *curl_; // Local curl instance CURLcode res_; // Curl result OR HTTP status code if successful @@ -509,4 +518,3 @@ class HttpOperation { } // namespace common } // namespace exporters OPENTELEMETRY_END_NAMESPACE - diff --git a/exporters/include/opentelemetry/exporters/common/http/main.cc b/exporters/include/opentelemetry/exporters/common/http/main.cc new file mode 100644 index 0000000000..a0b437800f --- /dev/null +++ b/exporters/include/opentelemetry/exporters/common/http/main.cc @@ -0,0 +1,38 @@ +#include "http_client_curl.h" +#include "opentelemetry/nostd/string_view.h" +#include + +namespace curl = opentelemetry::exporters::common::http::curl; +namespace http_sdk = opentelemetry::sdk::common::http; + +class CustomEventHandler: public http_sdk::EventHandler +{ +public: + virtual void OnResponse(http_sdk::Response &response) noexcept override + { + std::cout << "LALIT -> In main : " << response.GetStatusCode() << "\n"; + } + + virtual void OnEvent(http_sdk::SessionState state, opentelemetry::nostd::string_view reason) noexcept override + { + std::cout << reason ; + } + + virtual void OnConnecting(const http_sdk::SSLCertificate &) noexcept {} + + virtual ~CustomEventHandler() = default; +}; + +int main() +{ + curl::SessionManager sessionManager; + + auto session = sessionManager.CreateSession("127.0.0.1", 8080); + auto request = session->CreateRequest(); + CustomEventHandler *handler = new CustomEventHandler(); + session->SendRequest(*handler); + session->FinishSession(); + std::cout << " =====> LALIT :::===> deleting handler\n"; + delete handler; +} + diff --git a/exporters/include/opentelemtry/exporters/common/http/main.cc b/exporters/include/opentelemtry/exporters/common/http/main.cc deleted file mode 100644 index 89357c0720..0000000000 --- a/exporters/include/opentelemtry/exporters/common/http/main.cc +++ /dev/null @@ -1,6 +0,0 @@ -#include "curl_http_operation.h" - - -int main() -{ -} diff --git a/exporters/test/CMakeLists.txt b/exporters/test/CMakeLists.txt new file mode 100644 index 0000000000..8b573ef21c --- /dev/null +++ b/exporters/test/CMakeLists.txt @@ -0,0 +1,9 @@ +if(WITH_CURL) + add_executable("curl_http_test" "curl_http_test.cc") + include_directories(${CURL_INCLUDE_DIR}) + target_link_libraries( "curl_http_test" + ${GTEST_BOTH_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT} + ${CURL_LIBRARIES}) + gtest_add_tests(TARGET "curl_http_test" TEST_PREFIX curl. TEST_LIST + "curl_http_test") +endif() diff --git a/exporters/test/curl_http_test.cc b/exporters/test/curl_http_test.cc new file mode 100644 index 0000000000..46df29466c --- /dev/null +++ b/exporters/test/curl_http_test.cc @@ -0,0 +1,167 @@ +#include "opentelemetry/exporters/common/http/http_client_curl.h" +#include "opentelemetry/ext/http/server/http_server.h" + +#include +#include +#include +#include +#include +#include +#include + +#define HTTP_PORT 19000 + +#include + +namespace curl = opentelemetry::exporters::common::http::curl; +namespace http_sdk = opentelemetry::sdk::common::http; + +class CustomEventHandler: public http_sdk::EventHandler +{ +public: + virtual void OnResponse(http_sdk::Response &response) noexcept override {}; + virtual void OnEvent(http_sdk::SessionState state, opentelemetry::nostd::string_view reason) noexcept override {} + virtual void OnConnecting(const http_sdk::SSLCertificate &) noexcept {} + virtual ~CustomEventHandler() = default; + bool is_called_ = false; +}; + +class GetEventHandler: public CustomEventHandler +{ + void OnResponse(http_sdk::Response &response) noexcept override + { + ASSERT_EQ(200, response.GetStatusCode()); + ASSERT_EQ(response.GetBody().size(), 0); + is_called_ = true; + }; +}; + +class PostEventHandler: public CustomEventHandler +{ + void OnResponse(http_sdk::Response &response) noexcept override + { + ASSERT_EQ(200, response.GetStatusCode()); + std::string body(response.GetBody().begin(),response.GetBody().end()); + ASSERT_EQ(body, "{'k1':'v1', 'k2':'v2', 'k3':'v3'}"); + is_called_ = true; + } +}; + +class BasicCurlHttpTests : public ::testing::Test, + public HTTP_SERVER_NS::HttpRequestCallback +{ + protected: + HTTP_SERVER_NS::HttpServer server_; + std::string server_address_; + std::atomic is_setup_; + std::atomic is_running_; + std::vector received_requests_; + std::mutex mtx_requests; + std::condition_variable cv_got_events; + std::mutex cv_m; + + + public: + BasicCurlHttpTests() : + is_setup_(false) , + is_running_(false) + {}; + + virtual void SetUp() override + { + if (is_setup_.exchange(true)) + { + return; + } + int port = server_.addListeningPort(HTTP_PORT); + std::ostringstream os; + os << "localhost:" << port; + server_address_ = "http://" + os.str() + "/simple/"; + server_.setServerName(os.str()); + server_.setKeepalive(false); + server_.addHandler("/simple/", *this); + server_.addHandler("/get/", *this); + server_.addHandler("/post/", *this); + server_.start(); + is_running_ = true; + } + + virtual void TearDown() override + { + if (!is_setup_.exchange(false)) + return; + server_.stop(); + is_running_ = false; + } + + virtual int onHttpRequest(HTTP_SERVER_NS::HttpRequest const &request, HTTP_SERVER_NS::HttpResponse &response) override + { + if (request.uri == "/get/") + { + std::unique_lock lk(mtx_requests); + received_requests_.push_back(request); + response.headers["Content-Type"] = "text/plain"; + return 200; + } + if (request.uri == "/post/") + { + std::unique_lock lk(mtx_requests); + received_requests_.push_back(request); + response.headers["Content-Type"] = "application/json"; + response.body = "{'k1':'v1', 'k2':'v2', 'k3':'v3'}"; + return 200; + } + return 404; + + } + + bool waitForRequests(unsigned timeOutSec, unsigned expected_count = 1) + { + std::unique_lock lk(cv_m); + if (cv_got_events.wait_for(lk, std::chrono::milliseconds(1000 * timeOutSec), [&] { return received_requests_.size() >= expected_count; })) + { + return true; + } + return false; + } +}; + +TEST_F(BasicCurlHttpTests, DoNothing) +{ +} + +TEST_F(BasicCurlHttpTests, SendGetRequest) +{ + received_requests_.clear(); + curl::SessionManager session_manager; + + auto session = session_manager.CreateSession("127.0.0.1", HTTP_PORT); + auto request = session->CreateRequest(); + request->SetUri("get/"); + GetEventHandler *handler = new GetEventHandler(); + session->SendRequest(*handler); + ASSERT_TRUE(waitForRequests(1,1)); + session->FinishSession(); + ASSERT_TRUE(handler->is_called_); + delete handler; +} + +TEST_F(BasicCurlHttpTests, SendPostRequest) +{ + received_requests_.clear(); + curl::SessionManager session_manager; + + auto session = session_manager.CreateSession("127.0.0.1", HTTP_PORT); + auto request = session->CreateRequest(); + request->SetUri("post/"); + request->SetMethod(http_sdk::Method::Post); + PostEventHandler *handler = new PostEventHandler(); + session->SendRequest(*handler); + ASSERT_TRUE(waitForRequests(1,1)); + session->FinishSession(); + ASSERT_TRUE(handler->is_called_); + delete handler; +} + + + diff --git a/ext/include/opentelemetry/ext/http/server/http_server.h b/ext/include/opentelemetry/ext/http/server/http_server.h index 672bf95c6a..c9aa8273c4 100644 --- a/ext/include/opentelemetry/ext/http/server/http_server.h +++ b/ext/include/opentelemetry/ext/http/server/http_server.h @@ -24,6 +24,8 @@ # ifdef LOG_TRACE # undef LOG_TRACE # define LOG_TRACE(x, ...) printf(x "\n", __VA_ARGS__) +# undef LOG_INFO +# define LOG_INFO LOG_TRACE # endif #endif @@ -249,7 +251,7 @@ class HttpServer : private SocketTools::Reactor::SocketCallback protected: virtual void onSocketAcceptable(SocketTools::Socket socket) override { - LOG_TRACE("HttpServer: accepting socket fd=0x%llx", socket.m_sock); + LOG_TRACE("HttpServer: accepting socket fd=0x%d", socket.m_sock); assert(std::find(m_listeningSockets.begin(), m_listeningSockets.end(), socket) != m_listeningSockets.end()); @@ -269,7 +271,7 @@ class HttpServer : private SocketTools::Reactor::SocketCallback virtual void onSocketReadable(SocketTools::Socket socket) override { - LOG_TRACE("HttpServer: reading socket fd=0x%llx", socket.m_sock); + LOG_TRACE("HttpServer: reading socket fd=0x%d", socket.m_sock); // No thread-safety here! assert(std::find(m_listeningSockets.begin(), m_listeningSockets.end(), socket) == m_listeningSockets.end()); @@ -297,7 +299,7 @@ class HttpServer : private SocketTools::Reactor::SocketCallback virtual void onSocketWritable(SocketTools::Socket socket) override { - LOG_TRACE("HttpServer: writing socket fd=0x%llx", socket.m_sock); + LOG_TRACE("HttpServer: writing socket fd=0x%d", socket.m_sock); // No thread-safety here! assert(std::find(m_listeningSockets.begin(), m_listeningSockets.end(), socket) == @@ -319,7 +321,7 @@ class HttpServer : private SocketTools::Reactor::SocketCallback virtual void onSocketClosed(SocketTools::Socket socket) override { - LOG_TRACE("HttpServer: closing socket fd=0x%llx", socket.m_sock); + LOG_TRACE("HttpServer: closing socket fd=0x%d", socket.m_sock); assert(std::find(m_listeningSockets.begin(), m_listeningSockets.end(), socket) == m_listeningSockets.end()); @@ -721,7 +723,6 @@ class HttpServer : private SocketTools::Reactor::SocketCallback conn.response.message.clear(); conn.response.headers.clear(); conn.response.body.clear(); - if (conn.response.code == 0) { conn.response.code = 404; // Not Found diff --git a/sdk/include/opentelemetry/sdk/common/http_client.h b/sdk/include/opentelemetry/sdk/common/http_client.h index 63b0dd4d30..11615d026a 100644 --- a/sdk/include/opentelemetry/sdk/common/http_client.h +++ b/sdk/include/opentelemetry/sdk/common/http_client.h @@ -60,19 +60,23 @@ enum class Method enum class SessionState { + CreateFailed, // session create failed + Created, // session created + Destroy, // session destroyed + Connecting, // connecting to peer + ConnectFailed, // connection failed + Connected, // connected + Sending, // sending request + SendFailed, // request send failed + Response, // response received + SSLHandshakeFailed, // SSL Handshake failed + TimedOut , // request timedout + NetworkError, // network error + ReadError, // error reading response + WriteError, // error writing request + Cancelled // (manually) cancelled +}; - CreateFailed, - Created, - SendFailed, - Connecting, - ConnectFailed, - Connected, - Sending, - Response, - Destroy, - Cancelled, - SSLHandshakeFailed -} ; using Byte = uint8_t; using StatusCode = uint16_t; From aef8b897dbebb20f67b24b64964ebb343e7ae32b Mon Sep 17 00:00:00 2001 From: Lalit Kumar Bhasin Date: Tue, 3 Nov 2020 09:37:14 +0530 Subject: [PATCH 04/32] format --- CMakeLists.txt | 15 +- .../exporters/common/http/http_client_curl.h | 69 +- .../common/http/http_operation_curl.h | 876 +++++++++--------- .../exporters/common/http/main.cc | 38 - exporters/test/CMakeLists.txt | 5 +- exporters/test/curl_http_test.cc | 241 +++-- .../ext/http/server/http_server.h | 4 +- .../opentelemetry/sdk/common/http_client.h | 33 +- 8 files changed, 617 insertions(+), 664 deletions(-) delete mode 100644 exporters/include/opentelemetry/exporters/common/http/main.cc diff --git a/CMakeLists.txt b/CMakeLists.txt index 73cbc5c159..578d531c05 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -16,8 +16,7 @@ option(WITH_OTPROTOCOL option(WITH_PROMETHEUS "Whether to include the Prometheus Client in the SDK" OFF) -option(WITH_CURL "Whether to include HTTP CURL Client in the SDK" - OFF) +option(WITH_CURL "Whether to include HTTP CURL Client in the SDK" OFF) option(WITH_TESTS "Whether to enable tests" ON) option(WITH_EXAMPLES "Whether to build examples" ON) @@ -31,12 +30,12 @@ if(WITH_TESTS) include(CTest) endif() -if (WITH_CURL) - set(CURL_LIBRARY "-lcurl") - find_package(CURL REQUIRED) - if (NOT CURL_FOUND) - message (FATAL_ERROR "libcurl not found! Have you installed deps?") - endif (NOT CURL_FOUND) +if(WITH_CURL) + set(CURL_LIBRARY "-lcurl") + find_package(CURL REQUIRED) + if(NOT CURL_FOUND) + message(FATAL_ERROR "libcurl not found! Have you installed deps?") + endif(NOT CURL_FOUND) endif() find_package(Threads) diff --git a/exporters/include/opentelemetry/exporters/common/http/http_client_curl.h b/exporters/include/opentelemetry/exporters/common/http/http_client_curl.h index e211ecc93d..a8117c5cf3 100644 --- a/exporters/include/opentelemetry/exporters/common/http/http_client_curl.h +++ b/exporters/include/opentelemetry/exporters/common/http/http_client_curl.h @@ -2,9 +2,9 @@ #include "http_operation_curl.h" +#include #include #include -#include OPENTELEMETRY_BEGIN_NAMESPACE namespace exporters @@ -16,7 +16,7 @@ namespace http namespace curl { -namespace http_sdk = opentelemetry::sdk::common::http; +namespace http_sdk = opentelemetry::sdk::common::http; const http_sdk::StatusCode Http_Ok = 200; class Request : public http_sdk::Request @@ -54,7 +54,7 @@ class Request : public http_sdk::Request http_sdk::Body body_; Headers headers_; nostd::string_view uri_; - std::chrono::milliseconds timeout_ms_{5000}; //ms + std::chrono::milliseconds timeout_ms_{5000}; // ms }; class Response : public http_sdk::Response @@ -66,7 +66,7 @@ class Response : public http_sdk::Response virtual const http_sdk::Body &GetBody() const noexcept override { return body_; } virtual bool ForEachHeader( - nostd::function_ref callable) const + nostd::function_ref callable) const noexcept override { for (const auto &header : headers_) @@ -79,8 +79,9 @@ class Response : public http_sdk::Response return true; } - virtual bool ForEachHeader(const nostd::string_view &name, - nostd::function_ref callable) const + virtual bool ForEachHeader( + const nostd::string_view &name, + nostd::function_ref callable) const noexcept override { auto range = headers_.equal_range(name); @@ -127,23 +128,27 @@ class Session : public http_sdk::Session virtual void SendRequest(http_sdk::EventHandler &callback) noexcept override { is_session_active_ = true; - std::string url = static_cast(host_) + "/" + static_cast(http_request_->uri_); + std::string url = + static_cast(host_) + "/" + static_cast(http_request_->uri_); auto callback_ptr = &callback; - curl_operation_.reset(new HttpOperation(http_request_->method_, url, callback_ptr, http_request_->headers_, http_request_->body_, false, http_request_->timeout_ms_.count())); - curl_operation_->SendAsync([this, callback_ptr](HttpOperation& operation){ - if (operation.WasAborted()) { - //Manually cancelled - callback_ptr->OnEvent(http_sdk::SessionState::Cancelled, ""); - } - - if (operation.GetResponseCode() >= CURL_LAST){ - // we have a http response - auto response = std::unique_ptr(new Response()); - response->headers_ = operation.GetResponseHeaders(); - response->body_ = operation.GetResponseBody(); - callback_ptr->OnResponse(*response); - } + curl_operation_.reset(new HttpOperation(http_request_->method_, url, callback_ptr, + http_request_->headers_, http_request_->body_, false, + http_request_->timeout_ms_.count())); + curl_operation_->SendAsync([this, callback_ptr](HttpOperation &operation) { + if (operation.WasAborted()) + { + // Manually cancelled + callback_ptr->OnEvent(http_sdk::SessionState::Cancelled, ""); + } + if (operation.GetResponseCode() >= CURL_LAST) + { + // we have a http response + auto response = std::unique_ptr(new Response()); + response->headers_ = operation.GetResponseHeaders(); + response->body_ = operation.GetResponseBody(); + callback_ptr->OnResponse(*response); + } }); is_session_active_ = false; } @@ -159,11 +164,8 @@ class Session : public http_sdk::Session curl_operation_->Finish(); return true; } - - virtual bool IsSessionActive() noexcept override - { - return is_session_active_; - } + + virtual bool IsSessionActive() noexcept override { return is_session_active_; } void SetId(uint64_t session_id) { session_id_ = session_id; } @@ -176,19 +178,14 @@ class Session : public http_sdk::Session bool is_session_active_; }; - class SessionManager : public http_sdk::SessionManager { public: - - SessionManager() - { - curl_global_init(CURL_GLOBAL_ALL); - } + SessionManager() { curl_global_init(CURL_GLOBAL_ALL); } std::shared_ptr CreateSession(nostd::string_view host, - uint16_t port = 80) noexcept override + uint16_t port = 80) noexcept override { auto session = std::make_shared(*this, static_cast(host), port); auto session_id = ++next_session_id_; @@ -221,16 +218,14 @@ class SessionManager : public http_sdk::SessionManager sessions_.erase(session_id); } - ~SessionManager() { - curl_global_cleanup(); - } + ~SessionManager() { curl_global_cleanup(); } private: std::atomic next_session_id_; std::map> sessions_; }; -} // namespace curl +} // namespace curl } // namespace http } // namespace common } // namespace exporters diff --git a/exporters/include/opentelemetry/exporters/common/http/http_operation_curl.h b/exporters/include/opentelemetry/exporters/common/http/http_operation_curl.h index 64d8392c14..b76e9cbb8b 100644 --- a/exporters/include/opentelemetry/exporters/common/http/http_operation_curl.h +++ b/exporters/include/opentelemetry/exporters/common/http/http_operation_curl.h @@ -5,14 +5,14 @@ #include #include #include -#include +#include #include +#include #include -#include #ifdef _WIN32 -#include +# include #else -#include +# include #endif OPENTELEMETRY_BEGIN_NAMESPACE @@ -24,494 +24,500 @@ namespace http { namespace curl { - namespace http_sdk = opentelemetry::sdk::common::http; - const size_t default_http_conn_timeout = 5000; //ms - const std::string http_status_regexp = "HTTP\\/\\d\\.\\d (\\d+)\\ .*" ; - const std::string http_header_regexp = "(.*)\\: (.*)\\n*"; - - struct curl_ci { - bool operator() (const nostd::string_view & s1, const nostd::string_view& s2) const { - return std::lexicographical_compare( - s1.begin(), s1.end(), - s2.begin(), s2.end(), - [](char c1, char c2) { - return ::tolower(c1) < ::tolower(c2); - } - ); - } - }; - using Headers = std::multimap; - - -class HttpOperation { -public: +namespace http_sdk = opentelemetry::sdk::common::http; +const size_t default_http_conn_timeout = 5000; // ms +const std::string http_status_regexp = "HTTP\\/\\d\\.\\d (\\d+)\\ .*"; +const std::string http_header_regexp = "(.*)\\: (.*)\\n*"; + +struct curl_ci +{ + bool operator()(const nostd::string_view &s1, const nostd::string_view &s2) const + { + return std::lexicographical_compare( + s1.begin(), s1.end(), s2.begin(), s2.end(), + [](char c1, char c2) { return ::tolower(c1) < ::tolower(c2); }); + } +}; +using Headers = std::multimap; - void DispatchEvent(http_sdk::SessionState type, std::string reason = "") +class HttpOperation +{ +public: + void DispatchEvent(http_sdk::SessionState type, std::string reason = "") + { + if (callback_ != nullptr) + { + callback_->OnEvent(type, reason); + } + } + + std::atomic is_aborted_; // Set to 'true' when async callback is aborted + std::atomic is_finished_; // Set to 'true' when async callback is finished. + + /** + * Create local CURL instance for url and body + * + * @param url + * @param body + * @param httpConnTimeout HTTP connection timeout in seconds + * @param httpReadTimeout HTTP read timeout in seconds + */ + HttpOperation(http_sdk::Method method, + std::string url, + http_sdk::EventHandler *callback, + // Default empty headers and empty request body + const Headers &request_headers = Headers(), + const http_sdk::Body &request_body = http_sdk::Body(), + // Default connectivity and response size options + bool raw_response = false, + size_t http_conn_timeout = default_http_conn_timeout) + : + + // + method_(method), + url_(url), + callback_(callback), + + // Local vars + request_headers_(request_headers), + request_body_(request_body), + // Optional connection params + raw_response_(raw_response), + http_conn_timeout_(http_conn_timeout), + // Result + res_(CURLE_OK), + sockfd_(0), + is_aborted_(false), + is_finished_(false), + nread_(0) + { + response_.memory = nullptr; + response_.size = 0; + + /* get a curl handle */ + curl_ = curl_easy_init(); + if (!curl_) { - if(callback_ != nullptr) { - callback_->OnEvent(type, reason); - } + res_ = CURLE_FAILED_INIT; + DispatchEvent(http_sdk::SessionState::CreateFailed); + return; } - std::atomic is_aborted_; // Set to 'true' when async callback is aborted - std::atomic is_finished_; // Set to 'true' when async callback is finished. + curl_easy_setopt(curl_, CURLOPT_VERBOSE, 0); - /** - * Create local CURL instance for url and body - * - * @param url - * @param body - * @param httpConnTimeout HTTP connection timeout in seconds - * @param httpReadTimeout HTTP read timeout in seconds - */ - HttpOperation( - http_sdk::Method method, - std::string url, - http_sdk::EventHandler* callback, - // Default empty headers and empty request body - const Headers& request_headers = Headers(), - const http_sdk::Body& request_body = http_sdk::Body(), - // Default connectivity and response size options - bool raw_response = false, - size_t http_conn_timeout = default_http_conn_timeout) : - - // - method_(method), - url_(url), - callback_(callback), - - // Local vars - request_headers_(request_headers), - request_body_(request_body), - // Optional connection params - raw_response_(raw_response), - http_conn_timeout_(http_conn_timeout), - // Result - res_(CURLE_OK), - sockfd_(0), - is_aborted_(false), - is_finished_(false), - nread_(0) + // Specify target URL + curl_easy_setopt(curl_, CURLOPT_URL, url_.c_str()); + + // TODO: support ssl cert verification for https request + curl_easy_setopt(curl_, CURLOPT_SSL_VERIFYPEER, 0); // 1L + curl_easy_setopt(curl_, CURLOPT_SSL_VERIFYHOST, 0); // 2L + + // Specify our custom headers + for (auto &kv : this->request_headers_) { - response_.memory = nullptr; - response_.size = 0; - - /* get a curl handle */ - curl_ = curl_easy_init(); - if(!curl_) - { - res_ = CURLE_FAILED_INIT; - DispatchEvent(http_sdk::SessionState::CreateFailed); - return; - } - - curl_easy_setopt(curl_, CURLOPT_VERBOSE, 0); - - // Specify target URL - curl_easy_setopt(curl_, CURLOPT_URL, url_.c_str()); - - // TODO: support ssl cert verification for https request - curl_easy_setopt(curl_, CURLOPT_SSL_VERIFYPEER, 0); // 1L - curl_easy_setopt(curl_, CURLOPT_SSL_VERIFYHOST, 0); // 2L - - // Specify our custom headers - for(auto &kv : this->request_headers_) - { - std::string header = static_cast(kv.first); - header += ": "; - header += static_cast(kv.second); - headers_chunk_ = curl_slist_append(headers_chunk_, header.c_str()); - } - - if(headers_chunk_ != nullptr) - { - curl_easy_setopt(curl_, CURLOPT_HTTPHEADER, headers_chunk_); - } - - DispatchEvent(http_sdk::SessionState::Created); + std::string header = static_cast(kv.first); + header += ": "; + header += static_cast(kv.second); + headers_chunk_ = curl_slist_append(headers_chunk_, header.c_str()); } - /** - * Destroy CURL instance - */ - virtual ~HttpOperation() + if (headers_chunk_ != nullptr) { - // Given the request has not been aborted we should wait for completion here - // This guarantees the lifetime of this request. - if (result_.valid()) - { - result_.wait(); - } - //TBD - Need to be uncomment. This will callback instance is deleted. - //DispatchEvent(http_sdk::SessionState::Destroy); - res_ = CURLE_OK; - curl_easy_cleanup(curl_); - curl_slist_free_all(headers_chunk_); - ReleaseResponse(); + curl_easy_setopt(curl_, CURLOPT_HTTPHEADER, headers_chunk_); } - /** - * Finish CURL instance - */ - virtual void Finish() + DispatchEvent(http_sdk::SessionState::Created); + } + + /** + * Destroy CURL instance + */ + virtual ~HttpOperation() + { + // Given the request has not been aborted we should wait for completion here + // This guarantees the lifetime of this request. + if (result_.valid()) { - if (result_.valid() && !is_finished_) - { - result_.wait(); - is_finished_ = true; - } + result_.wait(); } - - /** - * Send request synchronously - */ - long Send() + // TBD - Need to be uncomment. This will callback instance is deleted. + // DispatchEvent(http_sdk::SessionState::Destroy); + res_ = CURLE_OK; + curl_easy_cleanup(curl_); + curl_slist_free_all(headers_chunk_); + ReleaseResponse(); + } + + /** + * Finish CURL instance + */ + virtual void Finish() + { + if (result_.valid() && !is_finished_) { - ReleaseResponse(); - // Request buffer - const void *request = (request_body_.empty())? NULL: &request_body_[0]; - const size_t req_size = request_body_.size(); - if(!curl_) - { - res_ = CURLE_FAILED_INIT; - DispatchEvent(http_sdk::SessionState::SendFailed); - return res_; - } - - // TODO: control local port to use - // curl_easy_setopt(curl, CURLOPT_LOCALPORT, dcf_port); - - // Perform initial connect, handling the timeout if needed - - curl_easy_setopt(curl_, CURLOPT_CONNECT_ONLY, 1L); - DispatchEvent(http_sdk::SessionState::Connecting); - res_ = curl_easy_perform(curl_); - - if(CURLE_OK != res_) - { - DispatchEvent(http_sdk::SessionState::ConnectFailed, curl_easy_strerror(res_)); // couldn't connect - stage 1 - return res_; - } - - /* Extract the socket from the curl handle - we'll need it for waiting. - * Note that this API takes a pointer to a 'long' while we use - * curl_socket_t for sockets otherwise. - */ - long sockextr = 0 ; - res_ = curl_easy_getinfo(curl_, CURLINFO_LASTSOCKET, &sockextr); - - if(CURLE_OK != res_) - { - DispatchEvent(http_sdk::SessionState::ConnectFailed, curl_easy_strerror(res_)); // couldn't connect - stage 2 - return res_; - } - - /* wait for the socket to become ready for sending */ - sockfd_ = sockextr; - if( !WaitOnSocket(sockfd_, 0, http_conn_timeout_ ) || is_aborted_) - { - res_ = CURLE_OPERATION_TIMEDOUT; - DispatchEvent(http_sdk::SessionState::ConnectFailed, " Is aborted: " + std::to_string(is_aborted_.load())); // couldn't connect - stage 3 - return res_; - } - - DispatchEvent(http_sdk::SessionState::Connected); - // once connection is there - switch back to easy perform for HTTP post - curl_easy_setopt(curl_, CURLOPT_CONNECT_ONLY, 0); - - // send all data to our callback function - if (raw_response_) - { - curl_easy_setopt(curl_, CURLOPT_HEADER, true); - curl_easy_setopt(curl_, CURLOPT_WRITEFUNCTION, (void *)&WriteMemoryCallback); - curl_easy_setopt(curl_, CURLOPT_WRITEDATA, (void *)&response_); - } else { - curl_easy_setopt(curl_, CURLOPT_WRITEFUNCTION, (void *)&WriteVectorCallback); - curl_easy_setopt(curl_, CURLOPT_HEADERDATA, (void *)&resp_headers_); - curl_easy_setopt(curl_, CURLOPT_WRITEDATA, (void *)&resp_body_); - } - - // TODO: only two methods supported for now - POST and GET - if (method_ == http_sdk::Method::Post) - { - // POST - curl_easy_setopt(curl_, CURLOPT_POST, true); - curl_easy_setopt(curl_, CURLOPT_POSTFIELDS, (const char *)request); - curl_easy_setopt(curl_, CURLOPT_POSTFIELDSIZE, req_size); - } else if (method_ == http_sdk::Method::Get) - { - // GET - } else - { - res_ = CURLE_UNSUPPORTED_PROTOCOL; - return res_; - } - - // abort if slower than 4kb/sec during 30 seconds - curl_easy_setopt(curl_, CURLOPT_LOW_SPEED_TIME, 30L); - curl_easy_setopt(curl_, CURLOPT_LOW_SPEED_LIMIT, 4096); - DispatchEvent(http_sdk::SessionState::Sending); - - res_ = curl_easy_perform(curl_); - if(CURLE_OK != res_) - { - DispatchEvent(http_sdk::SessionState::SendFailed, curl_easy_strerror(res_)); - return res_; - } - - /* Code snippet to parse raw HTTP response. This might come in handy - * if we ever consider to handle the raw upload instead of curl_easy_perform - ... - std::string resp((const char *)response); - std::regex http_status_regex(HTTP_STATUS_REGEXP); - std::smatch match; - if(std::regex_search(resp, match, http_status_regex)) - http_code = std::stol(match[1]); - ... - */ - - /* libcurl is nice enough to parse the http response code itself: */ - curl_easy_getinfo(curl_, CURLINFO_RESPONSE_CODE, &res_); - // We got some response from server. Dump the contents. - DispatchEvent(http_sdk::SessionState::Response); - - // This function returns: - // - on success: HTTP status code. - // - on failure: CURL error code. - // The two sets of enums (CURLE, HTTP codes) - do not intersect, so we collapse them in one set. - return res_; + result_.wait(); + is_finished_ = true; } - - std::future & SendAsync(std::function callback = nullptr) { - result_ = std::async(std::launch::async, [this, callback] { - long result = Send(); - if (callback!=nullptr) - { - callback(*this); - } - return result; - }); - return result_; + } + + /** + * Send request synchronously + */ + long Send() + { + ReleaseResponse(); + // Request buffer + const void *request = (request_body_.empty()) ? NULL : &request_body_[0]; + const size_t req_size = request_body_.size(); + if (!curl_) + { + res_ = CURLE_FAILED_INIT; + DispatchEvent(http_sdk::SessionState::SendFailed); + return res_; } - /** - * Get HTTP response code. This function returns CURL error code if HTTP response code is invalid. - */ - long GetResponseCode() + // TODO: control local port to use + // curl_easy_setopt(curl, CURLOPT_LOCALPORT, dcf_port); + + // Perform initial connect, handling the timeout if needed + + curl_easy_setopt(curl_, CURLOPT_CONNECT_ONLY, 1L); + DispatchEvent(http_sdk::SessionState::Connecting); + res_ = curl_easy_perform(curl_); + + if (CURLE_OK != res_) { - return res_; + DispatchEvent(http_sdk::SessionState::ConnectFailed, + curl_easy_strerror(res_)); // couldn't connect - stage 1 + return res_; } - /** - * Get whether or not response was programmatically aborted + /* Extract the socket from the curl handle - we'll need it for waiting. + * Note that this API takes a pointer to a 'long' while we use + * curl_socket_t for sockets otherwise. */ - bool WasAborted() + long sockextr = 0; + res_ = curl_easy_getinfo(curl_, CURLINFO_LASTSOCKET, &sockextr); + + if (CURLE_OK != res_) { - return is_aborted_.load(); + DispatchEvent(http_sdk::SessionState::ConnectFailed, + curl_easy_strerror(res_)); // couldn't connect - stage 2 + return res_; } - /** - * Return a copy of resposne headers - * - * @return - */ - Headers GetResponseHeaders() + /* wait for the socket to become ready for sending */ + sockfd_ = sockextr; + if (!WaitOnSocket(sockfd_, 0, http_conn_timeout_) || is_aborted_) { - Headers result; - if (resp_headers_.size() == 0) - return result; - - std::stringstream ss; - std::string headers((const char *)&resp_headers_[0], resp_headers_.size()); - ss.str(headers); - - std::string header; - while (std::getline(ss, header, '\n')) { - std::smatch match; - std::regex http_headers_regex(http_header_regexp); - if (std::regex_search(header, match, http_headers_regex)) - result.insert(std::pair(match[1], match[2])); - //result[match.str(1)] = match[2]; // Key: value - } - return result; + res_ = CURLE_OPERATION_TIMEDOUT; + DispatchEvent( + http_sdk::SessionState::ConnectFailed, + " Is aborted: " + std::to_string(is_aborted_.load())); // couldn't connect - stage 3 + return res_; } - /** - * Return a copy of response body - * - * @return - */ - std::vector GetResponseBody() + DispatchEvent(http_sdk::SessionState::Connected); + // once connection is there - switch back to easy perform for HTTP post + curl_easy_setopt(curl_, CURLOPT_CONNECT_ONLY, 0); + + // send all data to our callback function + if (raw_response_) { - return resp_body_; + curl_easy_setopt(curl_, CURLOPT_HEADER, true); + curl_easy_setopt(curl_, CURLOPT_WRITEFUNCTION, (void *)&WriteMemoryCallback); + curl_easy_setopt(curl_, CURLOPT_WRITEDATA, (void *)&response_); + } + else + { + curl_easy_setopt(curl_, CURLOPT_WRITEFUNCTION, (void *)&WriteVectorCallback); + curl_easy_setopt(curl_, CURLOPT_HEADERDATA, (void *)&resp_headers_); + curl_easy_setopt(curl_, CURLOPT_WRITEDATA, (void *)&resp_body_); } - /** - * Return a raw copy of response headers+body - * - * @return - */ - std::vector GetRawResponse() + // TODO: only two methods supported for now - POST and GET + if (method_ == http_sdk::Method::Post) + { + // POST + curl_easy_setopt(curl_, CURLOPT_POST, true); + curl_easy_setopt(curl_, CURLOPT_POSTFIELDS, (const char *)request); + curl_easy_setopt(curl_, CURLOPT_POSTFIELDSIZE, req_size); + } + else if (method_ == http_sdk::Method::Get) { - std::vector result; - if ((response_.memory!=nullptr)&&(response_.size!=0)) - result.insert(result.end(), (const char *)response_.memory, ((const char *)response_.memory) + response_.size); - return result; + // GET + } + else + { + res_ = CURLE_UNSUPPORTED_PROTOCOL; + return res_; } - /** - * Release memory allocated for response - */ - void ReleaseResponse() + // abort if slower than 4kb/sec during 30 seconds + curl_easy_setopt(curl_, CURLOPT_LOW_SPEED_TIME, 30L); + curl_easy_setopt(curl_, CURLOPT_LOW_SPEED_LIMIT, 4096); + DispatchEvent(http_sdk::SessionState::Sending); + + res_ = curl_easy_perform(curl_); + if (CURLE_OK != res_) { - if (response_.memory!=nullptr) { - free(response_.memory); - response_.memory = nullptr; - response_.size = 0; - } - resp_headers_.clear(); - resp_body_.clear(); + DispatchEvent(http_sdk::SessionState::SendFailed, curl_easy_strerror(res_)); + return res_; } - /** - * Abort request in connecting or reading state. + /* Code snippet to parse raw HTTP response. This might come in handy + * if we ever consider to handle the raw upload instead of curl_easy_perform + ... + std::string resp((const char *)response); + std::regex http_status_regex(HTTP_STATUS_REGEXP); + std::smatch match; + if(std::regex_search(resp, match, http_status_regex)) + http_code = std::stol(match[1]); + ... */ - void Abort() + + /* libcurl is nice enough to parse the http response code itself: */ + curl_easy_getinfo(curl_, CURLINFO_RESPONSE_CODE, &res_); + // We got some response from server. Dump the contents. + DispatchEvent(http_sdk::SessionState::Response); + + // This function returns: + // - on success: HTTP status code. + // - on failure: CURL error code. + // The two sets of enums (CURLE, HTTP codes) - do not intersect, so we collapse them in one set. + return res_; + } + + std::future &SendAsync(std::function callback = nullptr) + { + result_ = std::async(std::launch::async, [this, callback] { + long result = Send(); + if (callback != nullptr) + { + callback(*this); + } + return result; + }); + return result_; + } + + /** + * Get HTTP response code. This function returns CURL error code if HTTP response code is invalid. + */ + long GetResponseCode() { return res_; } + + /** + * Get whether or not response was programmatically aborted + */ + bool WasAborted() { return is_aborted_.load(); } + + /** + * Return a copy of resposne headers + * + * @return + */ + Headers GetResponseHeaders() + { + Headers result; + if (resp_headers_.size() == 0) + return result; + + std::stringstream ss; + std::string headers((const char *)&resp_headers_[0], resp_headers_.size()); + ss.str(headers); + + std::string header; + while (std::getline(ss, header, '\n')) { - is_aborted_ = true; - if (curl_ != nullptr) - { - // Simply close the socket - connection reset by peer - if (sockfd_) - { - ::close(sockfd_); - sockfd_ = 0; - } - } + std::smatch match; + std::regex http_headers_regex(http_header_regexp); + if (std::regex_search(header, match, http_headers_regex)) + result.insert(std::pair(match[1], match[2])); + // result[match.str(1)] = match[2]; // Key: value } - - CURL *GetHandle() + return result; + } + + /** + * Return a copy of response body + * + * @return + */ + std::vector GetResponseBody() { return resp_body_; } + + /** + * Return a raw copy of response headers+body + * + * @return + */ + std::vector GetRawResponse() + { + std::vector result; + if ((response_.memory != nullptr) && (response_.size != 0)) + result.insert(result.end(), (const char *)response_.memory, + ((const char *)response_.memory) + response_.size); + return result; + } + + /** + * Release memory allocated for response + */ + void ReleaseResponse() + { + if (response_.memory != nullptr) { - return curl_; + free(response_.memory); + response_.memory = nullptr; + response_.size = 0; } + resp_headers_.clear(); + resp_body_.clear(); + } + + /** + * Abort request in connecting or reading state. + */ + void Abort() + { + is_aborted_ = true; + if (curl_ != nullptr) + { + // Simply close the socket - connection reset by peer + if (sockfd_) + { + ::close(sockfd_); + sockfd_ = 0; + } + } + } + + CURL *GetHandle() { return curl_; } protected: - const bool raw_response_; // Do not split response headers from response body - const size_t http_conn_timeout_; // Timeout for connect. Default: 5000ms - - CURL *curl_; // Local curl instance - CURLcode res_; // Curl result OR HTTP status code if successful - - http_sdk::EventHandler* callback_ = nullptr; - - // Request values - http_sdk::Method method_; - std::string url_; - const Headers& request_headers_; - const http_sdk::Body& request_body_; - struct curl_slist *headers_chunk_ = nullptr; - - // Processed response headers and body - std::vector resp_headers_; - std::vector resp_body_; - - // Socket parameters - curl_socket_t sockfd_; - - //long sockextr_ = 0; - - curl_off_t nread_; - size_t sendlen_ = 0; // # bytes sent by client - size_t acklen_ = 0; // # bytes ack by server - - std::future result_; - - /** - * Helper routine to wait for data on socket - * - * @param sockfd - * @param for_recv - * @param timeout_ms - * @return - */ - static int WaitOnSocket(curl_socket_t sockfd, int for_recv, long timeout_ms) - { - struct timeval tv; - fd_set infd, outfd, errfd; - int res; + const bool raw_response_; // Do not split response headers from response body + const size_t http_conn_timeout_; // Timeout for connect. Default: 5000ms - tv.tv_sec = timeout_ms / 1000; - tv.tv_usec = (timeout_ms % 1000) * 1000; + CURL *curl_; // Local curl instance + CURLcode res_; // Curl result OR HTTP status code if successful - FD_ZERO(&infd); - FD_ZERO(&outfd); - FD_ZERO(&errfd); + http_sdk::EventHandler *callback_ = nullptr; - FD_SET(sockfd, &errfd); /* always check for error */ + // Request values + http_sdk::Method method_; + std::string url_; + const Headers &request_headers_; + const http_sdk::Body &request_body_; + struct curl_slist *headers_chunk_ = nullptr; - if(for_recv) { - FD_SET(sockfd, &infd); - } else { - FD_SET(sockfd, &outfd); - } + // Processed response headers and body + std::vector resp_headers_; + std::vector resp_body_; - /* select() returns the number of signalled sockets or -1 */ - res = select((int)sockfd + 1, &infd, &outfd, &errfd, &tv); - return res; - } + // Socket parameters + curl_socket_t sockfd_; - // Raw response buffer - struct MemoryStruct { - char *memory; - size_t size; - } response_; - - /** - * Old-school memory allocator - * - * @param contents - * @param size - * @param nmemb - * @param userp - * @return - */ - static size_t WriteMemoryCallback(void *contents, size_t size, size_t nmemb, void *userp) - { - size_t realsize = size * nmemb; - struct MemoryStruct *mem = (struct MemoryStruct *)userp; + // long sockextr_ = 0; + + curl_off_t nread_; + size_t sendlen_ = 0; // # bytes sent by client + size_t acklen_ = 0; // # bytes ack by server + + std::future result_; + + /** + * Helper routine to wait for data on socket + * + * @param sockfd + * @param for_recv + * @param timeout_ms + * @return + */ + static int WaitOnSocket(curl_socket_t sockfd, int for_recv, long timeout_ms) + { + struct timeval tv; + fd_set infd, outfd, errfd; + int res; - mem->memory = (char *)(realloc(mem->memory, mem->size + realsize + 1)); - if(mem->memory == NULL) { - /* out of memory! */ - return 0; - } + tv.tv_sec = timeout_ms / 1000; + tv.tv_usec = (timeout_ms % 1000) * 1000; - memcpy(&(mem->memory[mem->size]), contents, realsize); - mem->size += realsize; - mem->memory[mem->size] = 0; + FD_ZERO(&infd); + FD_ZERO(&outfd); + FD_ZERO(&errfd); - return realsize; + FD_SET(sockfd, &errfd); /* always check for error */ + + if (for_recv) + { + FD_SET(sockfd, &infd); + } + else + { + FD_SET(sockfd, &outfd); } - /** - * C++ STL std::string allocator - * - * @param ptr - * @param size - * @param nmemb - * @param data - * @return - */ - static size_t WriteVectorCallback(void *ptr, size_t size, size_t nmemb, std::vector* data) + /* select() returns the number of signalled sockets or -1 */ + res = select((int)sockfd + 1, &infd, &outfd, &errfd, &tv); + return res; + } + + // Raw response buffer + struct MemoryStruct + { + char *memory; + size_t size; + } response_; + + /** + * Old-school memory allocator + * + * @param contents + * @param size + * @param nmemb + * @param userp + * @return + */ + static size_t WriteMemoryCallback(void *contents, size_t size, size_t nmemb, void *userp) + { + size_t realsize = size * nmemb; + struct MemoryStruct *mem = (struct MemoryStruct *)userp; + + mem->memory = (char *)(realloc(mem->memory, mem->size + realsize + 1)); + if (mem->memory == NULL) { - if (data!=nullptr) { - const unsigned char * begin = (unsigned char *)(ptr); - const unsigned char * end = begin + size * nmemb; - data->insert( data->end(), begin, end); - } - return size * nmemb; + /* out of memory! */ + return 0; } + memcpy(&(mem->memory[mem->size]), contents, realsize); + mem->size += realsize; + mem->memory[mem->size] = 0; + + return realsize; + } + + /** + * C++ STL std::string allocator + * + * @param ptr + * @param size + * @param nmemb + * @param data + * @return + */ + static size_t WriteVectorCallback(void *ptr, + size_t size, + size_t nmemb, + std::vector *data) + { + if (data != nullptr) + { + const unsigned char *begin = (unsigned char *)(ptr); + const unsigned char *end = begin + size * nmemb; + data->insert(data->end(), begin, end); + } + return size * nmemb; + } }; } // namespace curl } // namespace http diff --git a/exporters/include/opentelemetry/exporters/common/http/main.cc b/exporters/include/opentelemetry/exporters/common/http/main.cc deleted file mode 100644 index a0b437800f..0000000000 --- a/exporters/include/opentelemetry/exporters/common/http/main.cc +++ /dev/null @@ -1,38 +0,0 @@ -#include "http_client_curl.h" -#include "opentelemetry/nostd/string_view.h" -#include - -namespace curl = opentelemetry::exporters::common::http::curl; -namespace http_sdk = opentelemetry::sdk::common::http; - -class CustomEventHandler: public http_sdk::EventHandler -{ -public: - virtual void OnResponse(http_sdk::Response &response) noexcept override - { - std::cout << "LALIT -> In main : " << response.GetStatusCode() << "\n"; - } - - virtual void OnEvent(http_sdk::SessionState state, opentelemetry::nostd::string_view reason) noexcept override - { - std::cout << reason ; - } - - virtual void OnConnecting(const http_sdk::SSLCertificate &) noexcept {} - - virtual ~CustomEventHandler() = default; -}; - -int main() -{ - curl::SessionManager sessionManager; - - auto session = sessionManager.CreateSession("127.0.0.1", 8080); - auto request = session->CreateRequest(); - CustomEventHandler *handler = new CustomEventHandler(); - session->SendRequest(*handler); - session->FinishSession(); - std::cout << " =====> LALIT :::===> deleting handler\n"; - delete handler; -} - diff --git a/exporters/test/CMakeLists.txt b/exporters/test/CMakeLists.txt index 8b573ef21c..285b0b2dc6 100644 --- a/exporters/test/CMakeLists.txt +++ b/exporters/test/CMakeLists.txt @@ -1,9 +1,8 @@ if(WITH_CURL) add_executable("curl_http_test" "curl_http_test.cc") include_directories(${CURL_INCLUDE_DIR}) - target_link_libraries( "curl_http_test" - ${GTEST_BOTH_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT} - ${CURL_LIBRARIES}) + target_link_libraries("curl_http_test" ${GTEST_BOTH_LIBRARIES} + ${CMAKE_THREAD_LIBS_INIT} ${CURL_LIBRARIES}) gtest_add_tests(TARGET "curl_http_test" TEST_PREFIX curl. TEST_LIST "curl_http_test") endif() diff --git a/exporters/test/curl_http_test.cc b/exporters/test/curl_http_test.cc index 46df29466c..06d38ea9a6 100644 --- a/exporters/test/curl_http_test.cc +++ b/exporters/test/curl_http_test.cc @@ -1,167 +1,160 @@ #include "opentelemetry/exporters/common/http/http_client_curl.h" #include "opentelemetry/ext/http/server/http_server.h" -#include -#include #include +#include +#include #include +#include #include -#include #include -#define HTTP_PORT 19000 +#define HTTP_PORT 19000 #include -namespace curl = opentelemetry::exporters::common::http::curl; +namespace curl = opentelemetry::exporters::common::http::curl; namespace http_sdk = opentelemetry::sdk::common::http; -class CustomEventHandler: public http_sdk::EventHandler +class CustomEventHandler : public http_sdk::EventHandler { public: - virtual void OnResponse(http_sdk::Response &response) noexcept override {}; - virtual void OnEvent(http_sdk::SessionState state, opentelemetry::nostd::string_view reason) noexcept override {} + virtual void OnResponse(http_sdk::Response &response) noexcept override{}; + virtual void OnEvent(http_sdk::SessionState state, + opentelemetry::nostd::string_view reason) noexcept override + {} virtual void OnConnecting(const http_sdk::SSLCertificate &) noexcept {} - virtual ~CustomEventHandler() = default; - bool is_called_ = false; + virtual ~CustomEventHandler() = default; + bool is_called_ = false; }; -class GetEventHandler: public CustomEventHandler +class GetEventHandler : public CustomEventHandler { - void OnResponse(http_sdk::Response &response) noexcept override - { - ASSERT_EQ(200, response.GetStatusCode()); - ASSERT_EQ(response.GetBody().size(), 0); - is_called_ = true; - }; + void OnResponse(http_sdk::Response &response) noexcept override + { + ASSERT_EQ(200, response.GetStatusCode()); + ASSERT_EQ(response.GetBody().size(), 0); + is_called_ = true; + }; }; -class PostEventHandler: public CustomEventHandler +class PostEventHandler : public CustomEventHandler { - void OnResponse(http_sdk::Response &response) noexcept override - { - ASSERT_EQ(200, response.GetStatusCode()); - std::string body(response.GetBody().begin(),response.GetBody().end()); - ASSERT_EQ(body, "{'k1':'v1', 'k2':'v2', 'k3':'v3'}"); - is_called_ = true; - } + void OnResponse(http_sdk::Response &response) noexcept override + { + ASSERT_EQ(200, response.GetStatusCode()); + std::string body(response.GetBody().begin(), response.GetBody().end()); + ASSERT_EQ(body, "{'k1':'v1', 'k2':'v2', 'k3':'v3'}"); + is_called_ = true; + } }; -class BasicCurlHttpTests : public ::testing::Test, - public HTTP_SERVER_NS::HttpRequestCallback +class BasicCurlHttpTests : public ::testing::Test, public HTTP_SERVER_NS::HttpRequestCallback { - protected: - HTTP_SERVER_NS::HttpServer server_; - std::string server_address_; - std::atomic is_setup_; - std::atomic is_running_; - std::vector received_requests_; - std::mutex mtx_requests; - std::condition_variable cv_got_events; - std::mutex cv_m; - - - public: - BasicCurlHttpTests() : - is_setup_(false) , - is_running_(false) - {}; - - virtual void SetUp() override +protected: + HTTP_SERVER_NS::HttpServer server_; + std::string server_address_; + std::atomic is_setup_; + std::atomic is_running_; + std::vector received_requests_; + std::mutex mtx_requests; + std::condition_variable cv_got_events; + std::mutex cv_m; + +public: + BasicCurlHttpTests() : is_setup_(false), is_running_(false){}; + + virtual void SetUp() override + { + if (is_setup_.exchange(true)) { - if (is_setup_.exchange(true)) - { - return; - } - int port = server_.addListeningPort(HTTP_PORT); - std::ostringstream os; - os << "localhost:" << port; - server_address_ = "http://" + os.str() + "/simple/"; - server_.setServerName(os.str()); - server_.setKeepalive(false); - server_.addHandler("/simple/", *this); - server_.addHandler("/get/", *this); - server_.addHandler("/post/", *this); - server_.start(); - is_running_ = true; + return; } - - virtual void TearDown() override + int port = server_.addListeningPort(HTTP_PORT); + std::ostringstream os; + os << "localhost:" << port; + server_address_ = "http://" + os.str() + "/simple/"; + server_.setServerName(os.str()); + server_.setKeepalive(false); + server_.addHandler("/simple/", *this); + server_.addHandler("/get/", *this); + server_.addHandler("/post/", *this); + server_.start(); + is_running_ = true; + } + + virtual void TearDown() override + { + if (!is_setup_.exchange(false)) + return; + server_.stop(); + is_running_ = false; + } + + virtual int onHttpRequest(HTTP_SERVER_NS::HttpRequest const &request, + HTTP_SERVER_NS::HttpResponse &response) override + { + if (request.uri == "/get/") { - if (!is_setup_.exchange(false)) - return; - server_.stop(); - is_running_ = false; + std::unique_lock lk(mtx_requests); + received_requests_.push_back(request); + response.headers["Content-Type"] = "text/plain"; + return 200; } - - virtual int onHttpRequest(HTTP_SERVER_NS::HttpRequest const &request, HTTP_SERVER_NS::HttpResponse &response) override + if (request.uri == "/post/") { - if (request.uri == "/get/") - { - std::unique_lock lk(mtx_requests); - received_requests_.push_back(request); - response.headers["Content-Type"] = "text/plain"; - return 200; - } - if (request.uri == "/post/") - { - std::unique_lock lk(mtx_requests); - received_requests_.push_back(request); - response.headers["Content-Type"] = "application/json"; - response.body = "{'k1':'v1', 'k2':'v2', 'k3':'v3'}"; - return 200; - } - return 404; - + std::unique_lock lk(mtx_requests); + received_requests_.push_back(request); + response.headers["Content-Type"] = "application/json"; + response.body = "{'k1':'v1', 'k2':'v2', 'k3':'v3'}"; + return 200; } - - bool waitForRequests(unsigned timeOutSec, unsigned expected_count = 1) + return 404; + } + + bool waitForRequests(unsigned timeOutSec, unsigned expected_count = 1) + { + std::unique_lock lk(cv_m); + if (cv_got_events.wait_for(lk, std::chrono::milliseconds(1000 * timeOutSec), + [&] { return received_requests_.size() >= expected_count; })) { - std::unique_lock lk(cv_m); - if (cv_got_events.wait_for(lk, std::chrono::milliseconds(1000 * timeOutSec), [&] { return received_requests_.size() >= expected_count; })) - { - return true; - } - return false; + return true; } + return false; + } }; -TEST_F(BasicCurlHttpTests, DoNothing) -{ -} +TEST_F(BasicCurlHttpTests, DoNothing) {} TEST_F(BasicCurlHttpTests, SendGetRequest) { - received_requests_.clear(); - curl::SessionManager session_manager; - - auto session = session_manager.CreateSession("127.0.0.1", HTTP_PORT); - auto request = session->CreateRequest(); - request->SetUri("get/"); - GetEventHandler *handler = new GetEventHandler(); - session->SendRequest(*handler); - ASSERT_TRUE(waitForRequests(1,1)); - session->FinishSession(); - ASSERT_TRUE(handler->is_called_); - delete handler; + received_requests_.clear(); + curl::SessionManager session_manager; + + auto session = session_manager.CreateSession("127.0.0.1", HTTP_PORT); + auto request = session->CreateRequest(); + request->SetUri("get/"); + GetEventHandler *handler = new GetEventHandler(); + session->SendRequest(*handler); + ASSERT_TRUE(waitForRequests(1, 1)); + session->FinishSession(); + ASSERT_TRUE(handler->is_called_); + delete handler; } TEST_F(BasicCurlHttpTests, SendPostRequest) { - received_requests_.clear(); - curl::SessionManager session_manager; - - auto session = session_manager.CreateSession("127.0.0.1", HTTP_PORT); - auto request = session->CreateRequest(); - request->SetUri("post/"); - request->SetMethod(http_sdk::Method::Post); - PostEventHandler *handler = new PostEventHandler(); - session->SendRequest(*handler); - ASSERT_TRUE(waitForRequests(1,1)); - session->FinishSession(); - ASSERT_TRUE(handler->is_called_); - delete handler; + received_requests_.clear(); + curl::SessionManager session_manager; + + auto session = session_manager.CreateSession("127.0.0.1", HTTP_PORT); + auto request = session->CreateRequest(); + request->SetUri("post/"); + request->SetMethod(http_sdk::Method::Post); + PostEventHandler *handler = new PostEventHandler(); + session->SendRequest(*handler); + ASSERT_TRUE(waitForRequests(1, 1)); + session->FinishSession(); + ASSERT_TRUE(handler->is_called_); + delete handler; } - - - diff --git a/ext/include/opentelemetry/ext/http/server/http_server.h b/ext/include/opentelemetry/ext/http/server/http_server.h index c9aa8273c4..51d6b1fe6d 100644 --- a/ext/include/opentelemetry/ext/http/server/http_server.h +++ b/ext/include/opentelemetry/ext/http/server/http_server.h @@ -24,8 +24,8 @@ # ifdef LOG_TRACE # undef LOG_TRACE # define LOG_TRACE(x, ...) printf(x "\n", __VA_ARGS__) -# undef LOG_INFO -# define LOG_INFO LOG_TRACE +# undef LOG_INFO +# define LOG_INFO LOG_TRACE # endif #endif diff --git a/sdk/include/opentelemetry/sdk/common/http_client.h b/sdk/include/opentelemetry/sdk/common/http_client.h index 11615d026a..1b2666e979 100644 --- a/sdk/include/opentelemetry/sdk/common/http_client.h +++ b/sdk/include/opentelemetry/sdk/common/http_client.h @@ -60,23 +60,22 @@ enum class Method enum class SessionState { - CreateFailed, // session create failed - Created, // session created - Destroy, // session destroyed - Connecting, // connecting to peer - ConnectFailed, // connection failed - Connected, // connected - Sending, // sending request - SendFailed, // request send failed - Response, // response received - SSLHandshakeFailed, // SSL Handshake failed - TimedOut , // request timedout - NetworkError, // network error - ReadError, // error reading response - WriteError, // error writing request - Cancelled // (manually) cancelled -}; - + CreateFailed, // session create failed + Created, // session created + Destroy, // session destroyed + Connecting, // connecting to peer + ConnectFailed, // connection failed + Connected, // connected + Sending, // sending request + SendFailed, // request send failed + Response, // response received + SSLHandshakeFailed, // SSL Handshake failed + TimedOut, // request timedout + NetworkError, // network error + ReadError, // error reading response + WriteError, // error writing request + Cancelled // (manually) cancelled +}; using Byte = uint8_t; using StatusCode = uint16_t; From 896259b9429159b244a6f073ef1b68bdce42e448 Mon Sep 17 00:00:00 2001 From: Lalit Kumar Bhasin Date: Tue, 3 Nov 2020 09:40:44 +0530 Subject: [PATCH 05/32] remove file --- .../common/http/http_client_httplib.h | 313 ------------------ 1 file changed, 313 deletions(-) delete mode 100644 exporters/include/opentelemetry/exporters/common/http/http_client_httplib.h diff --git a/exporters/include/opentelemetry/exporters/common/http/http_client_httplib.h b/exporters/include/opentelemetry/exporters/common/http/http_client_httplib.h deleted file mode 100644 index aff15b35a6..0000000000 --- a/exporters/include/opentelemetry/exporters/common/http/http_client_httplib.h +++ /dev/null @@ -1,313 +0,0 @@ -#pragma once - -#include "httplib.h" -#include "opentelemetry/sdk/common/http_client.h" - -#include -#include -#include - -OPENTELEMETRY_BEGIN_NAMESPACE -namespace exporters -{ -namespace common -{ -namespace http -{ - -namespace http_sdk = opentelemetry::sdk::common::http; -using HttpHeaders = std::multimap; - -const http_sdk::HttpStatusCode HTTP_OK = 200; - -class HttpRequest : public http_sdk::HttpRequest -{ - -public: - HttpRequest() : method_(http_sdk::HttpMethod::HTTP_GET), uri_("/") {} - - void setMethod(http_sdk::HttpMethod method) noexcept override { method_ = method; } - - void SetBody(http_sdk::HttpBody &body) noexcept override { body_ = std::move(body); } - - void AddHeader(const http_sdk::HttpHeader &header) noexcept override - { - headers_.insert({header.first, header.second}); - } - - void ReplaceHeader(const http_sdk::HttpHeader &header) noexcept override - { - // erase matching headers - auto range = headers_.equal_range(header.first); - headers_.erase(range.first, range.second); - AddHeader(header); - } - - virtual void setURI(const std::string &uri) noexcept override { uri_ = uri; } - - void SetTimeoutMs(std::chrono::milliseconds timeout_ms) noexcept override - { - _timeout_ms = timeout_ms; - } - -public: - http_sdk::HttpMethod method_; - http_sdk::HttpBody body_; - HttpHeaders headers_; - std::string uri_; - std::chrono::milliseconds _timeout_ms; -}; - -class HttpResponse : public http_sdk::HttpResponse -{ - -public: - HttpResponse() : status_code_(HTTP_OK) {} - - virtual const http_sdk::HttpBody &GetBody() const noexcept override { return body_; } - - virtual bool GetNextHeader(nostd::function_ref callable) const - noexcept override - { - for (const auto &header : headers_) - { - if (!callable(std::make_pair(header.first, header.second))) - { - return false; - } - } - return true; - } - - virtual bool GetNextHeader(const std::string &key, - nostd::function_ref callable) const - noexcept override - { - auto range = headers_.equal_range(key); - for (auto it = range.first; it != range.second; ++it) - { - if (!callable(std::make_pair(it->first, it->second))) - { - return false; - } - } - return true; - } - - virtual http_sdk::HttpStatusCode GetStatusCode() const noexcept override { return status_code_; } - - virtual const std::string &GetErrorMessage() const noexcept override { return error_message_; } - - virtual bool IsSuccess() const noexcept override { return (error_message_.length() == 0); } - -public: - HttpHeaders headers_; - http_sdk::HttpBody body_; - http_sdk::HttpStatusCode status_code_; - std::string error_message_; -}; - -class HttpSessionManager; - -class HttpSession : public http_sdk::HttpSession -{ - -public: - HttpSession(HttpSessionManager &session_manager, std::string host, uint16_t port = 80) - : session_manager_(session_manager), session_state_(http_sdk::HttpSessionState::CREATED) - { - if (host.rfind("http://", 0) != 0 && host.rfind("https://", 0) != 0) - { - host = "http://" + host; // TBD - https support - } - host = host + ":" + std::to_string(port); - httplib_client_.reset(new httplib::Client(host.c_str())); - } - - std::shared_ptr CreateRequest() noexcept override - { - http_request_.reset(new HttpRequest()); - return http_request_; - } - - virtual void SendRequest(http_sdk::HttpResponseHandler &callback) noexcept override - { - HttpResponse res; - if (http_sdk::HttpMethod::HTTP_GET == http_request_->method_) - { - auto result_future = std::async(std::launch::async, [this, &callback, &res] { - return this->SendGetRequestAsync(callback, res); - }); - } - else - { - auto result_future = std::async(std::launch::async, [this, &callback, &res] { - return this->SendPostRequestAsync(callback, res); - }); - } - // &HttpSession::SendRequestAsync, this, callback); - } - - virtual http_sdk::HttpSessionState GetSessionState() const noexcept override - { - return session_state_; - } - - virtual bool CancelSession() noexcept override - { - // TBD - Not Implemented - return true; - } - - virtual bool FinishSession() noexcept override - { - // TBD - Not Implemented - return true; - } - - void SetId(uint64_t session_id) { session_id_ = session_id; } - -private: - void SendGetRequestAsync(http_sdk::HttpResponseHandler &callback, HttpResponse &res) - { - session_state_ = http_sdk::HttpSessionState::ONGOING; - httplib::Result httplib_res = - httplib_client_->Get(http_request_->uri_.c_str(), http_request_->headers_); - httplib::Error err = httplib_res.error(); - if (err == httplib::Error::Success) - { - res.status_code_ = httplib_res->status; - res.headers_ = std::move(httplib_res->headers); - std::move(httplib_res->body.begin(), httplib_res->body.end(), std::back_inserter(res.body_)); - session_state_ = http_sdk::HttpSessionState::FINISHED; - callback.OnHttpResponse(res); - } - else - { - res.error_message_ = get_error_message(err); - session_state_ = http_sdk::HttpSessionState::FINISHED; - callback.OnHttpResponse(res); - } - } - - void SendPostRequestAsync(http_sdk::HttpResponseHandler &callback, HttpResponse &res) - { - session_state_ = http_sdk::HttpSessionState::ONGOING; - std::string body(http_request_->body_.begin(), http_request_->body_.end()); - // find content type from headers; - auto search = http_request_->headers_.find("Content-Type"); - std::string content_type; - if (search != http_request_->headers_.end()) - { - content_type = search->second; - } - else - { - content_type = "application/json"; // defaults - } - httplib::Result httplib_res = - httplib_client_->Post(http_request_->uri_.c_str(), std::move(body), content_type.c_str()); - httplib::Error err = httplib_res.error(); - if (err == httplib::Error::Success) - { - - res.status_code_ = httplib_res->status; - res.headers_ = std::move(httplib_res->headers); - std::move(httplib_res->body.begin(), httplib_res->body.end(), std::back_inserter(res.body_)); - session_state_ = http_sdk::HttpSessionState::FINISHED; - callback.OnHttpResponse(res); - } - else - { - res.error_message_ = get_error_message(err); - session_state_ = http_sdk::HttpSessionState::FINISHED; - callback.OnHttpResponse(res); - } - } - - std::string get_error_message(httplib::Error err) - { - switch (err) - { - case httplib::Error::Unknown: - return "Unknown error"; - case httplib::Error::Connection: - return "Connection error"; - case httplib::Error::BindIPAddress: - return "Bind IP address error"; - case httplib::Error::Read: - return "Read error"; - case httplib::Error::Write: - return "Write error"; - case httplib::ExceedRedirectCount: - return "Redirect Count Exceeded error"; - case httplib::Canceled: - return "Cancelled Error"; - case httplib::SSLConnection: - return "SSL Connection error"; - case httplib::SSLLoadingCerts: - return "SSL Certificate loading error"; - case httplib::SSLServerVerification: - return "SSL Server Verification error"; - case httplib::UnsupportedMultipartBoundaryChars: - return "Unsupported Multiple Boundary Characters"; - default: - return "Unknown error"; - } - } - -private: - std::shared_ptr http_request_; - http_sdk::HttpSessionState session_state_; - uint64_t session_id_; - HttpSessionManager &session_manager_; - std::unique_ptr httplib_client_; -}; - -class HttpSessionManager : public http_sdk::HttpSessionManager -{ - -public: - std::shared_ptr createHttpSession(std::string host, - uint16_t port = 80) noexcept override - { - auto session = std::make_shared(*this, host, port); - auto session_id = ++next_session_id_; - session->SetId(session_id); - sessions_.insert({session_id, session}); - return session; - } - - bool CancelAllSessions() noexcept override - { - for (auto &session : sessions_) - { - session.second->CancelSession(); - } - return true; - } - - bool FinishAllSessions() noexcept override - { - for (auto &session : sessions_) - { - session.second->FinishSession(); - } - return true; - } - - void CleanupSession(uint64_t session_id) - { - // TBD = Need to be thread safe - sessions_.erase(session_id); - } - -private: - std::atomic next_session_id_; - std::map> sessions_; -}; - -} // namespace http -} // namespace common -} // namespace exporters -OPENTELEMETRY_END_NAMESPACE From 8c593e6e6c7bff4e00de66c02c4871f93022d042 Mon Sep 17 00:00:00 2001 From: Lalit Kumar Bhasin Date: Tue, 3 Nov 2020 09:45:01 +0530 Subject: [PATCH 06/32] revert debug changes --- sdk/include/opentelemetry/sdk/common/main.cc | 3 --- 1 file changed, 3 deletions(-) delete mode 100644 sdk/include/opentelemetry/sdk/common/main.cc diff --git a/sdk/include/opentelemetry/sdk/common/main.cc b/sdk/include/opentelemetry/sdk/common/main.cc deleted file mode 100644 index 54e40891eb..0000000000 --- a/sdk/include/opentelemetry/sdk/common/main.cc +++ /dev/null @@ -1,3 +0,0 @@ -#include "http_client.h" - -int main() {} From bf80c0be0592b6567fabc88600c4a66b16a386d2 Mon Sep 17 00:00:00 2001 From: Lalit Kumar Bhasin Date: Tue, 3 Nov 2020 09:46:43 +0530 Subject: [PATCH 07/32] remove http_server debug --- .../opentelemetry/ext/http/server/http_server.h | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/ext/include/opentelemetry/ext/http/server/http_server.h b/ext/include/opentelemetry/ext/http/server/http_server.h index 51d6b1fe6d..9389219b9a 100644 --- a/ext/include/opentelemetry/ext/http/server/http_server.h +++ b/ext/include/opentelemetry/ext/http/server/http_server.h @@ -24,8 +24,6 @@ # ifdef LOG_TRACE # undef LOG_TRACE # define LOG_TRACE(x, ...) printf(x "\n", __VA_ARGS__) -# undef LOG_INFO -# define LOG_INFO LOG_TRACE # endif #endif @@ -251,7 +249,7 @@ class HttpServer : private SocketTools::Reactor::SocketCallback protected: virtual void onSocketAcceptable(SocketTools::Socket socket) override { - LOG_TRACE("HttpServer: accepting socket fd=0x%d", socket.m_sock); + LOG_TRACE("HttpServer: accepting socket fd=0x%llx", socket.m_sock); assert(std::find(m_listeningSockets.begin(), m_listeningSockets.end(), socket) != m_listeningSockets.end()); @@ -271,7 +269,7 @@ class HttpServer : private SocketTools::Reactor::SocketCallback virtual void onSocketReadable(SocketTools::Socket socket) override { - LOG_TRACE("HttpServer: reading socket fd=0x%d", socket.m_sock); + LOG_TRACE("HttpServer: reading socket fd=0x%llx", socket.m_sock); // No thread-safety here! assert(std::find(m_listeningSockets.begin(), m_listeningSockets.end(), socket) == m_listeningSockets.end()); @@ -299,7 +297,7 @@ class HttpServer : private SocketTools::Reactor::SocketCallback virtual void onSocketWritable(SocketTools::Socket socket) override { - LOG_TRACE("HttpServer: writing socket fd=0x%d", socket.m_sock); + LOG_TRACE("HttpServer: writing socket fd=0x%llx", socket.m_sock); // No thread-safety here! assert(std::find(m_listeningSockets.begin(), m_listeningSockets.end(), socket) == @@ -321,7 +319,7 @@ class HttpServer : private SocketTools::Reactor::SocketCallback virtual void onSocketClosed(SocketTools::Socket socket) override { - LOG_TRACE("HttpServer: closing socket fd=0x%d", socket.m_sock); + LOG_TRACE("HttpServer: closing socket fd=0x%llx", socket.m_sock); assert(std::find(m_listeningSockets.begin(), m_listeningSockets.end(), socket) == m_listeningSockets.end()); From 2f8a40589dc912bb721433d822ca7888e55a2ebe Mon Sep 17 00:00:00 2001 From: Lalit Kumar Bhasin Date: Tue, 3 Nov 2020 10:08:42 +0530 Subject: [PATCH 08/32] fix std::smatch type conversion --- .../opentelemetry/exporters/common/http/http_operation_curl.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/exporters/include/opentelemetry/exporters/common/http/http_operation_curl.h b/exporters/include/opentelemetry/exporters/common/http/http_operation_curl.h index b76e9cbb8b..5c292f0c06 100644 --- a/exporters/include/opentelemetry/exporters/common/http/http_operation_curl.h +++ b/exporters/include/opentelemetry/exporters/common/http/http_operation_curl.h @@ -332,7 +332,8 @@ class HttpOperation std::smatch match; std::regex http_headers_regex(http_header_regexp); if (std::regex_search(header, match, http_headers_regex)) - result.insert(std::pair(match[1], match[2])); + result.insert(std::pair( + static_cast(match[1]), static_cast(match[2]))); // result[match.str(1)] = match[2]; // Key: value } return result; From 6a591136abdf03e1be123c47c048cbd423333b32 Mon Sep 17 00:00:00 2001 From: Lalit Kumar Bhasin Date: Tue, 3 Nov 2020 22:37:49 +0530 Subject: [PATCH 09/32] fix std::regex oom crash on docker container --- .../exporters/common/http/http_operation_curl.h | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/exporters/include/opentelemetry/exporters/common/http/http_operation_curl.h b/exporters/include/opentelemetry/exporters/common/http/http_operation_curl.h index 5c292f0c06..f9198f9702 100644 --- a/exporters/include/opentelemetry/exporters/common/http/http_operation_curl.h +++ b/exporters/include/opentelemetry/exporters/common/http/http_operation_curl.h @@ -329,12 +329,19 @@ class HttpOperation std::string header; while (std::getline(ss, header, '\n')) { - std::smatch match; + // TBD - Regex below crashes for out-of-memory on CI docker container, so + // switching to string comparison + /*std::smatch match; std::regex http_headers_regex(http_header_regexp); if (std::regex_search(header, match, http_headers_regex)) result.insert(std::pair( static_cast(match[1]), static_cast(match[2]))); - // result[match.str(1)] = match[2]; // Key: value + */ + size_t pos = header.find(": "); + if (pos != std::string::npos) + result.insert(std::pair( + static_cast(header.substr(0, pos)), + static_cast(header.substr(pos + 2)))); } return result; } From ec90c97f27f94c5f5adb7438406ae05c8723316a Mon Sep 17 00:00:00 2001 From: Lalit Kumar Bhasin Date: Wed, 4 Nov 2020 00:22:07 +0530 Subject: [PATCH 10/32] fix string_view conversion --- .../opentelemetry/exporters/common/http/http_client_curl.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/exporters/include/opentelemetry/exporters/common/http/http_client_curl.h b/exporters/include/opentelemetry/exporters/common/http/http_client_curl.h index a8117c5cf3..d2ba398dde 100644 --- a/exporters/include/opentelemetry/exporters/common/http/http_client_curl.h +++ b/exporters/include/opentelemetry/exporters/common/http/http_client_curl.h @@ -114,9 +114,9 @@ class Session : public http_sdk::Session { if (host.rfind("http://", 0) != 0 && host.rfind("https://", 0) != 0) { - host_ = "http://" + host; // TBD - https support + host = "http://" + host; // TODO - https support } - host_ = host + ":" + std::to_string(port); + host_ = static_cast(host + ":" + std::to_string(port)); } std::shared_ptr CreateRequest() noexcept override From 78bcdee7c1714474dbb04c8032a6619abd09cf4f Mon Sep 17 00:00:00 2001 From: Lalit Kumar Bhasin Date: Wed, 4 Nov 2020 01:40:01 +0530 Subject: [PATCH 11/32] fix string_view conversion --- .../exporters/common/http/http_client_curl.h | 13 ++++++------- 1 file changed, 6 insertions(+), 7 deletions(-) diff --git a/exporters/include/opentelemetry/exporters/common/http/http_client_curl.h b/exporters/include/opentelemetry/exporters/common/http/http_client_curl.h index d2ba398dde..c2d7efb8b8 100644 --- a/exporters/include/opentelemetry/exporters/common/http/http_client_curl.h +++ b/exporters/include/opentelemetry/exporters/common/http/http_client_curl.h @@ -114,9 +114,9 @@ class Session : public http_sdk::Session { if (host.rfind("http://", 0) != 0 && host.rfind("https://", 0) != 0) { - host = "http://" + host; // TODO - https support + host_ = "http://" + host; // TODO - https support } - host_ = static_cast(host + ":" + std::to_string(port)); + host_ += ":" + std::to_string(port) + "/"; } std::shared_ptr CreateRequest() noexcept override @@ -128,9 +128,8 @@ class Session : public http_sdk::Session virtual void SendRequest(http_sdk::EventHandler &callback) noexcept override { is_session_active_ = true; - std::string url = - static_cast(host_) + "/" + static_cast(http_request_->uri_); - auto callback_ptr = &callback; + std::string url = host_ + std::string(http_request_->uri_); + auto callback_ptr = &callback; curl_operation_.reset(new HttpOperation(http_request_->method_, url, callback_ptr, http_request_->headers_, http_request_->body_, false, http_request_->timeout_ms_.count())); @@ -171,7 +170,7 @@ class Session : public http_sdk::Session private: std::shared_ptr http_request_; - nostd::string_view host_; + std::string host_; std::unique_ptr curl_operation_; uint64_t session_id_; SessionManager &session_manager_; @@ -187,7 +186,7 @@ class SessionManager : public http_sdk::SessionManager std::shared_ptr CreateSession(nostd::string_view host, uint16_t port = 80) noexcept override { - auto session = std::make_shared(*this, static_cast(host), port); + auto session = std::make_shared(*this, std::string(host), port); auto session_id = ++next_session_id_; session->SetId(session_id); sessions_.insert({session_id, session}); From d91132f5c1db702cfa4d93a6d938670ecbe701cf Mon Sep 17 00:00:00 2001 From: Lalit Kumar Bhasin Date: Wed, 4 Nov 2020 02:05:56 +0530 Subject: [PATCH 12/32] fix format --- .../exporters/common/http/http_client_curl.h | 4 ---- .../exporters/common/http/http_operation_curl.h | 16 +++++++--------- 2 files changed, 7 insertions(+), 13 deletions(-) diff --git a/exporters/include/opentelemetry/exporters/common/http/http_client_curl.h b/exporters/include/opentelemetry/exporters/common/http/http_client_curl.h index c2d7efb8b8..89be76278e 100644 --- a/exporters/include/opentelemetry/exporters/common/http/http_client_curl.h +++ b/exporters/include/opentelemetry/exporters/common/http/http_client_curl.h @@ -21,7 +21,6 @@ const http_sdk::StatusCode Http_Ok = 200; class Request : public http_sdk::Request { - public: Request() : method_(http_sdk::Method::Get), uri_("/") {} @@ -59,7 +58,6 @@ class Request : public http_sdk::Request class Response : public http_sdk::Response { - public: Response() : status_code_(Http_Ok) {} @@ -107,7 +105,6 @@ class SessionManager; class Session : public http_sdk::Session { - public: Session(SessionManager &session_manager, std::string host, uint16_t port = 80) : session_manager_(session_manager), is_session_active_(false) @@ -179,7 +176,6 @@ class Session : public http_sdk::Session class SessionManager : public http_sdk::SessionManager { - public: SessionManager() { curl_global_init(CURL_GLOBAL_ALL); } diff --git a/exporters/include/opentelemetry/exporters/common/http/http_operation_curl.h b/exporters/include/opentelemetry/exporters/common/http/http_operation_curl.h index f9198f9702..f9f79590cf 100644 --- a/exporters/include/opentelemetry/exporters/common/http/http_operation_curl.h +++ b/exporters/include/opentelemetry/exporters/common/http/http_operation_curl.h @@ -71,9 +71,7 @@ class HttpOperation // Default connectivity and response size options bool raw_response = false, size_t http_conn_timeout = default_http_conn_timeout) - : - - // + : // method_(method), url_(url), callback_(callback), @@ -115,9 +113,9 @@ class HttpOperation // Specify our custom headers for (auto &kv : this->request_headers_) { - std::string header = static_cast(kv.first); + std::string header = std::string(kv.first); header += ": "; - header += static_cast(kv.second); + header += std::string(kv.second); headers_chunk_ = curl_slist_append(headers_chunk_, header.c_str()); } @@ -329,8 +327,9 @@ class HttpOperation std::string header; while (std::getline(ss, header, '\n')) { - // TBD - Regex below crashes for out-of-memory on CI docker container, so - // switching to string comparison + // TODO - Regex below crashes with out-of-memory on CI docker container, so + // switching to string comparison. Need to debug and revert back. + /*std::smatch match; std::regex http_headers_regex(http_header_regexp); if (std::regex_search(header, match, http_headers_regex)) @@ -340,8 +339,7 @@ class HttpOperation size_t pos = header.find(": "); if (pos != std::string::npos) result.insert(std::pair( - static_cast(header.substr(0, pos)), - static_cast(header.substr(pos + 2)))); + nostd::string_view(header.substr(0, pos)), nostd::string_view(header.substr(pos + 2)))); } return result; } From 10746ee9ed567840e37408a106f1fba1d0066a29 Mon Sep 17 00:00:00 2001 From: Lalit Kumar Bhasin Date: Tue, 10 Nov 2020 10:10:09 +0530 Subject: [PATCH 13/32] Update sdk/include/opentelemetry/sdk/common/http_client.h Co-authored-by: Reiley Yang --- sdk/include/opentelemetry/sdk/common/http_client.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk/include/opentelemetry/sdk/common/http_client.h b/sdk/include/opentelemetry/sdk/common/http_client.h index 1b2666e979..df45d06ef2 100644 --- a/sdk/include/opentelemetry/sdk/common/http_client.h +++ b/sdk/include/opentelemetry/sdk/common/http_client.h @@ -70,7 +70,7 @@ enum class SessionState SendFailed, // request send failed Response, // response received SSLHandshakeFailed, // SSL Handshake failed - TimedOut, // request timedout + TimedOut, // request timed out NetworkError, // network error ReadError, // error reading response WriteError, // error writing request From 92f67ffdfcf8c5a1022564b6db8473904d3b22d1 Mon Sep 17 00:00:00 2001 From: Lalit Kumar Bhasin Date: Tue, 10 Nov 2020 10:10:23 +0530 Subject: [PATCH 14/32] Update sdk/include/opentelemetry/sdk/common/http_client.h Co-authored-by: Reiley Yang --- sdk/include/opentelemetry/sdk/common/http_client.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sdk/include/opentelemetry/sdk/common/http_client.h b/sdk/include/opentelemetry/sdk/common/http_client.h index df45d06ef2..69bd028544 100644 --- a/sdk/include/opentelemetry/sdk/common/http_client.h +++ b/sdk/include/opentelemetry/sdk/common/http_client.h @@ -69,7 +69,7 @@ enum class SessionState Sending, // sending request SendFailed, // request send failed Response, // response received - SSLHandshakeFailed, // SSL Handshake failed + SSLHandshakeFailed, // SSL handshake failed TimedOut, // request timed out NetworkError, // network error ReadError, // error reading response From bc4f3c1f9193b5b12f4405e64fc95245f3761139 Mon Sep 17 00:00:00 2001 From: Lalit Kumar Bhasin Date: Tue, 10 Nov 2020 10:40:50 +0530 Subject: [PATCH 15/32] review comments --- .../exporters/common/http/http_operation_curl.h | 6 ++++-- exporters/test/CMakeLists.txt | 11 ++++++----- sdk/include/opentelemetry/sdk/common/http_client.h | 4 ++-- 3 files changed, 12 insertions(+), 9 deletions(-) diff --git a/exporters/include/opentelemetry/exporters/common/http/http_operation_curl.h b/exporters/include/opentelemetry/exporters/common/http/http_operation_curl.h index f9f79590cf..5c47fa62e4 100644 --- a/exporters/include/opentelemetry/exporters/common/http/http_operation_curl.h +++ b/exporters/include/opentelemetry/exporters/common/http/http_operation_curl.h @@ -58,9 +58,11 @@ class HttpOperation * Create local CURL instance for url and body * * @param url - * @param body + * @param callback + * @param request Request Headers + * @param body Reques Body + * @param raw_response whether to parse the response * @param httpConnTimeout HTTP connection timeout in seconds - * @param httpReadTimeout HTTP read timeout in seconds */ HttpOperation(http_sdk::Method method, std::string url, diff --git a/exporters/test/CMakeLists.txt b/exporters/test/CMakeLists.txt index 285b0b2dc6..b5b036041d 100644 --- a/exporters/test/CMakeLists.txt +++ b/exporters/test/CMakeLists.txt @@ -1,8 +1,9 @@ if(WITH_CURL) - add_executable("curl_http_test" "curl_http_test.cc") + set(FILENAME curl_http_test) + add_executable(${FILENAME} ${FILENAME}.cc) include_directories(${CURL_INCLUDE_DIR}) - target_link_libraries("curl_http_test" ${GTEST_BOTH_LIBRARIES} + target_link_libraries(${FILENAME} ${GTEST_BOTH_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT} ${CURL_LIBRARIES}) - gtest_add_tests(TARGET "curl_http_test" TEST_PREFIX curl. TEST_LIST - "curl_http_test") -endif() + gtest_add_tests(TARGET ${FILENAME} TEST_PREFIX curl. TEST_LIST + ${FILENAME}) +endif() \ No newline at end of file diff --git a/sdk/include/opentelemetry/sdk/common/http_client.h b/sdk/include/opentelemetry/sdk/common/http_client.h index 69bd028544..c067b0da43 100644 --- a/sdk/include/opentelemetry/sdk/common/http_client.h +++ b/sdk/include/opentelemetry/sdk/common/http_client.h @@ -62,7 +62,7 @@ enum class SessionState { CreateFailed, // session create failed Created, // session created - Destroy, // session destroyed + Destroyed, // session destroyed Connecting, // connecting to peer ConnectFailed, // connection failed Connected, // connected @@ -70,7 +70,7 @@ enum class SessionState SendFailed, // request send failed Response, // response received SSLHandshakeFailed, // SSL handshake failed - TimedOut, // request timed out + TimedOut, // request time out NetworkError, // network error ReadError, // error reading response WriteError, // error writing request From bee8c75311396999f0ac663fa95bc30fdaab6277 Mon Sep 17 00:00:00 2001 From: Lalit Kumar Bhasin Date: Tue, 10 Nov 2020 10:47:05 +0530 Subject: [PATCH 16/32] format --- .../exporters/common/http/http_operation_curl.h | 2 +- exporters/test/CMakeLists.txt | 5 ++--- 2 files changed, 3 insertions(+), 4 deletions(-) diff --git a/exporters/include/opentelemetry/exporters/common/http/http_operation_curl.h b/exporters/include/opentelemetry/exporters/common/http/http_operation_curl.h index 5c47fa62e4..2cda1a2817 100644 --- a/exporters/include/opentelemetry/exporters/common/http/http_operation_curl.h +++ b/exporters/include/opentelemetry/exporters/common/http/http_operation_curl.h @@ -58,7 +58,7 @@ class HttpOperation * Create local CURL instance for url and body * * @param url - * @param callback + * @param callback * @param request Request Headers * @param body Reques Body * @param raw_response whether to parse the response diff --git a/exporters/test/CMakeLists.txt b/exporters/test/CMakeLists.txt index b5b036041d..9b32c37a13 100644 --- a/exporters/test/CMakeLists.txt +++ b/exporters/test/CMakeLists.txt @@ -4,6 +4,5 @@ if(WITH_CURL) include_directories(${CURL_INCLUDE_DIR}) target_link_libraries(${FILENAME} ${GTEST_BOTH_LIBRARIES} ${CMAKE_THREAD_LIBS_INIT} ${CURL_LIBRARIES}) - gtest_add_tests(TARGET ${FILENAME} TEST_PREFIX curl. TEST_LIST - ${FILENAME}) -endif() \ No newline at end of file + gtest_add_tests(TARGET ${FILENAME} TEST_PREFIX curl. TEST_LIST ${FILENAME}) +endif() From 2ca38b6e6e3a48b62f7e1d4eea1f4047c6a9391c Mon Sep 17 00:00:00 2001 From: Lalit Kumar Bhasin Date: Thu, 12 Nov 2020 16:39:48 +0530 Subject: [PATCH 17/32] review comments --- CMakeLists.txt | 4 - ci/do_ci.sh | 3 - ext/CMakeLists.txt | 1 + .../ext/http/client/curl}/http_client_curl.h | 68 ++++----- .../http/client/curl}/http_operation_curl.h | 132 +++++++----------- .../ext/http/client}/http_client.h | 10 +- ext/test/CMakeLists.txt | 1 + .../test => ext/test/http}/curl_http_test.cc | 20 +-- 8 files changed, 103 insertions(+), 136 deletions(-) rename {exporters/include/opentelemetry/exporters/common/http => ext/include/opentelemetry/ext/http/client/curl}/http_client_curl.h (70%) rename {exporters/include/opentelemetry/exporters/common/http => ext/include/opentelemetry/ext/http/client/curl}/http_operation_curl.h (77%) rename {sdk/include/opentelemetry/sdk/common => ext/include/opentelemetry/ext/http/client}/http_client.h (98%) rename {exporters/test => ext/test/http}/curl_http_test.cc (86%) diff --git a/CMakeLists.txt b/CMakeLists.txt index 578d531c05..265351a083 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -16,8 +16,6 @@ option(WITH_OTPROTOCOL option(WITH_PROMETHEUS "Whether to include the Prometheus Client in the SDK" OFF) -option(WITH_CURL "Whether to include HTTP CURL Client in the SDK" OFF) - option(WITH_TESTS "Whether to enable tests" ON) option(WITH_EXAMPLES "Whether to build examples" ON) @@ -75,7 +73,5 @@ include_directories(.) if(WITH_EXAMPLES) add_subdirectory(examples) endif() -include_directories(ext/include) add_subdirectory(ext) -include_directories(exporters/include) add_subdirectory(exporters) diff --git a/ci/do_ci.sh b/ci/do_ci.sh index e73c32986f..4790daabf5 100755 --- a/ci/do_ci.sh +++ b/ci/do_ci.sh @@ -16,7 +16,6 @@ if [[ "$1" == "cmake.test" ]]; then rm -rf * cmake -DCMAKE_BUILD_TYPE=Debug \ -DCMAKE_CXX_FLAGS="-Werror" \ - -DWITH_CURL=ON \ "${SRC_DIR}" make make test @@ -27,7 +26,6 @@ elif [[ "$1" == "cmake.c++20.test" ]]; then cmake -DCMAKE_BUILD_TYPE=Debug \ -DCMAKE_CXX_FLAGS="-Werror" \ -DCMAKE_CXX_STANDARD=20 \ - -DWITH_CURL=ON \ "${SRC_DIR}" make make test @@ -38,7 +36,6 @@ elif [[ "$1" == "cmake.legacy.test" ]]; then cmake -DCMAKE_BUILD_TYPE=Debug \ -DCMAKE_CXX_FLAGS="-Werror" \ -DCMAKE_CXX_STANDARD=11 \ - -DWITH_CURL=ON \ "${SRC_DIR}" make make test diff --git a/ext/CMakeLists.txt b/ext/CMakeLists.txt index 75205ac71e..fa8f1e9498 100644 --- a/ext/CMakeLists.txt +++ b/ext/CMakeLists.txt @@ -1,4 +1,5 @@ add_subdirectory(src) +include_directories(include) if(BUILD_TESTING) add_subdirectory(test) diff --git a/exporters/include/opentelemetry/exporters/common/http/http_client_curl.h b/ext/include/opentelemetry/ext/http/client/curl/http_client_curl.h similarity index 70% rename from exporters/include/opentelemetry/exporters/common/http/http_client_curl.h rename to ext/include/opentelemetry/ext/http/client/curl/http_client_curl.h index 89be76278e..54ed8d2634 100644 --- a/exporters/include/opentelemetry/exporters/common/http/http_client_curl.h +++ b/ext/include/opentelemetry/ext/http/client/curl/http_client_curl.h @@ -7,41 +7,44 @@ #include OPENTELEMETRY_BEGIN_NAMESPACE -namespace exporters -{ -namespace common +namespace ext { namespace http { +namespace client +{ namespace curl { -namespace http_sdk = opentelemetry::sdk::common::http; -const http_sdk::StatusCode Http_Ok = 200; +namespace http_client = opentelemetry::ext::http::client; +const http_client::StatusCode Http_Ok = 200; -class Request : public http_sdk::Request +class Request : public http_client::Request { public: - Request() : method_(http_sdk::Method::Get), uri_("/") {} + Request() : method_(http_client::Method::Get), uri_("/") {} - void SetMethod(http_sdk::Method method) noexcept override { method_ = method; } + void SetMethod(http_client::Method method) noexcept override { method_ = method; } - void SetBody(http_sdk::Body &body) noexcept override { body_ = std::move(body); } + void SetBody(http_client::Body &body) noexcept override { body_ = std::move(body); } void AddHeader(nostd::string_view name, nostd::string_view value) noexcept override { - headers_.insert(std::pair(name, value)); + headers_.insert(std::pair(name, value)); } void ReplaceHeader(nostd::string_view name, nostd::string_view value) noexcept override { // erase matching headers - auto range = headers_.equal_range(name); + auto range = headers_.equal_range(static_cast(name)); headers_.erase(range.first, range.second); AddHeader(name, value); } - virtual void SetUri(nostd::string_view uri) noexcept override { uri_ = uri; } + virtual void SetUri(nostd::string_view uri) noexcept override + { + uri_ = static_cast(uri); + } void SetTimeoutMs(std::chrono::milliseconds timeout_ms) noexcept override { @@ -49,19 +52,19 @@ class Request : public http_sdk::Request } public: - http_sdk::Method method_; - http_sdk::Body body_; + http_client::Method method_; + http_client::Body body_; Headers headers_; - nostd::string_view uri_; + std::string uri_; std::chrono::milliseconds timeout_ms_{5000}; // ms }; -class Response : public http_sdk::Response +class Response : public http_client::Response { public: Response() : status_code_(Http_Ok) {} - virtual const http_sdk::Body &GetBody() const noexcept override { return body_; } + virtual const http_client::Body &GetBody() const noexcept override { return body_; } virtual bool ForEachHeader( nostd::function_ref callable) const @@ -82,7 +85,7 @@ class Response : public http_sdk::Response nostd::function_ref callable) const noexcept override { - auto range = headers_.equal_range(name); + auto range = headers_.equal_range(static_cast(name)); for (auto it = range.first; it != range.second; ++it) { if (!callable(it->first, it->second)) @@ -93,17 +96,17 @@ class Response : public http_sdk::Response return true; } - virtual http_sdk::StatusCode GetStatusCode() const noexcept override { return status_code_; } + virtual http_client::StatusCode GetStatusCode() const noexcept override { return status_code_; } public: Headers headers_; - http_sdk::Body body_; - http_sdk::StatusCode status_code_; + http_client::Body body_; + http_client::StatusCode status_code_; }; class SessionManager; -class Session : public http_sdk::Session +class Session : public http_client::Session { public: Session(SessionManager &session_manager, std::string host, uint16_t port = 80) @@ -116,25 +119,25 @@ class Session : public http_sdk::Session host_ += ":" + std::to_string(port) + "/"; } - std::shared_ptr CreateRequest() noexcept override + std::shared_ptr CreateRequest() noexcept override { http_request_.reset(new Request()); return http_request_; } - virtual void SendRequest(http_sdk::EventHandler &callback) noexcept override + virtual void SendRequest(http_client::EventHandler &callback) noexcept override { is_session_active_ = true; std::string url = host_ + std::string(http_request_->uri_); auto callback_ptr = &callback; curl_operation_.reset(new HttpOperation(http_request_->method_, url, callback_ptr, http_request_->headers_, http_request_->body_, false, - http_request_->timeout_ms_.count())); + http_request_->timeout_ms_)); curl_operation_->SendAsync([this, callback_ptr](HttpOperation &operation) { if (operation.WasAborted()) { // Manually cancelled - callback_ptr->OnEvent(http_sdk::SessionState::Cancelled, ""); + callback_ptr->OnEvent(http_client::SessionState::Cancelled, ""); } if (operation.GetResponseCode() >= CURL_LAST) @@ -145,8 +148,8 @@ class Session : public http_sdk::Session response->body_ = operation.GetResponseBody(); callback_ptr->OnResponse(*response); } + is_session_active_ = false; }); - is_session_active_ = false; } virtual bool CancelSession() noexcept override @@ -174,13 +177,14 @@ class Session : public http_sdk::Session bool is_session_active_; }; -class SessionManager : public http_sdk::SessionManager +class SessionManager : public http_client::SessionManager { public: + // The call (curl_global_init) is not thread safe. Ensure this is called only once. SessionManager() { curl_global_init(CURL_GLOBAL_ALL); } - std::shared_ptr CreateSession(nostd::string_view host, - uint16_t port = 80) noexcept override + std::shared_ptr CreateSession(nostd::string_view host, + uint16_t port = 80) noexcept override { auto session = std::make_shared(*this, std::string(host), port); auto session_id = ++next_session_id_; @@ -221,7 +225,7 @@ class SessionManager : public http_sdk::SessionManager }; } // namespace curl +} // namespace client } // namespace http -} // namespace common -} // namespace exporters +} // namespace ext OPENTELEMETRY_END_NAMESPACE diff --git a/exporters/include/opentelemetry/exporters/common/http/http_operation_curl.h b/ext/include/opentelemetry/ext/http/client/curl/http_operation_curl.h similarity index 77% rename from exporters/include/opentelemetry/exporters/common/http/http_operation_curl.h rename to ext/include/opentelemetry/ext/http/client/curl/http_operation_curl.h index 2cda1a2817..fffd6a48f1 100644 --- a/exporters/include/opentelemetry/exporters/common/http/http_operation_curl.h +++ b/ext/include/opentelemetry/ext/http/client/curl/http_operation_curl.h @@ -1,6 +1,6 @@ #pragma once -#include "opentelemetry/sdk/common/http_client.h" +#include "opentelemetry/ext/http/client/http_client.h" #include #include @@ -16,34 +16,34 @@ #endif OPENTELEMETRY_BEGIN_NAMESPACE -namespace exporters -{ -namespace common +namespace ext { namespace http { +namespace client +{ namespace curl { -namespace http_sdk = opentelemetry::sdk::common::http; -const size_t default_http_conn_timeout = 5000; // ms -const std::string http_status_regexp = "HTTP\\/\\d\\.\\d (\\d+)\\ .*"; -const std::string http_header_regexp = "(.*)\\: (.*)\\n*"; +namespace http_client = opentelemetry::ext::http::client; +const std::chrono::milliseconds default_http_conn_timeout(5000); // ms +const std::string http_status_regexp = "HTTP\\/\\d\\.\\d (\\d+)\\ .*"; +const std::string http_header_regexp = "(.*)\\: (.*)\\n*"; struct curl_ci { - bool operator()(const nostd::string_view &s1, const nostd::string_view &s2) const + bool operator()(const std::string &s1, const std::string &s2) const { return std::lexicographical_compare( s1.begin(), s1.end(), s2.begin(), s2.end(), [](char c1, char c2) { return ::tolower(c1) < ::tolower(c2); }); } }; -using Headers = std::multimap; +using Headers = std::multimap; class HttpOperation { public: - void DispatchEvent(http_sdk::SessionState type, std::string reason = "") + void DispatchEvent(http_client::SessionState type, std::string reason = "") { if (callback_ != nullptr) { @@ -64,15 +64,15 @@ class HttpOperation * @param raw_response whether to parse the response * @param httpConnTimeout HTTP connection timeout in seconds */ - HttpOperation(http_sdk::Method method, + HttpOperation(http_client::Method method, std::string url, - http_sdk::EventHandler *callback, + http_client::EventHandler *callback, // Default empty headers and empty request body - const Headers &request_headers = Headers(), - const http_sdk::Body &request_body = http_sdk::Body(), + const Headers &request_headers = Headers(), + const http_client::Body &request_body = http_client::Body(), // Default connectivity and response size options - bool raw_response = false, - size_t http_conn_timeout = default_http_conn_timeout) + bool is_raw_response = false, + std::chrono::milliseconds http_conn_timeout = default_http_conn_timeout) : // method_(method), url_(url), @@ -82,7 +82,7 @@ class HttpOperation request_headers_(request_headers), request_body_(request_body), // Optional connection params - raw_response_(raw_response), + is_raw_response_(is_raw_response), http_conn_timeout_(http_conn_timeout), // Result res_(CURLE_OK), @@ -91,15 +91,12 @@ class HttpOperation is_finished_(false), nread_(0) { - response_.memory = nullptr; - response_.size = 0; - /* get a curl handle */ curl_ = curl_easy_init(); if (!curl_) { res_ = CURLE_FAILED_INIT; - DispatchEvent(http_sdk::SessionState::CreateFailed); + DispatchEvent(http_client::SessionState::CreateFailed); return; } @@ -126,7 +123,7 @@ class HttpOperation curl_easy_setopt(curl_, CURLOPT_HTTPHEADER, headers_chunk_); } - DispatchEvent(http_sdk::SessionState::Created); + DispatchEvent(http_client::SessionState::Created); } /** @@ -141,7 +138,7 @@ class HttpOperation result_.wait(); } // TBD - Need to be uncomment. This will callback instance is deleted. - // DispatchEvent(http_sdk::SessionState::Destroy); + // DispatchEvent(http_client::SessionState::Destroy); res_ = CURLE_OK; curl_easy_cleanup(curl_); curl_slist_free_all(headers_chunk_); @@ -172,7 +169,7 @@ class HttpOperation if (!curl_) { res_ = CURLE_FAILED_INIT; - DispatchEvent(http_sdk::SessionState::SendFailed); + DispatchEvent(http_client::SessionState::SendFailed); return res_; } @@ -182,12 +179,12 @@ class HttpOperation // Perform initial connect, handling the timeout if needed curl_easy_setopt(curl_, CURLOPT_CONNECT_ONLY, 1L); - DispatchEvent(http_sdk::SessionState::Connecting); + DispatchEvent(http_client::SessionState::Connecting); res_ = curl_easy_perform(curl_); if (CURLE_OK != res_) { - DispatchEvent(http_sdk::SessionState::ConnectFailed, + DispatchEvent(http_client::SessionState::ConnectFailed, curl_easy_strerror(res_)); // couldn't connect - stage 1 return res_; } @@ -201,32 +198,32 @@ class HttpOperation if (CURLE_OK != res_) { - DispatchEvent(http_sdk::SessionState::ConnectFailed, + DispatchEvent(http_client::SessionState::ConnectFailed, curl_easy_strerror(res_)); // couldn't connect - stage 2 return res_; } /* wait for the socket to become ready for sending */ sockfd_ = sockextr; - if (!WaitOnSocket(sockfd_, 0, http_conn_timeout_) || is_aborted_) + if (!WaitOnSocket(sockfd_, 0, http_conn_timeout_.count()) || is_aborted_) { res_ = CURLE_OPERATION_TIMEDOUT; DispatchEvent( - http_sdk::SessionState::ConnectFailed, + http_client::SessionState::ConnectFailed, " Is aborted: " + std::to_string(is_aborted_.load())); // couldn't connect - stage 3 return res_; } - DispatchEvent(http_sdk::SessionState::Connected); + DispatchEvent(http_client::SessionState::Connected); // once connection is there - switch back to easy perform for HTTP post curl_easy_setopt(curl_, CURLOPT_CONNECT_ONLY, 0); // send all data to our callback function - if (raw_response_) + if (is_raw_response_) { curl_easy_setopt(curl_, CURLOPT_HEADER, true); curl_easy_setopt(curl_, CURLOPT_WRITEFUNCTION, (void *)&WriteMemoryCallback); - curl_easy_setopt(curl_, CURLOPT_WRITEDATA, (void *)&response_); + curl_easy_setopt(curl_, CURLOPT_WRITEDATA, (void *)&raw_response_); } else { @@ -236,14 +233,14 @@ class HttpOperation } // TODO: only two methods supported for now - POST and GET - if (method_ == http_sdk::Method::Post) + if (method_ == http_client::Method::Post) { // POST curl_easy_setopt(curl_, CURLOPT_POST, true); curl_easy_setopt(curl_, CURLOPT_POSTFIELDS, (const char *)request); curl_easy_setopt(curl_, CURLOPT_POSTFIELDSIZE, req_size); } - else if (method_ == http_sdk::Method::Get) + else if (method_ == http_client::Method::Get) { // GET } @@ -256,12 +253,12 @@ class HttpOperation // abort if slower than 4kb/sec during 30 seconds curl_easy_setopt(curl_, CURLOPT_LOW_SPEED_TIME, 30L); curl_easy_setopt(curl_, CURLOPT_LOW_SPEED_LIMIT, 4096); - DispatchEvent(http_sdk::SessionState::Sending); + DispatchEvent(http_client::SessionState::Sending); res_ = curl_easy_perform(curl_); if (CURLE_OK != res_) { - DispatchEvent(http_sdk::SessionState::SendFailed, curl_easy_strerror(res_)); + DispatchEvent(http_client::SessionState::SendFailed, curl_easy_strerror(res_)); return res_; } @@ -279,7 +276,7 @@ class HttpOperation /* libcurl is nice enough to parse the http response code itself: */ curl_easy_getinfo(curl_, CURLINFO_RESPONSE_CODE, &res_); // We got some response from server. Dump the contents. - DispatchEvent(http_sdk::SessionState::Response); + DispatchEvent(http_client::SessionState::Response); // This function returns: // - on success: HTTP status code. @@ -358,28 +355,16 @@ class HttpOperation * * @return */ - std::vector GetRawResponse() - { - std::vector result; - if ((response_.memory != nullptr) && (response_.size != 0)) - result.insert(result.end(), (const char *)response_.memory, - ((const char *)response_.memory) + response_.size); - return result; - } + std::vector GetRawResponse() { return raw_response_; } /** * Release memory allocated for response */ void ReleaseResponse() { - if (response_.memory != nullptr) - { - free(response_.memory); - response_.memory = nullptr; - response_.size = 0; - } resp_headers_.clear(); resp_body_.clear(); + raw_response_.clear(); } /** @@ -402,24 +387,25 @@ class HttpOperation CURL *GetHandle() { return curl_; } protected: - const bool raw_response_; // Do not split response headers from response body - const size_t http_conn_timeout_; // Timeout for connect. Default: 5000ms + const bool is_raw_response_; // Do not split response headers from response body + const std::chrono::milliseconds http_conn_timeout_; // Timeout for connect. Default: 5000ms CURL *curl_; // Local curl instance CURLcode res_; // Curl result OR HTTP status code if successful - http_sdk::EventHandler *callback_ = nullptr; + http_client::EventHandler *callback_ = nullptr; // Request values - http_sdk::Method method_; + http_client::Method method_; std::string url_; const Headers &request_headers_; - const http_sdk::Body &request_body_; + const http_client::Body &request_body_; struct curl_slist *headers_chunk_ = nullptr; // Processed response headers and body std::vector resp_headers_; std::vector resp_body_; + std::vector raw_response_; // Socket parameters curl_socket_t sockfd_; @@ -469,13 +455,6 @@ class HttpOperation return res; } - // Raw response buffer - struct MemoryStruct - { - char *memory; - size_t size; - } response_; - /** * Old-school memory allocator * @@ -487,25 +466,14 @@ class HttpOperation */ static size_t WriteMemoryCallback(void *contents, size_t size, size_t nmemb, void *userp) { - size_t realsize = size * nmemb; - struct MemoryStruct *mem = (struct MemoryStruct *)userp; - - mem->memory = (char *)(realloc(mem->memory, mem->size + realsize + 1)); - if (mem->memory == NULL) - { - /* out of memory! */ - return 0; - } - - memcpy(&(mem->memory[mem->size]), contents, realsize); - mem->size += realsize; - mem->memory[mem->size] = 0; - - return realsize; + std::vector *buf = static_cast *>(userp); + buf->insert(buf->end(), static_cast(contents), + static_cast(contents) + (size * nmemb)); + return size * nmemb; } /** - * C++ STL std::string allocator + * C++ STL std::vector allocator * * @param ptr * @param size @@ -528,7 +496,7 @@ class HttpOperation } }; } // namespace curl +} // namespace client } // namespace http -} // namespace common -} // namespace exporters +} // namespace ext OPENTELEMETRY_END_NAMESPACE diff --git a/sdk/include/opentelemetry/sdk/common/http_client.h b/ext/include/opentelemetry/ext/http/client/http_client.h similarity index 98% rename from sdk/include/opentelemetry/sdk/common/http_client.h rename to ext/include/opentelemetry/ext/http/client/http_client.h index c067b0da43..a6f1c3e924 100644 --- a/sdk/include/opentelemetry/sdk/common/http_client.h +++ b/ext/include/opentelemetry/ext/http/client/http_client.h @@ -40,12 +40,12 @@ */ OPENTELEMETRY_BEGIN_NAMESPACE -namespace sdk -{ -namespace common +namespace ext { namespace http { +namespace client +{ enum class Method { @@ -160,7 +160,7 @@ class SessionManager virtual ~SessionManager() = default; }; +} // namespace client } // namespace http -} // namespace common -} // namespace sdk +} // namespace ext OPENTELEMETRY_END_NAMESPACE diff --git a/ext/test/CMakeLists.txt b/ext/test/CMakeLists.txt index 189a03f69c..d12e56796b 100644 --- a/ext/test/CMakeLists.txt +++ b/ext/test/CMakeLists.txt @@ -1 +1,2 @@ add_subdirectory(zpages) +add_subdirectory(http) diff --git a/exporters/test/curl_http_test.cc b/ext/test/http/curl_http_test.cc similarity index 86% rename from exporters/test/curl_http_test.cc rename to ext/test/http/curl_http_test.cc index 06d38ea9a6..26cfa726f5 100644 --- a/exporters/test/curl_http_test.cc +++ b/ext/test/http/curl_http_test.cc @@ -1,4 +1,4 @@ -#include "opentelemetry/exporters/common/http/http_client_curl.h" +#include "opentelemetry/ext//http/client/curl//http_client_curl.h" #include "opentelemetry/ext/http/server/http_server.h" #include @@ -13,24 +13,24 @@ #include -namespace curl = opentelemetry::exporters::common::http::curl; -namespace http_sdk = opentelemetry::sdk::common::http; +namespace curl = opentelemetry::ext::http::client::curl; +namespace http_client = opentelemetry::ext::http::client; -class CustomEventHandler : public http_sdk::EventHandler +class CustomEventHandler : public http_client::EventHandler { public: - virtual void OnResponse(http_sdk::Response &response) noexcept override{}; - virtual void OnEvent(http_sdk::SessionState state, + virtual void OnResponse(http_client::Response &response) noexcept override{}; + virtual void OnEvent(http_client::SessionState state, opentelemetry::nostd::string_view reason) noexcept override {} - virtual void OnConnecting(const http_sdk::SSLCertificate &) noexcept {} + virtual void OnConnecting(const http_client::SSLCertificate &) noexcept {} virtual ~CustomEventHandler() = default; bool is_called_ = false; }; class GetEventHandler : public CustomEventHandler { - void OnResponse(http_sdk::Response &response) noexcept override + void OnResponse(http_client::Response &response) noexcept override { ASSERT_EQ(200, response.GetStatusCode()); ASSERT_EQ(response.GetBody().size(), 0); @@ -40,7 +40,7 @@ class GetEventHandler : public CustomEventHandler class PostEventHandler : public CustomEventHandler { - void OnResponse(http_sdk::Response &response) noexcept override + void OnResponse(http_client::Response &response) noexcept override { ASSERT_EQ(200, response.GetStatusCode()); std::string body(response.GetBody().begin(), response.GetBody().end()); @@ -150,7 +150,7 @@ TEST_F(BasicCurlHttpTests, SendPostRequest) auto session = session_manager.CreateSession("127.0.0.1", HTTP_PORT); auto request = session->CreateRequest(); request->SetUri("post/"); - request->SetMethod(http_sdk::Method::Post); + request->SetMethod(http_client::Method::Post); PostEventHandler *handler = new PostEventHandler(); session->SendRequest(*handler); ASSERT_TRUE(waitForRequests(1, 1)); From 1389f9ac33f58372924a8ca917e86f2f04f07c2a Mon Sep 17 00:00:00 2001 From: Lalit Kumar Bhasin Date: Thu, 12 Nov 2020 16:44:40 +0530 Subject: [PATCH 18/32] missing cmake --- ext/test/http/CMakeLists.txt | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 ext/test/http/CMakeLists.txt diff --git a/ext/test/http/CMakeLists.txt b/ext/test/http/CMakeLists.txt new file mode 100644 index 0000000000..ab558d2ca7 --- /dev/null +++ b/ext/test/http/CMakeLists.txt @@ -0,0 +1,11 @@ +find_package(CURL) +if(CURL_FOUND) + set(CURL_LIBRARY "-lcurl") + set(FILENAME curl_http_test) + add_executable(${FILENAME} ${FILENAME}.cc) + include_directories(${CURL_INCLUDE_DIR}) + target_link_libraries(${FILENAME} ${GTEST_BOTH_LIBRARIES} + ${CMAKE_THREAD_LIBS_INIT} ${CURL_LIBRARIES}) + gtest_add_tests(TARGET ${FILENAME} TEST_PREFIX ext.http.curl. TEST_LIST + ${FILENAME}) +endif() From 48c60263342eb75204e87969f0f4f8b9210e3b63 Mon Sep 17 00:00:00 2001 From: Lalit Kumar Bhasin Date: Thu, 12 Nov 2020 16:54:02 +0530 Subject: [PATCH 19/32] convert string_view to string in map --- .../opentelemetry/ext/http/client/curl/http_operation_curl.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/ext/include/opentelemetry/ext/http/client/curl/http_operation_curl.h b/ext/include/opentelemetry/ext/http/client/curl/http_operation_curl.h index fffd6a48f1..e1383c88fd 100644 --- a/ext/include/opentelemetry/ext/http/client/curl/http_operation_curl.h +++ b/ext/include/opentelemetry/ext/http/client/curl/http_operation_curl.h @@ -337,8 +337,8 @@ class HttpOperation */ size_t pos = header.find(": "); if (pos != std::string::npos) - result.insert(std::pair( - nostd::string_view(header.substr(0, pos)), nostd::string_view(header.substr(pos + 2)))); + result.insert( + std::pair(header.substr(0, pos), header.substr(pos + 2))); } return result; } From 473e6123712bf303a850de8d146296d209ad8308 Mon Sep 17 00:00:00 2001 From: Lalit Kumar Bhasin Date: Thu, 12 Nov 2020 17:18:13 +0530 Subject: [PATCH 20/32] fix string_view conversion --- .../opentelemetry/ext/http/client/curl/http_client_curl.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ext/include/opentelemetry/ext/http/client/curl/http_client_curl.h b/ext/include/opentelemetry/ext/http/client/curl/http_client_curl.h index 54ed8d2634..0605edc9fc 100644 --- a/ext/include/opentelemetry/ext/http/client/curl/http_client_curl.h +++ b/ext/include/opentelemetry/ext/http/client/curl/http_client_curl.h @@ -30,7 +30,8 @@ class Request : public http_client::Request void AddHeader(nostd::string_view name, nostd::string_view value) noexcept override { - headers_.insert(std::pair(name, value)); + headers_.insert(std::pair(static_cast(name), + static_cast(value))); } void ReplaceHeader(nostd::string_view name, nostd::string_view value) noexcept override From 7ca1b730c659a9966ed2bed5390eefe7aedd3ff0 Mon Sep 17 00:00:00 2001 From: Lalit Kumar Bhasin Date: Thu, 12 Nov 2020 19:00:30 +0530 Subject: [PATCH 21/32] remove older changes --- CMakeLists.txt | 10 +--------- exporters/CMakeLists.txt | 4 ---- exporters/test/CMakeLists.txt | 8 -------- 3 files changed, 1 insertion(+), 21 deletions(-) delete mode 100644 exporters/test/CMakeLists.txt diff --git a/CMakeLists.txt b/CMakeLists.txt index 265351a083..e9b7cbc24f 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -28,14 +28,6 @@ if(WITH_TESTS) include(CTest) endif() -if(WITH_CURL) - set(CURL_LIBRARY "-lcurl") - find_package(CURL REQUIRED) - if(NOT CURL_FOUND) - message(FATAL_ERROR "libcurl not found! Have you installed deps?") - endif(NOT CURL_FOUND) -endif() - find_package(Threads) if(CMAKE_CXX_COMPILER_ID STREQUAL "MSVC") @@ -70,8 +62,8 @@ include_directories(sdk/include) include_directories(sdk) add_subdirectory(sdk) include_directories(.) +add_subdirectory(exporters) if(WITH_EXAMPLES) add_subdirectory(examples) endif() add_subdirectory(ext) -add_subdirectory(exporters) diff --git a/exporters/CMakeLists.txt b/exporters/CMakeLists.txt index 8166fa0ada..fdb9776473 100644 --- a/exporters/CMakeLists.txt +++ b/exporters/CMakeLists.txt @@ -8,7 +8,3 @@ add_subdirectory(memory) if(WITH_PROMETHEUS) add_subdirectory(prometheus) endif() - -if(BUILD_TESTING) - add_subdirectory(test) -endif() diff --git a/exporters/test/CMakeLists.txt b/exporters/test/CMakeLists.txt deleted file mode 100644 index 9b32c37a13..0000000000 --- a/exporters/test/CMakeLists.txt +++ /dev/null @@ -1,8 +0,0 @@ -if(WITH_CURL) - set(FILENAME curl_http_test) - add_executable(${FILENAME} ${FILENAME}.cc) - include_directories(${CURL_INCLUDE_DIR}) - target_link_libraries(${FILENAME} ${GTEST_BOTH_LIBRARIES} - ${CMAKE_THREAD_LIBS_INIT} ${CURL_LIBRARIES}) - gtest_add_tests(TARGET ${FILENAME} TEST_PREFIX curl. TEST_LIST ${FILENAME}) -endif() From fce9c5c56e43f8d70fa6061744dae0ab5bc27f5e Mon Sep 17 00:00:00 2001 From: Lalit Kumar Bhasin Date: Thu, 12 Nov 2020 19:16:48 +0530 Subject: [PATCH 22/32] remove http server --- ext/include/opentelemetry/ext/http/server/http_server.h | 1 + 1 file changed, 1 insertion(+) diff --git a/ext/include/opentelemetry/ext/http/server/http_server.h b/ext/include/opentelemetry/ext/http/server/http_server.h index 9389219b9a..87afc98571 100644 --- a/ext/include/opentelemetry/ext/http/server/http_server.h +++ b/ext/include/opentelemetry/ext/http/server/http_server.h @@ -720,6 +720,7 @@ class HttpServer : private SocketTools::Reactor::SocketCallback { conn.response.message.clear(); conn.response.headers.clear(); + conn.response.body.clear(); if (conn.response.code == 0) { From 90fefe3489882f1fd597a895c75ce922d9b090bc Mon Sep 17 00:00:00 2001 From: Lalit Kumar Bhasin Date: Thu, 12 Nov 2020 19:37:30 +0530 Subject: [PATCH 23/32] remove http server changes --- ext/include/opentelemetry/ext/http/server/http_server.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/include/opentelemetry/ext/http/server/http_server.h b/ext/include/opentelemetry/ext/http/server/http_server.h index 87afc98571..672bf95c6a 100644 --- a/ext/include/opentelemetry/ext/http/server/http_server.h +++ b/ext/include/opentelemetry/ext/http/server/http_server.h @@ -720,8 +720,8 @@ class HttpServer : private SocketTools::Reactor::SocketCallback { conn.response.message.clear(); conn.response.headers.clear(); - conn.response.body.clear(); + if (conn.response.code == 0) { conn.response.code = 404; // Not Found From 6350db0d7f017e3af448cc6bca6fca108a9dea9d Mon Sep 17 00:00:00 2001 From: Lalit Kumar Bhasin Date: Wed, 18 Nov 2020 14:27:16 +0530 Subject: [PATCH 24/32] add http request and respons tests --- ext/test/http/curl_http_test.cc | 53 +++++++++++++++++++++++++++++++++ 1 file changed, 53 insertions(+) diff --git a/ext/test/http/curl_http_test.cc b/ext/test/http/curl_http_test.cc index 26cfa726f5..a298b66f5f 100644 --- a/ext/test/http/curl_http_test.cc +++ b/ext/test/http/curl_http_test.cc @@ -126,6 +126,59 @@ class BasicCurlHttpTests : public ::testing::Test, public HTTP_SERVER_NS::HttpRe TEST_F(BasicCurlHttpTests, DoNothing) {} +TEST_F(BasicCurlHttpTests, HttpRequest) +{ + curl::Request req; + const char *b = "test-data"; + http_client::Body body = {b, b + strlen(b)}; + http_client::Body body1 = body; + req.SetBody(body); + ASSERT_EQ(req.body_, body1); + req.AddHeader("name1", "value1"); + req.AddHeader("name2", "value2"); + ASSERT_TRUE(req.headers_.find("name1")->second == "value1"); + ASSERT_TRUE(req.headers_.find("name2")->second == "value2"); + + req.ReplaceHeader("name1", "value3"); + ASSERT_EQ(req.headers_.find("name1")->second, "value3"); + + req.SetTimeoutMs(std::chrono::duration(5000)); + ASSERT_EQ(req.timeout_ms_, std::chrono::duration(5000)); +} + +TEST_F(BasicCurlHttpTests, HttpResponse) +{ + curl::Response res; + std::multimap m1 = { + {"name1", "value1_1"}, {"name1", "value1_2"}, {"name2", "value3"}, {"name3", "value3"}}; + res.headers_ = m1; + + const char *b = "test-data"; + http_client::Body body = {b, b + strlen(b)}; + int count = 0; + res.ForEachHeader("name1", [&count](opentelemetry::nostd::string_view name, + opentelemetry::nostd::string_view value) { + if (name != "name1") + return false; + if (value != "value1_1" && value != "value1_2") + return false; + count++; + return true; + }); + ASSERT_EQ(count, 2); + count = 0; + res.ForEachHeader( + [&count](opentelemetry::nostd::string_view name, opentelemetry::nostd::string_view value) { + if (name != "name1" && name != "name2" && name != "name3") + return false; + if (value != "value1_1" && value != "value1_2" && value != "value2" && value != "value3") + return false; + count++; + return true; + }); + ASSERT_EQ(count, 4); +} + TEST_F(BasicCurlHttpTests, SendGetRequest) { received_requests_.clear(); From bd3ca47c2d5a59e5a18d0d315e56944b85307ff2 Mon Sep 17 00:00:00 2001 From: Lalit Kumar Bhasin Date: Wed, 18 Nov 2020 14:51:50 +0530 Subject: [PATCH 25/32] more tests --- ext/test/http/curl_http_test.cc | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/ext/test/http/curl_http_test.cc b/ext/test/http/curl_http_test.cc index a298b66f5f..4939fabc00 100644 --- a/ext/test/http/curl_http_test.cc +++ b/ext/test/http/curl_http_test.cc @@ -209,5 +209,9 @@ TEST_F(BasicCurlHttpTests, SendPostRequest) ASSERT_TRUE(waitForRequests(1, 1)); session->FinishSession(); ASSERT_TRUE(handler->is_called_); + + session_manager.CancelAllSessions(); + session_manager.FinishAllSessions(); + delete handler; } From 7c73b04e1fe15affc5f97d899e1fb992711d3a2a Mon Sep 17 00:00:00 2001 From: Lalit Kumar Bhasin Date: Wed, 18 Nov 2020 15:14:30 +0530 Subject: [PATCH 26/32] http timeout test --- ext/test/http/curl_http_test.cc | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/ext/test/http/curl_http_test.cc b/ext/test/http/curl_http_test.cc index 4939fabc00..f0751187f6 100644 --- a/ext/test/http/curl_http_test.cc +++ b/ext/test/http/curl_http_test.cc @@ -204,6 +204,11 @@ TEST_F(BasicCurlHttpTests, SendPostRequest) auto request = session->CreateRequest(); request->SetUri("post/"); request->SetMethod(http_client::Method::Post); + + const char *b = "test-data"; + http_client::Body body = {b, b + strlen(b)}; + request->SetBody(body); + request->AddHeader("Content-Type", "text/plain"); PostEventHandler *handler = new PostEventHandler(); session->SendRequest(*handler); ASSERT_TRUE(waitForRequests(1, 1)); @@ -215,3 +220,18 @@ TEST_F(BasicCurlHttpTests, SendPostRequest) delete handler; } + +TEST_F(BasicCurlHttpTests, RequestTimeout) +{ + received_requests_.clear(); + curl::SessionManager session_manager; + + auto session = session_manager.CreateSession("127.0.0.10", HTTP_PORT); // Non Existing address + auto request = session->CreateRequest(); + request->SetUri("get/"); + GetEventHandler *handler = new GetEventHandler(); + session->SendRequest(*handler); + session->FinishSession(); + ASSERT_TRUE(handler->is_called_); + delete handler; +} From c1cd5ef2ad28fb5b3edcaf4e77343652049f480f Mon Sep 17 00:00:00 2001 From: Lalit Kumar Bhasin Date: Wed, 18 Nov 2020 17:10:46 +0530 Subject: [PATCH 27/32] absolute path for http_server header --- ext/test/http/curl_http_test.cc | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/test/http/curl_http_test.cc b/ext/test/http/curl_http_test.cc index f0751187f6..05e87d738b 100644 --- a/ext/test/http/curl_http_test.cc +++ b/ext/test/http/curl_http_test.cc @@ -1,5 +1,5 @@ +#include #include "opentelemetry/ext//http/client/curl//http_client_curl.h" -#include "opentelemetry/ext/http/server/http_server.h" #include #include From b1c8d4253e49e1bd499d90eeff6cb7a7f0ed6f91 Mon Sep 17 00:00:00 2001 From: Lalit Kumar Bhasin Date: Wed, 18 Nov 2020 17:34:57 +0530 Subject: [PATCH 28/32] absolute path http_server --- ext/include/opentelemetry/ext/zpages/zpages_http_server.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/ext/include/opentelemetry/ext/zpages/zpages_http_server.h b/ext/include/opentelemetry/ext/zpages/zpages_http_server.h index fb43ed0642..d44e7489b0 100644 --- a/ext/include/opentelemetry/ext/zpages/zpages_http_server.h +++ b/ext/include/opentelemetry/ext/zpages/zpages_http_server.h @@ -7,7 +7,7 @@ #include #include -#include "opentelemetry/ext/http/server/http_server.h" +#include OPENTELEMETRY_BEGIN_NAMESPACE namespace ext From a8e3919f708799832cafaa0e62cd581ee66d0143 Mon Sep 17 00:00:00 2001 From: Lalit Kumar Bhasin Date: Wed, 18 Nov 2020 21:47:41 +0530 Subject: [PATCH 29/32] ignore http_server from coverage ; revert absolute path --- ci/do_ci.sh | 2 ++ ext/include/opentelemetry/ext/zpages/zpages_http_server.h | 2 +- ext/test/http/curl_http_test.cc | 2 +- 3 files changed, 4 insertions(+), 2 deletions(-) diff --git a/ci/do_ci.sh b/ci/do_ci.sh index 41aaa6b54f..327bc78a82 100755 --- a/ci/do_ci.sh +++ b/ci/do_ci.sh @@ -169,6 +169,8 @@ elif [[ "$1" == "code.coverage" ]]; then make make test lcov --directory $PWD --capture --output-file coverage.info + lcov --remove coverage.info '*/ext/http/server/http_server.h'> tmp_coverage.info 2>/dev/null + cp tmp_coverage.info coverage.info exit 0 fi diff --git a/ext/include/opentelemetry/ext/zpages/zpages_http_server.h b/ext/include/opentelemetry/ext/zpages/zpages_http_server.h index d44e7489b0..fb43ed0642 100644 --- a/ext/include/opentelemetry/ext/zpages/zpages_http_server.h +++ b/ext/include/opentelemetry/ext/zpages/zpages_http_server.h @@ -7,7 +7,7 @@ #include #include -#include +#include "opentelemetry/ext/http/server/http_server.h" OPENTELEMETRY_BEGIN_NAMESPACE namespace ext diff --git a/ext/test/http/curl_http_test.cc b/ext/test/http/curl_http_test.cc index 05e87d738b..f0751187f6 100644 --- a/ext/test/http/curl_http_test.cc +++ b/ext/test/http/curl_http_test.cc @@ -1,5 +1,5 @@ -#include #include "opentelemetry/ext//http/client/curl//http_client_curl.h" +#include "opentelemetry/ext/http/server/http_server.h" #include #include From d69633c4fe81f3a8738f35efc5684c8ef6808d24 Mon Sep 17 00:00:00 2001 From: Lalit Kumar Bhasin Date: Thu, 19 Nov 2020 01:06:36 +0530 Subject: [PATCH 30/32] increase coverage --- ext/test/http/curl_http_test.cc | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/ext/test/http/curl_http_test.cc b/ext/test/http/curl_http_test.cc index f0751187f6..079600442e 100644 --- a/ext/test/http/curl_http_test.cc +++ b/ext/test/http/curl_http_test.cc @@ -235,3 +235,17 @@ TEST_F(BasicCurlHttpTests, RequestTimeout) ASSERT_TRUE(handler->is_called_); delete handler; } + +TEST_F(BasicCurlHttpTests, CurlHttpOperations) +{ + GetEventHandler *handler = new GetEventHandler(); + + const char *b = "test-data"; + http_client::Body body = {b, b + strlen(b)}; + + std::multimap m1 = { + {"name1", "value1_1"}, {"name1", "value1_2"}, {"name2", "value3"}, {"name3", "value3"}}; + curl::Headers headers = m1; + curl::HttpOperation http_operations(http_client::Method::Head, "/get", handler, headers, body, + false); +} From e45a969fd3da47e2ef962eb9bff7164449fa7759 Mon Sep 17 00:00:00 2001 From: Lalit Kumar Bhasin Date: Thu, 19 Nov 2020 01:27:28 +0530 Subject: [PATCH 31/32] more tests --- ext/test/http/curl_http_test.cc | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) diff --git a/ext/test/http/curl_http_test.cc b/ext/test/http/curl_http_test.cc index 079600442e..1c7245ed47 100644 --- a/ext/test/http/curl_http_test.cc +++ b/ext/test/http/curl_http_test.cc @@ -246,6 +246,15 @@ TEST_F(BasicCurlHttpTests, CurlHttpOperations) std::multimap m1 = { {"name1", "value1_1"}, {"name1", "value1_2"}, {"name2", "value3"}, {"name3", "value3"}}; curl::Headers headers = m1; - curl::HttpOperation http_operations(http_client::Method::Head, "/get", handler, headers, body, - false); + curl::HttpOperation http_operations1(http_client::Method::Head, "/get", handler, headers, body, + true); + http_operations1.Send(); + + curl::HttpOperation http_operations2(http_client::Method::Get, "/get", handler, headers, body, + true); + http_operations2.Send(); + + curl::HttpOperation http_operations3(http_client::Method::Get, "/get", handler, headers, body, + false); + http_operations3.Send(); } From 2568485d329827ece0dc14e28c5df3365cbfea0a Mon Sep 17 00:00:00 2001 From: Lalit Kumar Bhasin Date: Thu, 19 Nov 2020 01:37:33 +0530 Subject: [PATCH 32/32] remove all files for http_server --- ci/do_ci.sh | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/ci/do_ci.sh b/ci/do_ci.sh index 327bc78a82..589d826afa 100755 --- a/ci/do_ci.sh +++ b/ci/do_ci.sh @@ -169,7 +169,8 @@ elif [[ "$1" == "code.coverage" ]]; then make make test lcov --directory $PWD --capture --output-file coverage.info - lcov --remove coverage.info '*/ext/http/server/http_server.h'> tmp_coverage.info 2>/dev/null + # removing test http server coverage from the total coverage. We don't use this server completely. + lcov --remove coverage.info '*/ext/http/server/*'> tmp_coverage.info 2>/dev/null cp tmp_coverage.info coverage.info exit 0 fi