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

Generated by: LCOV version 1.14