LCOV - code coverage report
Current view: top level - root/contrail/src/contrail-common/http/client - http_client.cc (source / functions) Hit Total Coverage
Test: OpenSDN C/C++ coverage (all TARGET_SET jobs) Lines: 35 311 11.3 %
Date: 2026-11-09 02:10:49 Functions: 7 51 13.7 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /*
       2             :  * Copyright (c) 2013 Juniper Networks, Inc. All rights reserved.
       3             :  * Copyright Contributors to the OpenSDN project
       4             :  */
       5             : 
       6             : #include "http_client.h"
       7             : #include <boost/bind/bind.hpp>
       8             : #include <boost/algorithm/string.hpp>
       9             : #include "base/address_util.h"
      10             : #include "base/task_annotations.h"
      11             : #include "io/event_manager.h"
      12             : #include "base/logging.h"
      13             : #include "http_curl.h"
      14             : 
      15             : using namespace std;
      16             : using namespace boost::placeholders;
      17             : 
      18           0 : HttpClientSession::HttpClientSession(HttpClient *client, Socket *socket) 
      19           0 :     : TcpSession(client, socket) , delete_called_(0) {
      20           0 :         set_observer(boost::bind(&HttpClientSession::OnEvent, this, _1, _2));
      21           0 : }
      22             : 
      23           0 : void HttpClientSession::OnRead(Buffer buffer) {
      24           0 :     return;
      25             : }
      26             : 
      27           0 : void HttpClientSession::OnEvent(TcpSession *session, Event event) {
      28           0 :     if (connection_) {
      29           0 :         connection_->client()->
      30           0 :             ProcessEvent(boost::bind(&HttpClientSession::OnEventInternal,
      31           0 :                          this, TcpSessionPtr(session), event));
      32             :     }
      33           0 : }
      34             : 
      35           0 : void HttpClientSession::OnEventInternal(TcpSessionPtr session, Event event) {
      36           0 :     if (event_cb_ && !event_cb_.empty()) {
      37           0 :         event_cb_(static_cast<HttpClientSession *>(session.get()), event);
      38             :     }
      39             : 
      40           0 :     if (event == CLOSE) {
      41           0 :         goto error;
      42             :     }
      43           0 :     if (event == ACCEPT) {
      44           0 :         goto error;
      45             :     }
      46           0 :     if (event == CONNECT_COMPLETE) {
      47           0 :         goto error;
      48             :     }
      49           0 :     if (event == CONNECT_FAILED) {
      50           0 :         goto error;
      51             :     }
      52           0 :     if (event == EVENT_NONE) {
      53           0 :         goto error;
      54             :     }
      55           0 :     return;
      56             : 
      57           0 : error:
      58             :     // Call callback function with error;
      59           0 :     return;
      60             : }
      61             : 
      62           0 : void HttpClientSession::RegisterEventCb(SessionEventCb cb) {
      63           0 :     event_cb_ = cb;
      64           0 : }
      65             : 
      66             : namespace {
      67           0 :     boost::asio::ip::address String2Addr(const std::string& host) {
      68           0 :         boost::system::error_code ec;
      69           0 :         return AddressFromString(host, &ec);
      70             :     }
      71             : }
      72             : 
      73           0 : HttpConnection::HttpConnection(boost::asio::ip::tcp::endpoint ep,
      74           0 :                                size_t id, HttpClient *client) :
      75           0 :     host_(ep.address().to_string()), endpoint_(ep),
      76           0 :     id_(id), cb_(NULL), offset_(0), curl_handle_(NULL),
      77           0 :     session_(NULL), client_(client), use_ssl_(false), client_cert_(""),
      78           0 :     client_cert_type_("PEM"), client_key_(""), ca_cert_(""), state_(STATUS) {
      79           0 : }
      80             : 
      81           0 : HttpConnection::HttpConnection(const std::string& host, int port,
      82           0 :                                size_t id, HttpClient *client) :
      83           0 :     host_(host), endpoint_(String2Addr(host), port),
      84           0 :     id_(id), cb_(NULL), offset_(0), curl_handle_(NULL),
      85           0 :     session_(NULL), client_(client), use_ssl_(false), client_cert_(""),
      86           0 :     client_cert_type_("PEM"), client_key_(""), ca_cert_(""), state_(STATUS) {
      87           0 : }
      88             : 
      89           0 : HttpConnection::~HttpConnection() {
      90           0 :     delete_session();
      91           0 : }
      92             : 
      93           0 : std::string HttpConnection::make_url(std::string &path) {
      94           0 :     std::ostringstream ret;
      95             : 
      96           0 :     if (use_ssl_) {
      97           0 :         ret << "https://" << host_;
      98             :     } else {
      99           0 :         ret << "http://" << host_;
     100             :     }
     101           0 :     if (endpoint_.port() != 0) {
     102           0 :         ret << ":" << endpoint_.port();
     103             :     }
     104           0 :     ret << "/" << path;
     105             : 
     106           0 :     return ret.str();
     107           0 : }
     108             : 
     109           0 : HttpClientSession *HttpConnection::CreateSession() {
     110             :     HttpClientSession *session = 
     111           0 :         static_cast<HttpClientSession *>(client_->CreateSession());
     112           0 :     if (session) {
     113           0 :         session->SetConnection(this);
     114             :     }
     115           0 :     return session;
     116             : }
     117             : 
     118           0 : void HttpConnection::delete_session() {
     119           0 :     HttpClientSession *session = session_;
     120           0 :     if (session_) {
     121             :         {
     122           0 :             std::scoped_lock lock(session_->mutex());
     123           0 :             session_->SetConnection(NULL);
     124           0 :             session_ = NULL;
     125           0 :         }
     126           0 :         client_->DeleteSession(session);
     127             :     }
     128           0 : }
     129             : 
     130           0 : void HttpConnection::set_session(HttpClientSession *session) {
     131           0 :     session_ = session;
     132           0 :     if (session && event_cb_ && !event_cb_.empty())
     133           0 :         session->RegisterEventCb(event_cb_);
     134           0 : }
     135             : 
     136           0 : int HttpConnection::HttpGet(const std::string &path, HttpCb cb) {
     137           0 :     std::vector<std::string> hdr_options;
     138           0 :     return HttpGet(path, false, true, false, hdr_options, cb);
     139           0 : }
     140             : 
     141           0 : int HttpConnection::HttpGet(const std::string &path, bool header,
     142             :                             bool short_timeout, bool reuse,
     143             :                             std::vector<std::string> &hdr_options,
     144             :                             HttpCb cb) {
     145           0 :     const std::string body;
     146             : 
     147           0 :     client()->ProcessEvent(boost::bind(&HttpConnection::HttpProcessInternal,
     148           0 :                            this, body, path, bool2bf(header, short_timeout,
     149             :                                reuse), hdr_options, cb, HTTP_GET));
     150           0 :     return 0;
     151           0 : }
     152             : 
     153           0 : int HttpConnection::HttpHead(const std::string &path, bool header, bool short_timeout,
     154             :                              bool reuse, std::vector<std::string> &hdr_options,
     155             :                              HttpCb cb) {
     156           0 :     const std::string body;
     157           0 :     client()->ProcessEvent(boost::bind(&HttpConnection::HttpProcessInternal,
     158           0 :                            this, body, path, bool2bf(header, short_timeout,
     159             :                                reuse), hdr_options, cb, HTTP_HEAD));
     160           0 :     return 0;
     161           0 : }
     162             : 
     163           0 : int HttpConnection::HttpPut(const std::string &put_string,
     164             :                             const std::string &path,  HttpCb cb) {
     165           0 :     std::vector<std::string> hdr_options;
     166           0 :     return HttpPut(put_string, path, false, true, false, hdr_options, cb);
     167           0 : }
     168             : 
     169           0 : int HttpConnection::HttpPut(const std::string &put_string,
     170             :                             const std::string &path, bool header,
     171             :                             bool short_timeout,
     172             :                             bool reuse, std::vector<std::string> &hdr_options,
     173             :                             HttpCb cb) {
     174           0 :     client()->ProcessEvent(boost::bind(&HttpConnection::HttpProcessInternal,
     175             :                                        this, put_string, path,
     176           0 :                                        bool2bf(header, short_timeout, reuse),
     177             :                                        hdr_options, cb, HTTP_PUT));
     178           0 :     return 0;
     179             : }
     180             : 
     181           0 : int HttpConnection::HttpPost(const std::string &post_string,
     182             :                              const std::string &path,  HttpCb cb) {
     183           0 :     std::vector<std::string> hdr_options;
     184           0 :     return HttpPost(post_string, path, false, true, false, hdr_options, cb);
     185           0 : }
     186             : 
     187           0 : int HttpConnection::HttpPost(const std::string &post_string,
     188             :                              const std::string &path, bool header,
     189             :                              bool short_timeout, bool reuse,
     190             :                              std::vector<std::string> &hdr_options, HttpCb cb) {
     191           0 :     client()->ProcessEvent(boost::bind(&HttpConnection::HttpProcessInternal,
     192           0 :                            this, post_string, path, bool2bf(header,
     193             :                            short_timeout, reuse), hdr_options, cb, HTTP_POST));
     194           0 :     return 0;
     195             : }
     196             : 
     197           0 : int HttpConnection::HttpDelete(const std::string &path, HttpCb cb) {
     198           0 :     std::vector<std::string> hdr_options;
     199           0 :     return HttpDelete(path, false, true, false, hdr_options, cb);
     200           0 : }
     201             : 
     202           0 : int HttpConnection::HttpDelete(const std::string &path, bool header, bool short_timeout,
     203             :                                bool reuse, std::vector<std::string> &hdr_options,
     204             :                                HttpCb cb) {
     205           0 :     const std::string body;
     206           0 :     client()->ProcessEvent(boost::bind(&HttpConnection::HttpProcessInternal,
     207           0 :                            this, body, path, bool2bf(header, short_timeout,
     208             :                                reuse), hdr_options, cb, HTTP_DELETE));
     209           0 :     return 0;
     210           0 : }
     211             : 
     212           0 : void HttpConnection::ClearCallback() {
     213           0 :    cb_ = NULL; 
     214           0 : }
     215             : 
     216           0 : void HttpConnection::HttpProcessInternal(const std::string body,
     217             :                                          std::string path,
     218             :                                          unsigned short hdr_shortTimeout_reuse,
     219             :                                          std::vector<std::string> hdr_options,
     220             :                                          HttpCb cb, http_method method) {
     221             :     bool short_timeout, reuse;
     222           0 :     bf2bool(hdr_shortTimeout_reuse, sent_hdr_, short_timeout, reuse);
     223           0 :     state_ = STATUS;
     224           0 :     status_ = 0;
     225           0 :     version_.clear();
     226           0 :     reason_.clear();
     227           0 :     if (client()->AddConnection(this) == false) {
     228             :         // connection already exists
     229           0 :         if (!reuse)
     230           0 :             return;
     231             :     }
     232             : 
     233           0 :     struct _GlobalInfo *gi = client()->GlobalInfo();
     234           0 :     struct _ConnInfo *curl_handle = new_conn(this, gi, sent_hdr_, short_timeout,
     235             :                                              reuse);
     236           0 :     if (!curl_handle) {
     237           0 :         LOG(DEBUG, "Http : unable to create new connection");
     238           0 :         return;
     239             :     }
     240             : 
     241           0 :     if (curl_handle_) {
     242             :         // delete existing curl_handle
     243           0 :         del_curl_handle(curl_handle_, gi);
     244             :     }
     245           0 :     curl_handle->connection = this;
     246           0 :     set_curl_handle(curl_handle);
     247             : 
     248           0 :     cb_ = cb;
     249             : 
     250           0 :     std::string url = make_url(path);
     251           0 :     set_url(curl_handle_, url.c_str());
     252             : 
     253             :     // set special curl options requested in http connection
     254           0 :     std::map<CURLoption, int> *curl_options = this->curl_options();
     255           0 :     std::map<CURLoption, int>::iterator iter = curl_options->begin();
     256           0 :     while (iter != curl_options->end()) {
     257           0 :         set_curl_option(curl_handle_->easy, iter->first, iter->second);
     258           0 :         iter++;
     259             :     }
     260             : 
     261             :     // set SSL curl options
     262           0 :     if (use_ssl_) {
     263           0 :         set_ssl_options(curl_handle_, client_cert_.c_str(),
     264             :             client_cert_type_.c_str(), client_key_.c_str(), ca_cert_.c_str());
     265             :     }
     266             : 
     267             :     // Add header options to the get request
     268           0 :     for (uint32_t i = 0; i < hdr_options.size(); ++i)
     269           0 :         set_header_options(curl_handle_, hdr_options[i].c_str());
     270             : 
     271           0 :     switch (method) {
     272           0 :         case HTTP_GET:
     273           0 :             http_get(curl_handle_, gi);
     274           0 :             break;
     275             : 
     276           0 :         case HTTP_HEAD:
     277           0 :             http_head(curl_handle_, gi);
     278           0 :             break;
     279             : 
     280           0 :         case HTTP_POST:
     281           0 :             if (!hdr_options.size()) {
     282             :                 // if no header options are set, set the content type
     283           0 :                 set_header_options(curl_handle_, "Content-Type: application/xml");
     284             :             }
     285           0 :             set_post_string(curl_handle_, body.c_str(), body.size());
     286           0 :             http_post(curl_handle_, gi);
     287           0 :             break;
     288             : 
     289           0 :         case HTTP_PUT:
     290           0 :             if (!hdr_options.size()) {
     291             :                 // if no header options are set, set the content type
     292           0 :                 set_header_options(curl_handle_, "Content-Type: application/xml");
     293             :             }
     294           0 :             set_put_string(curl_handle_, body.c_str(), body.size());
     295           0 :             http_put(curl_handle_, gi);
     296           0 :             break;
     297             : 
     298           0 :         case HTTP_DELETE:
     299           0 :             http_delete(curl_handle_, gi);
     300           0 :             break;
     301             : 
     302           0 :         default:
     303           0 :             assert(0);
     304             :     }
     305           0 : }
     306             : 
     307           0 : void HttpConnection::AssignData(const char *ptr, size_t size) {
     308             : 
     309           0 :     buf_.assign(ptr, size);
     310             : 
     311             :     // callback to client
     312           0 :     boost::system::error_code error;
     313           0 :     if (cb_ != NULL)
     314           0 :         cb_(buf_, error);
     315           0 : }
     316             : 
     317           0 : void HttpConnection::AssignHeader(const char *ptr, size_t size) {
     318             : 
     319           0 :     buf_.assign(ptr, size);
     320             : 
     321           0 :     switch (state_) {
     322           0 :         case STATUS: {
     323           0 :             status_ = 0;
     324           0 :             int i = buf_.find(' ', 0);
     325           0 :             version_ = boost::algorithm::trim_copy(buf_.substr(0, i));
     326           0 :             int j = buf_.find(' ', i+1);
     327           0 :             status_ = atoi(buf_.substr(i+1, j).c_str());
     328           0 :             reason_ = boost::algorithm::trim_copy(buf_.substr(j+1));
     329             : #ifdef __DEBUG__
     330             :             //for (std::string::iterator ii=buf_.begin()+i+1;
     331             :             //        ii != buf_.begin()+j; ii++) {
     332             :             //    status_ = (status_ << 3) + (status_ << 1) + (*ii - '0');
     333             :             //}
     334             :             std::cout << "Status Line: " << std::dec << status_ << ":"
     335             :                 << reason_ << "(" << version_ << ")" << std::endl;
     336             : #endif
     337           0 :             state_ = HEADER;
     338           0 :             break;
     339             :                      }
     340           0 :         case HEADER:
     341           0 :             if (buf_ != "\r\n") {
     342           0 :                 std::istringstream iss(buf_);
     343           0 :                 std::string tok;
     344           0 :                 while (std::getline(iss, tok, '\r')  && tok != "\n") {
     345           0 :                     unsigned int i = tok.find(':', 0);
     346             :                     if (i != std::string::npos) {
     347           0 :                         headers_.insert(std::make_pair(
     348           0 :                             boost::algorithm::trim_copy(tok.substr(0, i)),
     349           0 :                             boost::algorithm::trim_copy(tok.substr(i + 1))));
     350             :                     }
     351             :                 }
     352           0 :             }
     353           0 :             break;
     354             :     }
     355           0 :     boost::system::error_code error;
     356             :     // callback to client *backward compatibility*
     357           0 :     if (sent_hdr_ && cb_ != NULL) {
     358           0 :         cb_(buf_, error);
     359             :     }
     360           0 : }
     361             : 
     362           0 : const std::string &HttpConnection::GetData() {
     363           0 :     return buf_;
     364             : }
     365             : 
     366           0 : void HttpConnection::UpdateOffset(size_t bytes) {
     367           0 :     offset_ += bytes;
     368           0 : }
     369             : 
     370           0 : size_t HttpConnection::GetOffset() {
     371           0 :     return offset_;
     372             : }
     373             : 
     374           2 : HttpClient::HttpClient(EventManager *evm, std::string task_name) :
     375             :   TcpServer(evm),
     376           2 :   curl_timer_(TimerManager::CreateTimer(*evm->io_service(), task_name,
     377             :               TaskScheduler::GetInstance()->GetTaskId(task_name), 0)),
     378           2 :   id_(0), work_queue_(TaskScheduler::GetInstance()->GetTaskId(task_name), 0,
     379           4 :               boost::bind(&HttpClient::DequeueEvent, this, _1)) {
     380           2 :     gi_ = (struct _GlobalInfo *)malloc(sizeof(struct _GlobalInfo));
     381           2 :     memset(gi_, 0, sizeof(struct _GlobalInfo));
     382           2 : }
     383             : 
     384           2 : void HttpClient::ShutdownInternal() {
     385             : 
     386           2 :     for (HttpConnectionMap::iterator iter = map_.begin(), next = iter;
     387           2 :          iter != map_.end(); iter = next) {
     388           0 :         next++;
     389           0 :         RemoveConnectionInternal(iter->second);
     390             :     }
     391             : 
     392           2 :     curl_multi_cleanup(gi_->multi);
     393           2 :     TimerManager::DeleteTimer(curl_timer_);
     394           2 :     SessionShutdown();
     395             : 
     396             :     /* Schedule a shutdown of WorkQueue */
     397           2 :     work_queue_.ScheduleShutdown();
     398           2 :     assert(!map_.size());
     399           2 : }
     400             : 
     401             : /*
     402             :  * Ensure task that calls this function is not mutually exclusive
     403             :  * to the task that runs HttpClient::ShutdownInternal()
     404             :  *
     405             :  * Tight loop to check the Callback has been scheduled
     406             :  */
     407           2 : void HttpClient::Shutdown() {
     408           2 :     work_queue_.Enqueue(boost::bind(&HttpClient::ShutdownInternal, 
     409             :                         this));
     410             : 
     411           2 :     uint32_t count = 0;
     412           4 :     while (!(work_queue_.deleted() == true) && count++ < 10000) {
     413           2 :         usleep(1000);
     414             :     }
     415             : 
     416           2 :     assert(work_queue_.deleted() == true);
     417           2 : }
     418             : 
     419           2 : HttpClient::~HttpClient() {
     420           2 :     free(gi_);
     421           2 : }
     422             : 
     423           2 : void HttpClient::Init() {
     424           2 :     curl_init(this);
     425           2 : }
     426             : 
     427           2 : void HttpClient::SessionShutdown() {
     428           2 :     TcpServer::Shutdown();
     429           2 : }
     430             : 
     431           0 : boost::asio::io_context *HttpClient::io_service() {
     432           0 :     return this->event_manager()->io_service();
     433             : };
     434             : 
     435           0 : TcpSession *HttpClient::AllocSession(Socket *socket) {
     436           0 :     HttpClientSession *session = new HttpClientSession(this, socket);
     437           0 :     return session;
     438             : }
     439             : 
     440           0 : TcpSession *HttpClient::CreateSession() {
     441           0 :     TcpSession *session = TcpServer::CreateSession();
     442           0 :     Socket *socket = session->socket();
     443           0 :     boost::system::error_code err;
     444           0 :     socket->open(boost::asio::ip::tcp::v4(), err);
     445             : 
     446           0 :     if (err) {
     447           0 :         LOG(ERROR, "http socket open failed: " << err);
     448           0 :         return NULL;
     449             :     }
     450             : 
     451           0 :     err = session->SetSocketOptions();
     452           0 :     return session;
     453             : }
     454             : 
     455           0 : HttpConnection *HttpClient::CreateConnection(boost::asio::ip::tcp::endpoint ep) {
     456           0 :     HttpConnection *conn = new HttpConnection(ep, ++id_, this);
     457           0 :     return conn;
     458             : }
     459             : 
     460           0 : HttpConnection *HttpClient::CreateConnection(const std::string& host, int port) {
     461           0 :     HttpConnection *conn = new HttpConnection(host, port, ++id_, this);
     462           0 :     return conn;
     463             : }
     464             : 
     465           0 : bool HttpClient::AddConnection(HttpConnection *conn) {
     466           0 :     Key key = std::make_pair(conn->endpoint(), conn->id());
     467           0 :     if (map_.find(key) == map_.end()) {
     468           0 :         map_.insert(key, conn);
     469           0 :         return true;
     470             :     }
     471           0 :     return false;
     472             : }
     473             : 
     474           0 : void HttpClient::RemoveConnection(HttpConnection *connection) {
     475           0 :     connection->ClearCallback();
     476           0 :     work_queue_.Enqueue(boost::bind(&HttpClient::RemoveConnectionInternal, 
     477             :                                      this, connection));
     478           0 : }
     479             : 
     480           0 : void HttpClient::ProcessEvent(EnqueuedCb cb) {
     481           0 :     if(!cb.empty()) {
     482           0 :        work_queue_.Enqueue(cb);
     483             :     }
     484           0 : }
     485             : 
     486           0 : void HttpClient::TimerErrorHandler(std::string name, std::string error) {
     487           0 : }
     488             : 
     489           0 : bool HttpClient::TimerCb() {
     490           0 :     return timer_cb(gi_);
     491             : }
     492             : 
     493           0 : void HttpClient::StartTimer(long timeout_ms) {
     494           0 :     CancelTimer();
     495           0 :     curl_timer_->Start(timeout_ms, boost::bind(&HttpClient::TimerCb, this)); 
     496           0 : }
     497             : 
     498           0 : void HttpClient::CancelTimer() {
     499           0 :     curl_timer_->Cancel();
     500           0 : }
     501             : 
     502           0 : bool HttpClient::IsErrorHard(const boost::system::error_code &ec) {
     503           0 :     return TcpSession::IsSocketErrorHard(ec);
     504             : }
     505             : 
     506           0 : void HttpClient::RemoveConnectionInternal(HttpConnection *connection) {
     507           0 :     boost::asio::ip::tcp::endpoint endpoint = connection->endpoint();
     508           0 :     size_t id = connection->id();
     509           0 :     del_conn(connection, gi_);
     510           0 :     map_.erase(std::make_pair(endpoint, id));
     511           0 :     return;
     512             : }
     513             : 
     514           2 : bool HttpClient::DequeueEvent(EnqueuedCb cb) {
     515           2 :     cb();
     516           2 :     return true;
     517             : }

Generated by: LCOV version 1.14