Line data Source code
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 :
59 : using contrail::sandesh::transport::TMemoryBuffer;
60 :
61 : SandeshHttp::map_type* SandeshHttp::map_ = NULL;
62 : HttpServer* SandeshHttp::hServ_ = NULL;
63 : string SandeshHttp::index_str_;
64 : SandeshHttp::HtmlInfo *SandeshHttp::index_hti_ = NULL;
65 :
66 :
67 : #define SESSION_SEND(buf) \
68 : HttpSession::SendSession(context,reinterpret_cast<const u_int8_t *>(buf), strlen(buf), NULL)
69 :
70 : enum HttpXMLState {
71 : HXMLInvalid,
72 : HXMLNew,
73 : HXMLIncomplete,
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 10 : 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 10 : int loc = strcspn(reinterpret_cast<const char *>(buf)," ");
104 10 : strncpy(resp_name, reinterpret_cast<const char *>(buf + 1), loc - 1);
105 10 : resp_name[loc - 1] = 0;
106 :
107 : static std::mutex hmutex;
108 : static int seq = 0;
109 10 : std::scoped_lock lock(hmutex);
110 10 : std::string client_ctx;
111 10 : seq++;
112 :
113 10 : client_ctx = HttpSession::get_client_context(context);
114 : HttpXMLState state;
115 :
116 : // Calculate current state;
117 10 : if (!client_ctx.empty()) {
118 0 : state = HXMLIncomplete;
119 : } else {
120 10 : state = HXMLNew;
121 10 : client_ctx = resp_name;
122 :
123 : // If the session is gone, we can stop processing.
124 10 : if (!HttpSession::set_client_context(context, client_ctx))
125 0 : return;
126 : }
127 :
128 10 : if ((HXMLNew == state) && (!more)) {
129 : // This is the first and last chunk of this response
130 :
131 10 : SESSION_SEND(xsl_response);
132 :
133 10 : size_t total_len = len;
134 10 : sprintf(buffer_str,
135 : "<?xml-stylesheet type=\"text/xsl\" href=\"/universal_parse.xsl\"?>"
136 : );
137 10 : sprintf(length_str,
138 10 : "Content-Length: %zu\r\n\r\n", total_len + strlen(buffer_str));
139 10 : SESSION_SEND(length_str);
140 10 : SESSION_SEND(buffer_str);
141 :
142 10 : HttpSession::SendSession(context, buf, len, NULL);
143 :
144 10 : } else if ((HXMLNew == state) && more) {
145 : // This is the first chunk of this response
146 0 : SESSION_SEND(chunk_response);
147 :
148 : // Calculation for Header
149 0 : sprintf(buffer_str,
150 : "<?xml-stylesheet type=\"text/xsl\" href=\"/universal_parse.xsl\"?>"
151 : );
152 0 : int hdr_len = strlen(buffer_str);
153 0 : sprintf(buffer_str,
154 : "<__%s_list type=\"slist\">\r\n", client_ctx.c_str());
155 0 : hdr_len += (strlen(buffer_str) - 2);
156 :
157 0 : sprintf(length_str, "%x\r\n", hdr_len);
158 0 : SESSION_SEND(length_str);
159 :
160 0 : sprintf(buffer_str,
161 : "<?xml-stylesheet type=\"text/xsl\" href=\"/universal_parse.xsl\"?>"
162 : );
163 0 : SESSION_SEND(buffer_str);
164 :
165 0 : sprintf(buffer_str,
166 : "<__%s_list type=\"slist\">\r\n", client_ctx.c_str());
167 0 : SESSION_SEND(buffer_str);
168 :
169 :
170 : // Calculation for Data
171 0 : sprintf(length_str, "%x\r\n", len);
172 0 : SESSION_SEND(length_str);
173 :
174 0 : HttpSession::SendSession(context, buf, len, NULL);
175 :
176 0 : sprintf(buffer_str,"\r\n");
177 0 : SESSION_SEND(buffer_str);
178 :
179 0 : } else if ((HXMLIncomplete == state) && !more) {
180 : // This is the last chunk of this response
181 :
182 : // Calculation for Data
183 0 : sprintf(length_str, "%x\r\n", len);
184 0 : SESSION_SEND(length_str);
185 :
186 0 : HttpSession::SendSession(context, buf, len, NULL);
187 :
188 0 : sprintf(buffer_str,"\r\n");
189 0 : SESSION_SEND(buffer_str);
190 :
191 : //Calculation for Footer
192 0 : sprintf(buffer_str,"</__%s_list>\r\n", client_ctx.c_str());
193 0 : sprintf(length_str, "%zx\r\n", strlen(buffer_str) - 2);
194 0 : SESSION_SEND(length_str);
195 0 : SESSION_SEND(buffer_str);
196 :
197 0 : sprintf(length_str, "0\r\n\r\n");
198 0 : SESSION_SEND(length_str);
199 0 : } else if ((HXMLIncomplete == state) && more) {
200 : // This is a middle chunk of the response
201 :
202 : // Calculation for Data
203 0 : sprintf(length_str, "%x\r\n", len);
204 0 : SESSION_SEND(length_str);
205 :
206 0 : HttpSession::SendSession(context, buf, len, NULL);
207 :
208 0 : sprintf(buffer_str,"\r\n");
209 0 : SESSION_SEND(buffer_str);
210 : }
211 : // Update context for other users
212 10 : if (!more) {
213 10 : client_ctx = resp_name;
214 10 : HttpSession::set_client_context(context,"");
215 : }
216 :
217 10 : }
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
228 0 : HttpSandeshFileCallback(SandeshHttp::HtmlInfo *hti,
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 0 : ((request->UrlPath().find(".js")!=string::npos) ?
254 0 : json_response : ((request->UrlPath().find(".xsl")!=string::npos) ?
255 0 : xsl_response : ((request->UrlPath().find(".css")!=string::npos) ?
256 0 : css_response : ((request->UrlPath().find(".xml")!=string::npos) ?
257 0 : xml_response : html_response))));
258 :
259 0 : sprintf(length_str,
260 : "Content-Length: %d\r\n\r\n", hti->html_len());
261 0 : session->Send(reinterpret_cast<const u_int8_t *>(response),
262 : strlen(response), NULL);
263 0 : session->Send(reinterpret_cast<const u_int8_t *>(length_str),
264 : strlen(length_str), NULL);
265 0 : session->Send(hti->html_str(), hti->html_len(), NULL);
266 0 : delete request;
267 0 : }
268 :
269 : SandeshHttp::RequestCallbackFn httpreqcb;
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
280 10 : HttpSandeshRequestCallback(HttpServer *hs,
281 : HttpSession *session,
282 : const HttpRequest *request) {
283 :
284 10 : string snh_name = request->UrlPath().substr(5);
285 10 : Sandesh *sandesh = SandeshBaseFactory::CreateInstance(snh_name);
286 10 : if (sandesh == NULL) {
287 0 : SANDESH_LOG(DEBUG, __func__ << " Unknown sandesh:" <<
288 : snh_name << std::endl);
289 0 : return;
290 : }
291 10 : SandeshRequest *rsnh = dynamic_cast<SandeshRequest *>(sandesh);
292 10 : assert(rsnh);
293 10 : rsnh->RequestFromHttp(session->get_context(), request->UrlQuery());
294 10 : httpreqcb(rsnh);
295 10 : delete request;
296 10 : }
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 10 : SandeshHttp::Response(Sandesh *snh, std::string context) {
311 10 : bool more = false;
312 :
313 10 : SandeshResponse * rsnh = dynamic_cast<SandeshResponse *>(snh);
314 10 : if (rsnh) {
315 10 : more = rsnh->get_more();
316 : } else {
317 0 : SandeshTrace * tsnh = dynamic_cast<SandeshTrace *>(snh);
318 0 : if (tsnh) {
319 0 : more = tsnh->get_more();
320 : } else {
321 0 : SandeshUVE * usnh = dynamic_cast<SandeshUVE *>(snh);
322 0 : assert(usnh);
323 0 : more = usnh->get_more();
324 : }
325 : }
326 10 : std::stringstream ss;
327 : uint8_t *buffer;
328 10 : uint32_t xfer = 0, offset;
329 :
330 : boost::shared_ptr<TMemoryBuffer> btrans =
331 : boost::shared_ptr<TMemoryBuffer>(
332 10 : new TMemoryBuffer(kEncodeBufferSize));
333 : boost::shared_ptr<TXMLProtocol> prot =
334 : boost::shared_ptr<TXMLProtocol>(
335 10 : new TXMLProtocol(btrans));
336 : // Write the sandesh
337 10 : xfer += snh->Write(prot);
338 : // Get the buffer
339 10 : btrans->getBuffer(&buffer, &offset);
340 10 : HttpSendXML(context, buffer, offset, snh->ModuleName().c_str(), more);
341 10 : buffer[offset] = 0;
342 10 : snh->Release();
343 10 : }
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 87 : SandeshHttp::Init(EventManager *evm, const string module,
356 : short port, RequestCallbackFn reqcb, int *hport, const SandeshConfig &config) {
357 87 : if (hServ_) {
358 0 : *hport = hServ_->GetPort();
359 87 : return true;
360 : }
361 87 : ostringstream index_ss;
362 :
363 174 : SandeshTraceBufferPtr httpbuf(SandeshTraceBufferCreate("httpbuf", 100));
364 87 : SANDESH_TRACE_TEXT_TRACE(httpbuf, "<Initializing httpbuf");
365 87 : SANDESH_TRACE_TEXT_TRACE(httpbuf, "Size 100");
366 :
367 87 : uint8_t dscp = 0;
368 87 : SandeshClient *client = Sandesh::client();
369 87 : if (client) {
370 0 : dscp = client->dscp_value();
371 : }
372 87 : struct SslConfig sslConfig;
373 87 : sslConfig.ssl_enabled = config.introspect_ssl_enable;
374 87 : sslConfig.ca_cert = config.ca_cert;
375 87 : sslConfig.certfile = config.server_certfile;
376 87 : sslConfig.keyfile = config.server_keyfile;
377 87 : sslConfig.ssl_insecure = config.introspect_ssl_insecure;
378 87 : hServ_ = new HttpServer(evm, sslConfig, dscp);
379 87 : httpreqcb = reqcb;
380 : index_ss << "<!DOCTYPE html PUBLIC \"-//W3C//DTD XHTML 1.0 Strict//EN\"" <<
381 87 : " \"http://www.w3.org/TR/xhtml1/DTD/xhtml1-strict.dtd\">" << endl;
382 87 : 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 87 : "<title>" << module << "</title></head><body>" << endl;
386 87 : index_ss << "<h1>Modules for " << module << "</h1>" << endl;
387 :
388 1917 : for (map_type::iterator it=map_->begin(); it!=map_->end(); it++) {
389 1830 : std::string regString = "/" + it->first;
390 1830 : hServ_->RegisterHandler(regString.c_str(),
391 1830 : boost::bind(&HttpSandeshFileCallback, &it->second, _1, _2));
392 1830 : if (it->first.find(".xml")!=string::npos) {
393 1224 : index_ss << "<a href=\"" << it->first << "\">" <<
394 612 : it->first << "</a><br/>" << endl;
395 : }
396 1830 : }
397 87 : index_ss << "</body></html>" << endl;
398 :
399 87 : index_str_ = index_ss.str();
400 87 : const char * index_str = index_str_.c_str();
401 87 : SANDESH_LOG(DEBUG, "HTTP Introspect Init");
402 :
403 87 : index_hti_ = new HtmlInfo(reinterpret_cast<const unsigned char *>(
404 87 : index_str),strlen(index_str));
405 87 : hServ_->RegisterHandler("/index.html",
406 : boost::bind(&HttpSandeshFileCallback, index_hti_, _1, _2));
407 87 : hServ_->RegisterHandler("/",
408 : boost::bind(&HttpSandeshFileCallback, index_hti_, _1, _2));
409 :
410 87 : SandeshBaseFactory::map_type::const_iterator it= SandeshBaseFactory::Begin();
411 6104 : for (; it!=SandeshBaseFactory::End(); it++) {
412 6017 : std::string regString = "/Snh_" + (*it).first;
413 6017 : hServ_->RegisterHandler(regString.c_str(),
414 : boost::bind(&HttpSandeshRequestCallback, hServ_, _1, _2));
415 6017 : }
416 :
417 87 : boost::system::error_code ec;
418 : bool success;
419 : IpAddress http_ip =
420 87 : boost::asio::ip::address::from_string(config.http_server_ip, ec);
421 87 : if (!ec) {
422 44 : success = hServ_->Initialize(port, http_ip);
423 : } else {
424 43 : success = hServ_->Initialize(port);
425 : }
426 87 : hServ_->SetSocketOptions(config);
427 87 : if (success) {
428 87 : int lport(hServ_->GetPort());
429 87 : SANDESH_LOG(DEBUG, "Sandesh Http Server Port: " << lport);
430 87 : *hport = lport;
431 87 : return true;
432 : } else {
433 0 : SANDESH_LOG(ERROR, "Failed to initialize Sandesh Http Server Port: "
434 : << port);
435 0 : return false;
436 : }
437 : if (dscp) {
438 : hServ_->SetListenSocketDscp(dscp);
439 : }
440 261 : }
441 :
442 : // Function to shut down HTTP Server
443 : void
444 87 : SandeshHttp::Uninit(void) {
445 87 : if (!hServ_) return;
446 :
447 87 : index_str_.clear();
448 87 : delete index_hti_;
449 :
450 87 : hServ_->Shutdown();
451 87 : hServ_->ClearSessions();
452 87 : 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 : //
459 87 : TcpServerManager::DeleteServer(hServ_);
460 87 : hServ_ = NULL;
461 : }
462 :
463 : void
464 0 : SandeshHttp::UpdateDscp(uint8_t dscp) {
465 0 : if (!hServ_) return;
466 0 : hServ_->UpdateDscp(dscp);
467 0 : hServ_->UpdateSessionsDscp(dscp);
468 : }
|