OpenSDN source code
http_session.cc
Go to the documentation of this file.
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 
24 std::mutex HttpSession::map_mutex_;
25 std::atomic<long> HttpSession::task_count_;
26 
27 // Input processing context
29 public:
30  RequestBuilder() : complete_(false) {
31  parser_.data = this;
32  Clear();
33  }
34 
35  void Clear() {
36  http_parser_init(&parser_, HTTP_REQUEST);
37  request_.reset(new HttpRequest());
38  complete_ = false;
39  header_key_.clear();
40  header_value_.clear();
41  }
42  size_t Parse(const u_int8_t *data, size_t datalen) {
43  size_t nparsed =
44  http_parser_execute(&parser_, &settings_,
45  reinterpret_cast<const char *>(data), datalen);
46  return nparsed;
47  }
48 
49  bool complete() const { return complete_; }
50 
51  // Transfers ownership
53  HttpRequest *request = request_.get();
54  request_.release();
55  return request;
56  }
57 private:
58  static int OnMessageBegin(struct http_parser *parser) {
59  return 0;
60  }
61 
62  static int OnHeadersComplete(struct http_parser *parser) {
63  RequestBuilder *builder =
64  reinterpret_cast<RequestBuilder *>(parser->data);
65  builder->request_->SetMethod(static_cast<http_method>(parser->method));
66  builder->request_->SetUrl(&builder->tmp_url_);
67  builder->PushHeader();
68  return 0;
69  }
70 
71  static int OnMessageComplete(struct http_parser *parser) {
72  RequestBuilder *builder =
73  reinterpret_cast<RequestBuilder *>(parser->data);
74  builder->complete_ = true;
75  return 0;
76  }
77 
78  static int OnUrl(struct http_parser *parser,
79  const char *loc, size_t length) {
80  RequestBuilder *builder =
81  reinterpret_cast<RequestBuilder *>(parser->data);
82  builder->tmp_url_.append(loc, length);
83  return 0;
84  }
85 
86  static int OnStatusComplete(struct http_parser *parser) {
87  return 0;
88  }
89 
90  static int OnHeaderField(struct http_parser *parser,
91  const char *loc, size_t length) {
92  RequestBuilder *builder =
93  reinterpret_cast<RequestBuilder *>(parser->data);
94  if (!builder->header_value_.empty()) {
95  builder->PushHeader();
96  }
97  builder->header_key_.append(loc, length);
98  return 0;
99  }
100 
101  static int OnHeaderValue(struct http_parser *parser,
102  const char *loc, size_t length) {
103  RequestBuilder *builder =
104  reinterpret_cast<RequestBuilder *>(parser->data);
105  builder->header_value_.append(loc, length);
106  return 0;
107  }
108 
109  static int OnBody(struct http_parser *parser,
110  const char *loc, size_t length) {
111  RequestBuilder *builder =
112  reinterpret_cast<RequestBuilder *>(parser->data);
113  builder->request_->SetBody(loc, length);
114  return 0;
115  }
116 
117  void PushHeader() {
118  request_->PushHeader(header_key_, header_value_);
119  header_key_.clear();
120  header_value_.clear();
121  }
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_;
132 };
133 
134 struct http_parser_settings HttpSession::RequestBuilder::settings_ = {
142  OnMessageComplete
143 };
144 
145 #undef HTTP_SYS_LOG
146 #define HTTP_SYS_LOG(...)
147 
149 public:
150  explicit RequestHandler(HttpSession *session)
151  : Task(req_handler_task_id_, session->GetSessionInstance()),
152  session_(session) {
153  }
154 
156  HTTP_SYS_LOG("HttpSession", SandeshLevel::UT_INFO, "RequestHandler destructor");
157  }
158 
159  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  session->Send(reinterpret_cast<const u_int8_t *>(no_response),
170  sizeof(no_response), NULL);
171  delete request;
172  }
173 
174  // Retrieve a request item from the queue. Return true if the queue
175  // is empty.
176  bool FromQ(HttpRequest *& r) {
177  return !session_->request_queue_.try_pop(r);
178  }
179  virtual bool Run() {
180  HTTP_SYS_LOG("HttpSession", SandeshLevel::UT_INFO,
181  "RequestHandler execute");
182  HttpRequest *request = NULL;
183  bool del_session = false;
184  HttpServer *server = static_cast<HttpServer *>(session_->server());
185  while (true) {
186  request = NULL;
187  session_->req_queue_empty_ = FromQ(request);
188  if (!request) break;
189  HTTP_SYS_LOG("HttpSession", SandeshLevel::UT_INFO,
190  "URL is " + request->ToString());
191  if (request->ToString().empty()) {
192  if (session_->event_cb_ && !session_->event_cb_.empty()) {
193  session_->event_cb_(session_.get(), request->Event());
194  }
195  del_session = true;
196  session_->set_observer(NULL);
197  session_->Close();
198  delete request;
199  } else {
200  HttpServer::HttpHandlerFn handler =
201  server->GetHandler(request->UrlPath());
202  if (handler == NULL) {
203  handler = boost::bind(&RequestHandler::NotFound,
204  this, _1, _2);
205  }
206  handler(session_.get(), request);
207  }
208  }
209  if (del_session) {
210  HTTP_SYS_LOG("HttpSession", SandeshLevel::UT_INFO, "DeleteSession "
211  + session_->ToString());
212  session_->set_observer(NULL);
213  server->DeleteSession(session_.get());
214  }
216  return true;
217  }
218  std::string Description() const { return "HttpSession::RequestHandler"; }
219 private:
221 };
222 
224  bool async_ready)
225  : SslSession(server, socket, async_ready), event_cb_(NULL) {
226  if (req_handler_task_id_ == -1) {
228  req_handler_task_id_ = scheduler->GetTaskId("http::RequestHandlerTask");
229  }
230  req_queue_empty_ = true;
231  set_observer(boost::bind(&HttpSession::OnSessionEvent, this, _1, _2));
232 }
233 
235  HttpRequest *request = NULL;
236  while (request_queue_.try_pop(request)) {
237  delete request;
238  }
239 }
240 
242  std::scoped_lock lock(map_mutex_);
243  context_str_ = "http%" + ToString();
244  GetMap()->insert(std::make_pair(context_str_, HttpSessionPtr(this)));
245  HTTP_SYS_LOG("HttpSession", SandeshLevel::UT_INFO,
246  "Created Session " + context_str_);
247 }
248 
250  event_cb_ = cb;
251 }
252 
254  enum TcpSession::Event event) {
255  HttpSession *h_session = dynamic_cast<HttpSession *>(session);
256  assert(h_session);
257 
258  switch (event) {
259  case TcpSession::CLOSE:
260  {
261  {
262  std::scoped_lock lock(map_mutex_);
263  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  }
271  h_session->context_str_ = "";
272  HttpRequest *request = new HttpRequest();
273  string nourl = "";
274  request->SetUrl(&nourl);
275  request->SetEvent(event);
276  request_queue_.push(request);
277  // Enqueue new RequestHandler task if needed
278  if (req_queue_empty_) {
280  RequestHandler *task = new RequestHandler(this);
282  scheduler->Enqueue(task);
283  }
284  }
285  break;
286  default:
287  break;
288  }
289 }
290 
292  const u_int8_t *data = BufferData(buffer);
293  size_t size = BufferSize(buffer);
294  std::stringstream msg;
295 
296  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  if (size == 0 || context_str_.size() == 0) {
301  ReleaseBuffer(buffer);
302  return;
303  }
304  if (request_builder_.get() == NULL) {
305  request_builder_.reset(new RequestBuilder());
306  }
307  request_builder_->Parse(data, size);
308  if (request_builder_->complete()) {
309  HttpRequest *request = request_builder_->GetRequest();
310  HTTP_SYS_LOG("HttpSession", SandeshLevel::UT_DEBUG, request->ToString());
311  request_queue_.push(request);
312  // Enqueue new RequestHandler task if needed
313  if (req_queue_empty_) {
315  RequestHandler *task = new RequestHandler(this);
317  scheduler->Enqueue(task);
318  }
319  request_builder_->Clear();
320  }
321  // TODO: error handling
322  ReleaseBuffer(buffer);
323 }
TcpSession::Event Event() const
Definition: http_request.h:40
void SetEvent(enum TcpSession::Event event)
Definition: http_request.h:32
std::string UrlPath() const
Definition: http_request.cc:25
void SetUrl(std::string *url)
Definition: http_request.h:23
std::string ToString() const
Definition: http_request.cc:15
boost::function< void(HttpSession *session, const HttpRequest *)> HttpHandlerFn
Definition: http_server.h:38
HttpHandlerFn GetHandler(const std::string &path)
Definition: http_server.cc:106
static int OnBody(struct http_parser *parser, const char *loc, size_t length)
static int OnHeadersComplete(struct http_parser *parser)
Definition: http_session.cc:62
static int OnHeaderField(struct http_parser *parser, const char *loc, size_t length)
Definition: http_session.cc:90
size_t Parse(const u_int8_t *data, size_t datalen)
Definition: http_session.cc:42
static int OnMessageBegin(struct http_parser *parser)
Definition: http_session.cc:58
static int OnMessageComplete(struct http_parser *parser)
Definition: http_session.cc:71
static int OnUrl(struct http_parser *parser, const char *loc, size_t length)
Definition: http_session.cc:78
HttpRequest * GetRequest()
Definition: http_session.cc:52
std::unique_ptr< HttpRequest > request_
static int OnStatusComplete(struct http_parser *parser)
Definition: http_session.cc:86
static int OnHeaderValue(struct http_parser *parser, const char *loc, size_t length)
virtual bool Run()
Code to execute in a task. Returns true if task is completed. Return false to reschedule the task.
std::string Description() const
Gives a description of the task.
RequestHandler(HttpSession *session)
bool FromQ(HttpRequest *&r)
void NotFound(HttpSession *session, const HttpRequest *request)
boost::asio::ssl::stream< boost::asio::ip::tcp::socket > SslSocket
Definition: ssl_session.h:20
The TaskScheduler keeps track of what tasks are currently schedulable. When a task is enqueued it is ...
Definition: task.h:304
int GetTaskId(const std::string &name)
Definition: task.cc:861
void Enqueue(Task *task)
Enqueues a task for running. Starts task if all policy rules are met else puts task in waitq....
Definition: task.cc:642
static TaskScheduler * GetInstance()
Definition: task.cc:554
Task is a class to describe a computational task within OpenSDN control plane applications....
Definition: task.h:79
virtual void DeleteSession(TcpSession *session)
Definition: tcp_server.cc:199
static const uint8_t * BufferData(const Buffer &buffer)
Definition: tcp_session.h:109
virtual std::string ToString() const
Definition: tcp_session.h:79
static size_t BufferSize(const Buffer &buffer)
Definition: tcp_session.h:112
virtual void ReleaseBuffer(Buffer buffer)
Definition: tcp_session.cc:144
void set_observer(EventObserver observer)
Definition: tcp_session.cc:219
boost::asio::const_buffer Buffer
Definition: tcp_session.h:60
virtual bool Send(const uint8_t *data, size_t size, size_t *sent)
Definition: tcp_session.cc:429
#define HTTP_SYS_LOG(...)
static std::mutex map_mutex_
Definition: http_session.h:92
boost::intrusive_ptr< HttpSession > HttpSessionPtr
Definition: http_session.h:59
static map_type * context_map_
Definition: http_session.h:91
void OnSessionEvent(TcpSession *session, enum TcpSession::Event event)
SessionEventCb event_cb_
Definition: http_session.h:88
std::map< std::string, HttpSessionPtr > map_type
Definition: http_session.h:61
boost::function< void(HttpSession *session, enum TcpSession::Event event)> SessionEventCb
Definition: http_session.h:23
static std::atomic< long > task_count_
Definition: http_session.h:93
void AcceptSession()
void RegisterEventCb(SessionEventCb cb)
boost::scoped_ptr< RequestBuilder > request_builder_
Definition: http_session.h:83
tbb::concurrent_queue< HttpRequest * > request_queue_
Definition: http_session.h:84
virtual void OnRead(Buffer buffer)
static int req_handler_task_id_
Definition: http_session.h:90
HttpSession(HttpServer *server, SslSocket *sock, bool async_ready=true)
std::string context_str_
Definition: http_session.h:86
std::atomic< bool > req_queue_empty_
Definition: http_session.h:85
static map_type * GetMap()
Definition: http_session.h:66
virtual ~HttpSession()
struct task_ task