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