LCOV - code coverage report
Current view: top level - root/contrail/src/contrail-common/http - http_session.cc (source / functions) Hit Total Coverage
Test: OpenSDN C/C++ coverage (all TARGET_SET jobs) Lines: 145 162 89.5 %
Date: 2026-08-03 02:19:58 Functions: 23 28 82.1 %
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/http_session.h"
       6             : 
       7             : #include <map>
       8             : #include <boost/bind/bind.hpp>
       9             : #include <cstdio>
      10             : 
      11             : #include "base/logging.h"
      12             : #include "base/task.h"
      13             : #include "http/http_request.h"
      14             : #include "http/http_server.h"
      15             : #include "http_parser/http_parser.h"
      16             : #include "sandesh/sandesh_http.h"
      17             : #include "http/http_log_types.h"
      18             : 
      19             : using namespace std;
      20             : using namespace boost::placeholders;
      21             : 
      22             : int HttpSession::req_handler_task_id_ = -1;
      23             : HttpSession::map_type * HttpSession::context_map_ = 0;
      24             : std::mutex HttpSession::map_mutex_;
      25             : std::atomic<long> HttpSession::task_count_;
      26             : 
      27             : // Input processing context
      28             : class HttpSession::RequestBuilder {
      29             : public:
      30          10 :     RequestBuilder() : complete_(false) {
      31          10 :         parser_.data = this;
      32          10 :         Clear();
      33          10 :     }
      34             : 
      35          20 :     void Clear() {
      36          20 :         http_parser_init(&parser_, HTTP_REQUEST);
      37          20 :         request_.reset(new HttpRequest());
      38          20 :         complete_ = false;
      39          20 :         header_key_.clear();
      40          20 :         header_value_.clear();
      41          20 :     }
      42          10 :     size_t Parse(const u_int8_t *data, size_t datalen) {
      43             :         size_t nparsed = 
      44          10 :             http_parser_execute(&parser_, &settings_,
      45             :                     reinterpret_cast<const char *>(data), datalen);
      46          10 :         return nparsed;
      47             :     }
      48             : 
      49          10 :     bool complete() const { return complete_; }
      50             : 
      51             :     // Transfers ownership
      52          10 :     HttpRequest *GetRequest() {
      53          10 :         HttpRequest *request = request_.get();
      54          10 :         request_.release();
      55          10 :         return request;
      56             :     }
      57             : private:
      58          10 :     static int OnMessageBegin(struct http_parser *parser) {
      59          10 :         return 0;
      60             :     }
      61             : 
      62          10 :     static int OnHeadersComplete(struct http_parser *parser) {
      63          10 :         RequestBuilder *builder =
      64             :             reinterpret_cast<RequestBuilder *>(parser->data);
      65          10 :         builder->request_->SetMethod(static_cast<http_method>(parser->method));
      66          10 :         builder->request_->SetUrl(&builder->tmp_url_);
      67          10 :         builder->PushHeader();
      68          10 :         return 0;
      69             :     }
      70             : 
      71          10 :     static int OnMessageComplete(struct http_parser *parser) {
      72          10 :         RequestBuilder *builder =
      73             :             reinterpret_cast<RequestBuilder *>(parser->data);
      74          10 :         builder->complete_ = true;
      75          10 :         return 0;
      76             :     }
      77             : 
      78          10 :     static int OnUrl(struct http_parser *parser,
      79             :                  const char *loc, size_t length) {
      80          10 :         RequestBuilder *builder =
      81             :             reinterpret_cast<RequestBuilder *>(parser->data);
      82          10 :         builder->tmp_url_.append(loc, length);
      83          10 :         return 0;
      84             :     }
      85             : 
      86           0 :     static int OnStatusComplete(struct http_parser *parser) {
      87           0 :         return 0;
      88             :     }
      89             : 
      90          50 :     static int OnHeaderField(struct http_parser *parser,
      91             :                  const char *loc, size_t length) {
      92          50 :         RequestBuilder *builder =
      93             :             reinterpret_cast<RequestBuilder *>(parser->data);
      94          50 :         if (!builder->header_value_.empty()) {
      95          40 :             builder->PushHeader();
      96             :         }
      97          50 :         builder->header_key_.append(loc, length);
      98          50 :         return 0;
      99             :     }
     100             : 
     101          50 :     static int OnHeaderValue(struct http_parser *parser,
     102             :                      const char *loc, size_t length) {
     103          50 :         RequestBuilder *builder =
     104             :             reinterpret_cast<RequestBuilder *>(parser->data);
     105          50 :         builder->header_value_.append(loc, length);
     106          50 :         return 0;
     107             :     }
     108             : 
     109           0 :     static int OnBody(struct http_parser *parser,
     110             :               const char *loc, size_t length) {
     111           0 :         RequestBuilder *builder =
     112             :             reinterpret_cast<RequestBuilder *>(parser->data);
     113           0 :         builder->request_->SetBody(loc, length);
     114           0 :         return 0;
     115             :     }
     116             : 
     117          50 :     void PushHeader() {
     118          50 :         request_->PushHeader(header_key_, header_value_);
     119          50 :         header_key_.clear();
     120          50 :         header_value_.clear();
     121          50 :     }
     122             : 
     123             :     static struct http_parser_settings settings_;
     124             : 
     125             :     struct http_parser parser_;
     126             :     std::unique_ptr<HttpRequest> request_;
     127             : 
     128             :     bool complete_;
     129             :     string tmp_url_;        // temporary: used while parsing
     130             :     string header_key_;
     131             :     string header_value_;
     132             : };
     133             : 
     134             : struct http_parser_settings HttpSession::RequestBuilder::settings_ = {
     135             :     OnMessageBegin,
     136             :     OnUrl,
     137             :     OnStatusComplete,
     138             :     OnHeaderField,
     139             :     OnHeaderValue,
     140             :     OnHeadersComplete,
     141             :     OnBody,
     142             :     OnMessageComplete
     143             : };
     144             : 
     145             : #undef HTTP_SYS_LOG
     146             : #define HTTP_SYS_LOG(...)
     147             : 
     148             : class HttpSession::RequestHandler : public Task {
     149             : public:
     150          20 :     explicit RequestHandler(HttpSession *session)
     151          20 :     : Task(req_handler_task_id_, session->GetSessionInstance()), 
     152          20 :       session_(session) {
     153          20 :     }
     154             : 
     155          40 :     ~RequestHandler() {
     156             :         HTTP_SYS_LOG("HttpSession", SandeshLevel::UT_INFO, "RequestHandler destructor");
     157          40 :     }
     158             : 
     159           0 :     void NotFound(HttpSession *session, const HttpRequest *request) {
     160             :         static const char no_response[] =
     161             : "HTTP/1.1 404 Not Found\r\n"
     162             : "Content-Type: text/html; charset=UTF-8\r\n"
     163             : "Content-Length: 46\r\n"
     164             : "\r\n"
     165             : "<html>\n"
     166             : "<title>404 Not Found</title>\n"
     167             : "</html>\r\n"
     168             : ;
     169           0 :         session->Send(reinterpret_cast<const u_int8_t *>(no_response),
     170             :               sizeof(no_response), NULL);
     171           0 :         delete request;
     172           0 :     }
     173             : 
     174             :     // Retrieve a request item from the queue. Return true if the queue
     175             :     // is empty.
     176          40 :     bool FromQ(HttpRequest *& r) {
     177          40 :         return !session_->request_queue_.try_pop(r);
     178             :     }
     179          20 :     virtual bool Run() {
     180             :         HTTP_SYS_LOG("HttpSession", SandeshLevel::UT_INFO,
     181             :                      "RequestHandler execute");
     182          20 :         HttpRequest *request = NULL;
     183          20 :         bool del_session = false;
     184          20 :         HttpServer *server = static_cast<HttpServer *>(session_->server());
     185             :         while (true) {
     186          40 :             request = NULL;
     187          40 :             session_->req_queue_empty_ = FromQ(request);
     188          40 :             if (!request) break;
     189             :             HTTP_SYS_LOG("HttpSession", SandeshLevel::UT_INFO,
     190             :                          "URL is " + request->ToString());
     191          20 :             if (request->ToString().empty()) {
     192          10 :                 if (session_->event_cb_ && !session_->event_cb_.empty()) {
     193           0 :                     session_->event_cb_(session_.get(), request->Event());
     194             :                 }
     195          10 :                 del_session = true;
     196          10 :                 session_->set_observer(NULL);
     197          10 :                 session_->Close();
     198          10 :                 delete request;
     199             :             } else {
     200             :                 HttpServer::HttpHandlerFn handler =
     201          10 :                         server->GetHandler(request->UrlPath());
     202          10 :                 if (handler == NULL) {
     203             :                     handler = boost::bind(&RequestHandler::NotFound,
     204           0 :                                           this, _1, _2);
     205             :                 }
     206          10 :                 handler(session_.get(), request);
     207          10 :             }
     208          20 :         }
     209          20 :         if (del_session) {
     210             :             HTTP_SYS_LOG("HttpSession", SandeshLevel::UT_INFO, "DeleteSession "
     211             :                 + session_->ToString());
     212          10 :             session_->set_observer(NULL);
     213          10 :             server->DeleteSession(session_.get());
     214             :         }
     215          20 :         HttpSession::task_count_--;
     216          20 :         return true;
     217             :     }
     218           0 :     std::string Description() const { return "HttpSession::RequestHandler"; }
     219             : private:
     220             :     HttpSessionPtr session_;
     221             : };
     222             : 
     223          10 : HttpSession::HttpSession(HttpServer *server, SslSocket *socket,
     224          10 :     bool async_ready)
     225          10 :     : SslSession(server, socket, async_ready), event_cb_(NULL) {
     226          10 :     if (req_handler_task_id_ == -1) {
     227           2 :         TaskScheduler *scheduler = TaskScheduler::GetInstance();
     228           2 :         req_handler_task_id_ = scheduler->GetTaskId("http::RequestHandlerTask");
     229             :     }
     230          10 :     req_queue_empty_ = true;
     231          10 :     set_observer(boost::bind(&HttpSession::OnSessionEvent, this, _1, _2));
     232          10 : }
     233             : 
     234          20 : HttpSession::~HttpSession() {
     235          10 :     HttpRequest *request = NULL;
     236          10 :     while (request_queue_.try_pop(request)) {
     237           0 :         delete request;
     238             :     }
     239          20 : }
     240             : 
     241          10 : void HttpSession::AcceptSession() {
     242          10 :     std::scoped_lock lock(map_mutex_);
     243          10 :     context_str_ = "http%" + ToString();
     244          10 :     GetMap()->insert(std::make_pair(context_str_, HttpSessionPtr(this)));
     245             :     HTTP_SYS_LOG("HttpSession", SandeshLevel::UT_INFO,
     246             :         "Created Session " + context_str_);
     247          10 : }
     248             : 
     249           0 : void HttpSession::RegisterEventCb(SessionEventCb cb) {
     250           0 :     event_cb_ = cb;
     251           0 : }
     252             : 
     253          20 : void HttpSession::OnSessionEvent(TcpSession *session,
     254             :         enum TcpSession::Event event) {
     255          20 :     HttpSession *h_session = dynamic_cast<HttpSession *>(session);
     256          20 :     assert(h_session);
     257             : 
     258          20 :     switch (event) {
     259          10 :     case TcpSession::CLOSE:
     260             :         {
     261             :             {
     262          10 :                 std::scoped_lock lock(map_mutex_);
     263          10 :                 if (GetMap()->erase(h_session->context_str_)) {
     264             :                     HTTP_SYS_LOG("HttpSession", SandeshLevel::UT_INFO,
     265             :                         "Removed Session " + h_session->context_str_);
     266             :                 } else {
     267             :                     HTTP_SYS_LOG("HttpSession", SandeshLevel::UT_INFO,
     268             :                         "Not Removed Session " + h_session->context_str_);                
     269             :                 }
     270          10 :             }
     271          10 :             h_session->context_str_ = "";
     272          10 :             HttpRequest *request = new HttpRequest();
     273          10 :             string nourl = "";
     274          10 :             request->SetUrl(&nourl);
     275          10 :             request->SetEvent(event);
     276          10 :             request_queue_.push(request);
     277             :             // Enqueue new RequestHandler task if needed
     278          10 :             if (req_queue_empty_) {
     279          10 :                 TaskScheduler *scheduler = TaskScheduler::GetInstance();
     280          10 :                 RequestHandler *task = new RequestHandler(this);
     281          10 :                 HttpSession::task_count_++;
     282          10 :                 scheduler->Enqueue(task);
     283             :             }
     284          10 :         }
     285          10 :         break;
     286          10 :     default:
     287          10 :         break;
     288             :     }
     289          20 : }
     290             : 
     291          20 : void HttpSession::OnRead(Buffer buffer) {
     292          20 :     const u_int8_t *data = BufferData(buffer);
     293          20 :     size_t size = BufferSize(buffer);
     294          20 :     std::stringstream msg;
     295             : 
     296          20 :     msg << "HttpSession::Read " << size << " bytes";
     297             :     HTTP_SYS_LOG("HttpSession", SandeshLevel::UT_DEBUG, msg.str());
     298             : 
     299             :     // No need to proceed if size is 0 which can be the case with ssl
     300          20 :     if (size == 0 || context_str_.size() == 0) {
     301          10 :         ReleaseBuffer(buffer);
     302          10 :         return;
     303             :     }
     304          10 :     if (request_builder_.get() == NULL) {
     305          10 :         request_builder_.reset(new RequestBuilder());
     306             :     }
     307          10 :     request_builder_->Parse(data, size);
     308          10 :     if (request_builder_->complete()) {
     309          10 :         HttpRequest *request = request_builder_->GetRequest();
     310             :         HTTP_SYS_LOG("HttpSession", SandeshLevel::UT_DEBUG, request->ToString());
     311          10 :         request_queue_.push(request);
     312             :         // Enqueue new RequestHandler task if needed
     313          10 :         if (req_queue_empty_) {
     314          10 :             TaskScheduler *scheduler = TaskScheduler::GetInstance();
     315          10 :             RequestHandler *task = new RequestHandler(this);
     316          10 :             HttpSession::task_count_++;
     317          10 :             scheduler->Enqueue(task);
     318             :         }
     319          10 :         request_builder_->Clear();
     320             :     }
     321             :     // TODO: error handling
     322          10 :     ReleaseBuffer(buffer);
     323          20 : }

Generated by: LCOV version 1.14