LCOV - code coverage report
Current view: top level - root/contrail/src/contrail-common/sandesh/library/cpp - sandesh_http.cc (source / functions) Hit Total Coverage
Test: OpenSDN C/C++ coverage (all TARGET_SET jobs) Lines: 115 179 64.2 %
Date: 2026-11-09 02:10:49 Functions: 5 7 71.4 %
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             : //
       7             : // sandesh_http.cc
       8             : //
       9             : // Implementation of SandeshHttp
      10             : //
      11             : // - Register each .sandesh file for generating webpage with all Requests
      12             : // - API for Registering Sandesh Requests so they can be served by HttpServer
      13             : // - Init and Uninit of HttpServer
      14             : //
      15             : 
      16             : #include <cstring>
      17             : #include <cstdlib>
      18             : #include <mutex>
      19             : 
      20             : #include "io/event_manager.h"
      21             : #include "http/http_server.h"
      22             : #include <boost/bind/bind.hpp>
      23             : #include <boost/tokenizer.hpp>
      24             : 
      25             : #include <sandesh/sandesh_types.h>
      26             : #include <sandesh/sandesh.h>
      27             : #include <sandesh/sandesh_http.h>
      28             : #include <sandesh/transport/TBufferTransports.h>
      29             : #include <sandesh/protocol/TXMLProtocol.h>
      30             : #include "sandesh_client.h"
      31             : #include <sandesh/sandesh_trace.h>
      32             : #include <sandesh/sandesh_trace_types.h>
      33             : 
      34             : #include "http/http_request.h"
      35             : #include "http/http_server.h"
      36             : #include "http/http_session.h"
      37             : 
      38             : #include "css_bootstrap_min_css.cpp"
      39             : #include "css_DT_bootstrap_css.cpp"
      40             : #include "css_images_sort_asc_png.cpp"
      41             : #include "css_images_sort_asc_disabled_png.cpp"
      42             : #include "css_images_sort_both_png.cpp"
      43             : #include "css_images_sort_desc_png.cpp"
      44             : #include "css_images_sort_desc_disabled_png.cpp"
      45             : #include "css_style_css.cpp"
      46             : #include "js_bootstrap_min_js.cpp"
      47             : #include "js_DT_bootstrap_js.cpp"
      48             : #include "js_jquery_2_0_3_min_js.cpp"
      49             : #include "js_jquery_dataTables_min_js.cpp"
      50             : #include "js_util_js.cpp"
      51             : #include "universal_parse_xsl.cpp"
      52             : 
      53             : 
      54             : static int kEncodeBufferSize = 4096;
      55             : 
      56             : using namespace contrail::sandesh::protocol;
      57             : using namespace std;
      58             : using namespace boost::placeholders;
      59             : 
      60             : using contrail::sandesh::transport::TMemoryBuffer;
      61             : 
      62             : SandeshHttp::map_type* SandeshHttp::map_ = NULL;
      63             : HttpServer* SandeshHttp::hServ_ = NULL;
      64             : string SandeshHttp::index_str_;
      65             : SandeshHttp::HtmlInfo *SandeshHttp::index_hti_ = NULL;
      66             : 
      67             : 
      68             : #define SESSION_SEND(buf) \
      69             :     HttpSession::SendSession(context,reinterpret_cast<const u_int8_t *>(buf), strlen(buf), NULL)
      70             : 
      71             : enum HttpXMLState {
      72             :     HXMLInvalid,
      73             :     HXMLNew,
      74             :     HXMLIncomplete,
      75             :     HXMLMax
      76             : };
      77             : 
      78             : // Helper function for forming HTTP headers and sending a bytestream
      79             : // for XML
      80             : //
      81             : // Arguments:
      82             : //   context : handle to Session on which to send bytestream
      83             : //   buf : Buffer that contains XML payload
      84             : //   len : length of buffer
      85             : //   name : Name of Sandesh Module
      86             : //   more : This is true if there is more content coming for this response
      87             : //
      88             : static void
      89          10 : HttpSendXML(const std::string& context, const u_int8_t * buf, uint32_t len,
      90             :             const char * name, bool more) {
      91             : 
      92             :     char buffer_str[100];
      93             :     char length_str[80];
      94             :     static const char xsl_response[] =
      95             : "HTTP/1.1 200 OK\r\n"
      96             : "Content-Type: text/xml\r\n"
      97             : ;
      98             :     static const char chunk_response[] =
      99             : "HTTP/1.1 200 OK\r\n"
     100             : "Content-Type: text/xml\r\n"
     101             : "Transfer-Encoding: chunked\r\n\r\n"
     102             : ;
     103             :     char resp_name[40];
     104          10 :     int loc = strcspn(reinterpret_cast<const char *>(buf)," ");
     105          10 :     strncpy(resp_name, reinterpret_cast<const char *>(buf + 1), loc - 1);
     106          10 :     resp_name[loc - 1] = 0;
     107             : 
     108             :     static std::mutex hmutex;
     109             :     static int seq = 0;
     110          10 :     std::scoped_lock lock(hmutex);
     111          10 :     std::string client_ctx;
     112          10 :     seq++;
     113             : 
     114          10 :     client_ctx = HttpSession::get_client_context(context);
     115             :     HttpXMLState state;
     116             : 
     117             :     // Calculate current state;
     118          10 :     if (!client_ctx.empty()) {
     119           0 :         state = HXMLIncomplete;
     120             :     } else {
     121          10 :         state = HXMLNew;
     122          10 :         client_ctx = resp_name;
     123             : 
     124             :         // If the session is gone, we can stop processing.
     125          10 :         if (!HttpSession::set_client_context(context, client_ctx))
     126           0 :             return;
     127             :     }
     128             : 
     129          10 :     if ((HXMLNew == state) && (!more)) {
     130             :         // This is the first and last chunk of this response
     131             : 
     132          10 :         SESSION_SEND(xsl_response);
     133             : 
     134          10 :         size_t total_len = len;
     135          10 :         sprintf(buffer_str,
     136             :             "<?xml-stylesheet type=\"text/xsl\" href=\"/universal_parse.xsl\"?>"
     137             :             );
     138          10 :         sprintf(length_str,
     139          10 :                 "Content-Length: %zu\r\n\r\n", total_len + strlen(buffer_str));
     140          10 :         SESSION_SEND(length_str);
     141          10 :         SESSION_SEND(buffer_str);
     142             : 
     143          10 :         HttpSession::SendSession(context, buf, len, NULL);
     144             : 
     145          10 :     } else if ((HXMLNew == state) && more) {
     146             :         // This is the first chunk of this response
     147           0 :         SESSION_SEND(chunk_response);
     148             : 
     149             :         // Calculation for Header
     150           0 :         sprintf(buffer_str,
     151             :             "<?xml-stylesheet type=\"text/xsl\" href=\"/universal_parse.xsl\"?>"
     152             :             );
     153           0 :         int hdr_len = strlen(buffer_str);
     154           0 :         sprintf(buffer_str,
     155             :             "<__%s_list type=\"slist\">\r\n", client_ctx.c_str());
     156           0 :         hdr_len += (strlen(buffer_str) - 2);
     157             : 
     158           0 :         sprintf(length_str, "%x\r\n", hdr_len);
     159           0 :         SESSION_SEND(length_str);
     160             : 
     161           0 :         sprintf(buffer_str,
     162             :             "<?xml-stylesheet type=\"text/xsl\" href=\"/universal_parse.xsl\"?>"
     163             :             );
     164           0 :         SESSION_SEND(buffer_str);
     165             : 
     166           0 :         sprintf(buffer_str,
     167             :             "<__%s_list type=\"slist\">\r\n", client_ctx.c_str());
     168           0 :         SESSION_SEND(buffer_str);
     169             : 
     170             : 
     171             :         // Calculation for Data
     172           0 :         sprintf(length_str, "%x\r\n", len);
     173           0 :         SESSION_SEND(length_str);
     174             : 
     175           0 :         HttpSession::SendSession(context, buf, len, NULL);
     176             : 
     177           0 :         sprintf(buffer_str,"\r\n");
     178           0 :         SESSION_SEND(buffer_str);
     179             : 
     180           0 :     } else if ((HXMLIncomplete == state) && !more) {
     181             :         // This is the last chunk of this response
     182             : 
     183             :         // Calculation for Data
     184           0 :         sprintf(length_str, "%x\r\n", len);
     185           0 :         SESSION_SEND(length_str);
     186             : 
     187           0 :         HttpSession::SendSession(context, buf, len, NULL);
     188             : 
     189           0 :         sprintf(buffer_str,"\r\n");
     190           0 :         SESSION_SEND(buffer_str);
     191             : 
     192             :         //Calculation for Footer
     193           0 :         sprintf(buffer_str,"</__%s_list>\r\n", client_ctx.c_str());
     194           0 :         sprintf(length_str, "%zx\r\n", strlen(buffer_str) - 2);
     195           0 :         SESSION_SEND(length_str);
     196           0 :         SESSION_SEND(buffer_str);
     197             : 
     198           0 :         sprintf(length_str, "0\r\n\r\n");
     199           0 :         SESSION_SEND(length_str);
     200           0 :     } else if ((HXMLIncomplete == state) && more) {
     201             :         // This is a middle chunk of the response
     202             : 
     203             :         // Calculation for Data
     204           0 :         sprintf(length_str, "%x\r\n", len);
     205           0 :         SESSION_SEND(length_str);
     206             : 
     207           0 :         HttpSession::SendSession(context, buf, len, NULL);
     208             : 
     209           0 :         sprintf(buffer_str,"\r\n");
     210           0 :         SESSION_SEND(buffer_str);
     211             :     }
     212             :     // Update context for other users
     213          10 :     if (!more) {
     214          10 :         client_ctx = resp_name;
     215          10 :         HttpSession::set_client_context(context,"");
     216             :     }
     217             : 
     218          10 : }
     219             : 
     220             : // Function for HTTP Server to call when HTTP Client Requests a .sandesh module
     221             : // or it's stylesheet
     222             : //
     223             : // Arguments:
     224             : //   hti : Buffer Ptr and length of HTTP payload to send
     225             : //   session : HttpSession on which to send
     226             : //   request : Contains the URL that was sent by the Client
     227             : //
     228             : static void
     229           0 : HttpSandeshFileCallback(SandeshHttp::HtmlInfo *hti,
     230             :         HttpSession *session,
     231             :         const HttpRequest *request) {
     232             : 
     233             :     char length_str[80];
     234             :     static const char html_response[] =
     235             : "HTTP/1.1 200 OK\r\n"
     236             : ;
     237             :     static const char json_response[] =
     238             : "HTTP/1.1 200 OK\r\n"
     239             : "Content-Type: application/json\r\n"
     240             : ;
     241             :     static const char xsl_response[] =
     242             : "HTTP/1.1 200 OK\r\n"
     243             : "Content-Type: text/xsl\r\n"
     244             : ;
     245             :     static const char xml_response[] =
     246             : "HTTP/1.1 200 OK\r\n"
     247             : "Content-Type: text/xml\r\n"
     248             : ;
     249             :     static const char css_response[] =
     250             : "HTTP/1.1 200 OK\r\n"
     251             : "Content-Type: text/css\r\n"
     252             : ;
     253             :     const char * response =
     254           0 :         ((request->UrlPath().find(".js")!=string::npos) ?
     255           0 :          json_response : ((request->UrlPath().find(".xsl")!=string::npos) ?
     256           0 :             xsl_response : ((request->UrlPath().find(".css")!=string::npos) ?
     257           0 :                 css_response : ((request->UrlPath().find(".xml")!=string::npos) ?
     258           0 :                     xml_response : html_response))));
     259             : 
     260           0 :     sprintf(length_str,
     261             :             "Content-Length: %d\r\n\r\n", hti->html_len());
     262           0 :     session->Send(reinterpret_cast<const u_int8_t *>(response),
     263             :             strlen(response), NULL);
     264           0 :     session->Send(reinterpret_cast<const u_int8_t *>(length_str),
     265             :             strlen(length_str), NULL);
     266           0 :     session->Send(hti->html_str(), hti->html_len(), NULL);
     267           0 :     delete request;
     268           0 : }
     269             : 
     270             : SandeshHttp::RequestCallbackFn httpreqcb;
     271             : 
     272             : // Function for HTTP Server to call when HTTP Client sends a Sandesh Request
     273             : //
     274             : // Arguments:
     275             : //   hs : Ptr to the HTTP Server
     276             : //   session : HttpSession on which to send
     277             : //   request : Contains the URL that was sent by the Client
     278             : //             This URL encodes the attributes of the Sandesh Request
     279             : //
     280             : static void
     281          10 : HttpSandeshRequestCallback(HttpServer *hs,
     282             :         HttpSession *session,
     283             :         const HttpRequest *request) {
     284             : 
     285          10 :     string snh_name = request->UrlPath().substr(5);
     286          10 :     Sandesh *sandesh = SandeshBaseFactory::CreateInstance(snh_name);
     287          10 :     if (sandesh == NULL) {
     288           0 :         SANDESH_LOG(DEBUG, __func__ << " Unknown sandesh:" <<
     289             :             snh_name << std::endl);
     290           0 :         return;
     291             :     }
     292          10 :     SandeshRequest *rsnh = dynamic_cast<SandeshRequest *>(sandesh);
     293          10 :     assert(rsnh);
     294          10 :     rsnh->RequestFromHttp(session->get_context(), request->UrlQuery());
     295          10 :     httpreqcb(rsnh);
     296          10 :     delete request;
     297          10 : }
     298             : 
     299             : // This function is called by the Sandesh Response handling code
     300             : // if the "context" of the Originating Sandesh Request indicates
     301             : // that the Request came from the HTTP Server
     302             : // We will form a HTTP/XML payload to send the contents of the Sandesh
     303             : // response back to the HTTP Client
     304             : //
     305             : // Arguments:
     306             : //   snh : Sandesh Response to send to the HTTP Client (base class)
     307             : //   context: Context that was in the Sandesh Request.
     308             : //            All HTTP Server-originated contexts start with "http%"
     309             : //
     310             : void
     311          10 : SandeshHttp::Response(Sandesh *snh, std::string context) {
     312          10 :     bool more = false;
     313             : 
     314          10 :     SandeshResponse * rsnh = dynamic_cast<SandeshResponse *>(snh);
     315          10 :     if (rsnh) {
     316          10 :         more = rsnh->get_more();
     317             :     } else {
     318           0 :         SandeshTrace * tsnh = dynamic_cast<SandeshTrace *>(snh);
     319           0 :         if (tsnh) {
     320           0 :             more = tsnh->get_more();
     321             :         } else {
     322           0 :             SandeshUVE * usnh = dynamic_cast<SandeshUVE *>(snh);
     323           0 :             assert(usnh);
     324           0 :             more = usnh->get_more();
     325             :         }
     326             :     }
     327          10 :     std::stringstream ss;
     328             :     uint8_t *buffer;
     329          10 :     uint32_t xfer = 0, offset;
     330             : 
     331             :     boost::shared_ptr<TMemoryBuffer> btrans =
     332             :             boost::shared_ptr<TMemoryBuffer>(
     333          10 :                     new TMemoryBuffer(kEncodeBufferSize));
     334             :     boost::shared_ptr<TXMLProtocol> prot =
     335             :             boost::shared_ptr<TXMLProtocol>(
     336          10 :                     new TXMLProtocol(btrans));
     337             :     // Write the sandesh
     338          10 :     xfer += snh->Write(prot);
     339             :     // Get the buffer
     340          10 :     btrans->getBuffer(&buffer, &offset);
     341          10 :     HttpSendXML(context, buffer, offset, snh->ModuleName().c_str(), more);
     342          10 :     buffer[offset] = 0;
     343          10 :     snh->Release();
     344          10 : }
     345             : 
     346             : // This function should be called during Sandesh Generator Initialization
     347             : // It initializes the HTTP Server and Registers callbacks for sandesh modules
     348             : // and sandesh requests.
     349             : //
     350             : // Arguments:
     351             : //   evm : EventManager that should be used by the HTTP Server
     352             : //   module : Name of module
     353             : //   port : Port number for HTTP Server (e.g. 8080)
     354             : //
     355             : bool
     356          86 : SandeshHttp::Init(EventManager *evm, const string module,
     357             :     short port, RequestCallbackFn reqcb, int *hport, const SandeshConfig &config) {
     358          86 :     if (hServ_) {
     359           0 :         *hport = hServ_->GetPort();
     360          86 :         return true;
     361             :     }
     362          86 :     ostringstream index_ss;
     363             : 
     364         172 :     SandeshTraceBufferPtr httpbuf(SandeshTraceBufferCreate("httpbuf", 100));
     365          86 :     SANDESH_TRACE_TEXT_TRACE(httpbuf, "<Initializing httpbuf");
     366          86 :     SANDESH_TRACE_TEXT_TRACE(httpbuf, "Size 100");
     367             : 
     368          86 :     uint8_t dscp = 0;
     369          86 :     SandeshClient *client = Sandesh::client();
     370          86 :     if (client) {
     371           0 :         dscp = client->dscp_value();
     372             :     }
     373          86 :     struct SslConfig sslConfig;
     374          86 :     sslConfig.ssl_enabled = config.introspect_ssl_enable;
     375          86 :     sslConfig.ca_cert = config.ca_cert;
     376          86 :     sslConfig.certfile = config.server_certfile;
     377          86 :     sslConfig.keyfile = config.server_keyfile;
     378          86 :     sslConfig.ssl_insecure = config.introspect_ssl_insecure;
     379          86 :     hServ_ = new HttpServer(evm, sslConfig, dscp);
     380          86 :     httpreqcb = reqcb;
     381             :     index_ss << "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\"" <<
     382          86 :         " \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">" << endl;
     383          86 :     index_ss << "<html xmlns=\"http://www.w3.org/1999/xhtml\">" << endl;
     384             :     index_ss << "<head>" <<
     385             :         "<link href=\"css/style.css\" rel=\"stylesheet\" type=\"text/css\"/>" <<
     386          86 :         "<title>" << module << "</title></head><body>" << endl;
     387          86 :     index_ss << "<h1>Modules for " << module << "</h1>" << endl;
     388             : 
     389        1880 :     for (map_type::iterator it=map_->begin(); it!=map_->end(); it++) {
     390        1794 :         std::string regString = "/" + it->first;
     391        1794 :         hServ_->RegisterHandler(regString.c_str(),
     392        1794 :                 boost::bind(&HttpSandeshFileCallback, &it->second, _1, _2));
     393        1794 :         if (it->first.find(".xml")!=string::npos) {
     394        1180 :             index_ss << "<a href=\"" << it->first << "\">" <<
     395         590 :                 it->first << "</a><br/>" << endl;
     396             :         }
     397        1794 :     }
     398          86 :     index_ss << "</body></html>" << endl;
     399             : 
     400          86 :     index_str_ = index_ss.str();
     401          86 :     const char * index_str = index_str_.c_str();
     402          86 :     SANDESH_LOG(DEBUG, "HTTP Introspect Init");
     403             : 
     404          86 :     index_hti_ = new HtmlInfo(reinterpret_cast<const unsigned char *>(
     405          86 :                 index_str),strlen(index_str));
     406          86 :     hServ_->RegisterHandler("/index.html",
     407             :         boost::bind(&HttpSandeshFileCallback, index_hti_, _1, _2));
     408          86 :     hServ_->RegisterHandler("/",
     409             :         boost::bind(&HttpSandeshFileCallback, index_hti_, _1, _2));
     410             : 
     411          86 :     SandeshBaseFactory::map_type::const_iterator it= SandeshBaseFactory::Begin();
     412        5904 :     for (; it!=SandeshBaseFactory::End(); it++) {
     413        5818 :         std::string regString = "/Snh_" + (*it).first;
     414        5818 :         hServ_->RegisterHandler(regString.c_str(),
     415             :             boost::bind(&HttpSandeshRequestCallback, hServ_, _1, _2));
     416        5818 :     }
     417             : 
     418          86 :     boost::system::error_code ec;
     419             :     bool success;
     420             :     IpAddress http_ip =
     421          86 :         boost::asio::ip::address::from_string(config.http_server_ip, ec);
     422          86 :     if (!ec) {
     423          44 :         success = hServ_->Initialize(port, http_ip);
     424             :     } else {
     425          42 :         success = hServ_->Initialize(port);
     426             :     }
     427          86 :     hServ_->SetSocketOptions(config);
     428          86 :     if (success) {
     429          86 :         int lport(hServ_->GetPort());
     430          86 :         SANDESH_LOG(DEBUG, "Sandesh Http Server Port: " << lport);
     431          86 :         *hport = lport;
     432          86 :         return true;
     433             :     } else {
     434           0 :         SANDESH_LOG(ERROR, "Failed to initialize Sandesh Http Server Port: "
     435             :             << port);
     436           0 :         return false;
     437             :     }
     438             :     if (dscp) {
     439             :         hServ_->SetListenSocketDscp(dscp);
     440             :     }
     441         258 : }
     442             : 
     443             : // Function to shut down HTTP Server
     444             : void
     445          86 : SandeshHttp::Uninit(void) {
     446          86 :     if (!hServ_) return;
     447             : 
     448          86 :     index_str_.clear();
     449          86 :     delete index_hti_;
     450             : 
     451          86 :     hServ_->Shutdown();
     452          86 :     hServ_->ClearSessions();
     453          86 :     hServ_->WaitForEmpty();
     454             : 
     455             :     //
     456             :     // TODO TcpServer delete can only happen after all inflight sessions
     457             :     // have been properly deleted. ASIO threads can hold reference to
     458             :     // HttpSession objects
     459             :     //
     460          86 :     TcpServerManager::DeleteServer(hServ_);
     461          86 :     hServ_ = NULL;
     462             : }
     463             : 
     464             : void
     465           0 : SandeshHttp::UpdateDscp(uint8_t dscp) {
     466           0 :     if (!hServ_) return;
     467           0 :     hServ_->UpdateDscp(dscp);
     468           0 :     hServ_->UpdateSessionsDscp(dscp);
     469             : }

Generated by: LCOV version 1.14