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

Generated by: LCOV version 1.14