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