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