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