1 /** @file
2 
3   A brief file description
4 
5   @section license License
6 
7   Licensed to the Apache Software Foundation (ASF) under one
8   or more contributor license agreements.  See the NOTICE file
9   distributed with this work for additional information
10   regarding copyright ownership.  The ASF licenses this file
11   to you under the Apache License, Version 2.0 (the
12   "License"); you may not use this file except in compliance
13   with the License.  You may obtain a copy of the License at
14 
15       http://www.apache.org/licenses/LICENSE-2.0
16 
17   Unless required by applicable law or agreed to in writing, software
18   distributed under the License is distributed on an "AS IS" BASIS,
19   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20   See the License for the specific language governing permissions and
21   limitations under the License.
22  */
23 
24 /****************************************************************************
25 
26    HttpPages.cc
27 
28    Description:
29        Data structures and stat page generators for http info
30 
31 
32  ****************************************************************************/
33 #include "HttpPages.h"
34 #include "HttpSM.h"
35 #include "HttpDebugNames.h"
36 
37 HttpSMListBucket HttpSMList[HTTP_LIST_BUCKETS];
38 
HttpPagesHandler(Continuation * cont,HTTPHdr * header)39 HttpPagesHandler::HttpPagesHandler(Continuation *cont, HTTPHdr *header)
40   : BaseStatPagesHandler(new_ProxyMutex()), request(nullptr), list_bucket(0), state(HP_INIT), sm_id(0)
41 {
42   action = cont;
43 
44   URL *url;
45   int length;
46 
47   url     = header->url_get();
48   request = const_cast<char *>(url->path_get(&length));
49   request = arena.str_store(request, length);
50 
51   if (strncmp(request, "sm_details", sizeof("sm_details")) == 0) {
52     arena.str_free(request);
53     request = const_cast<char *>(url->query_get(&length));
54     request = arena.str_store(request, length);
55     SET_HANDLER(&HttpPagesHandler::handle_smdetails);
56 
57   } else {
58     SET_HANDLER(&HttpPagesHandler::handle_smlist);
59   }
60 }
61 
~HttpPagesHandler()62 HttpPagesHandler::~HttpPagesHandler() {}
63 
64 int64_t
extract_id(const char * query)65 HttpPagesHandler::extract_id(const char *query)
66 {
67   char *p;
68   int64_t id;
69 
70   p = const_cast<char *>(strstr(query, "id="));
71   if (!p) {
72     return -1;
73   }
74   p += sizeof("id=") - 1;
75 
76   id = ink_atoi64(p);
77 
78   // Check to see if we found the id
79   if (id == 0) {
80     if (*p == '0' && *(p + 1) == '\0') {
81       return 0;
82     } else {
83       return -1;
84     }
85   } else {
86     return id;
87   }
88 }
89 
90 void
dump_hdr(HTTPHdr * hdr,const char * desc)91 HttpPagesHandler::dump_hdr(HTTPHdr *hdr, const char *desc)
92 {
93   if (hdr->valid()) {
94     resp_add("<h4> %s </h4>\n<pre>\n", desc);
95     char b[4096];
96     int used, tmp, offset;
97     int done;
98     offset = 0;
99     do {
100       used = 0;
101       tmp  = offset;
102       done = hdr->print(b, 4095, &used, &tmp);
103       offset += used;
104       b[used] = '\0';
105       resp_add(b);
106     } while (!done);
107     resp_add("</pre>\n");
108   }
109 }
110 
111 void
dump_tunnel_info(HttpSM * sm)112 HttpPagesHandler::dump_tunnel_info(HttpSM *sm)
113 {
114   HttpTunnel *t = sm->get_tunnel();
115 
116   resp_add("<h4> Tunneling Info </h4>");
117 
118   resp_add("<p> Producers </p>");
119   resp_begin_table(1, 4, 60);
120   for (auto &producer : t->producers) {
121     if (producer.vc != nullptr) {
122       resp_begin_row();
123 
124       // Col 1 - name
125       resp_begin_column();
126       resp_add(producer.name);
127       resp_end_column();
128 
129       // Col 2 - alive
130       resp_begin_column();
131       resp_add("%d", producer.alive);
132       resp_end_column();
133 
134       // Col 3 - ndone
135       resp_begin_column();
136       if (producer.alive && producer.read_vio) {
137         resp_add("%" PRId64, producer.read_vio->ndone);
138       } else {
139         resp_add("%" PRId64, producer.bytes_read);
140       }
141       resp_end_column();
142 
143       // Col 4 - nbytes
144       resp_begin_column();
145       if (producer.alive && producer.read_vio) {
146         resp_add("%" PRId64, producer.read_vio->nbytes);
147       } else {
148         resp_add("-");
149       }
150       resp_end_column();
151 
152       resp_end_row();
153     }
154   }
155   resp_end_table();
156 
157   resp_add("<p> Consumers </p>");
158   resp_begin_table(1, 5, 60);
159   for (auto &consumer : t->consumers) {
160     if (consumer.vc != nullptr) {
161       resp_begin_row();
162 
163       // Col 1 - name
164       resp_begin_column();
165       resp_add(consumer.name);
166       resp_end_column();
167 
168       // Col 2 - alive
169       resp_begin_column();
170       resp_add("%d", consumer.alive);
171       resp_end_column();
172 
173       // Col 3 - ndone
174       resp_begin_column();
175       if (consumer.alive && consumer.write_vio) {
176         resp_add("%" PRId64, consumer.write_vio->ndone);
177       } else {
178         resp_add("%" PRId64, consumer.bytes_written);
179       }
180       resp_end_column();
181 
182       // Col 4 - nbytes
183       resp_begin_column();
184       if (consumer.alive && consumer.write_vio) {
185         resp_add("%" PRId64, consumer.write_vio->nbytes);
186       } else {
187         resp_add("-");
188       }
189       resp_end_column();
190 
191       // Col 5 - read avail
192       resp_begin_column();
193       if (consumer.alive && consumer.buffer_reader) {
194         resp_add("%" PRId64, consumer.buffer_reader->read_avail());
195       } else {
196         resp_add("-");
197       }
198       resp_end_column();
199 
200       resp_end_row();
201     }
202   }
203   resp_end_table();
204 }
205 
206 void
dump_history(HttpSM * sm)207 HttpPagesHandler::dump_history(HttpSM *sm)
208 {
209   resp_add("<h4> History</h4>");
210   resp_begin_table(1, 3, 60);
211 
212   // Figure out how big the history is and look
213   //  for wrap around
214   for (unsigned int i = 0; i < sm->history.size(); i++) {
215     char buf[256];
216     resp_begin_row();
217 
218     resp_begin_column();
219     resp_add("%s", sm->history[i].location.str(buf, sizeof(buf)));
220     resp_end_column();
221 
222     resp_begin_column();
223     resp_add("%u", static_cast<unsigned int>(sm->history[i].event));
224     resp_end_column();
225 
226     resp_begin_column();
227     resp_add("%d", static_cast<int>(sm->history[i].reentrancy));
228     resp_end_column();
229 
230     resp_end_row();
231   }
232 
233   resp_end_table();
234 }
235 
236 int
dump_sm(HttpSM * sm)237 HttpPagesHandler::dump_sm(HttpSM *sm)
238 {
239   // Dump the current state
240   const char *sm_state = HttpDebugNames::get_action_name(sm->t_state.next_action);
241 
242   resp_begin_item();
243   resp_add("Current State: %s", sm_state);
244   resp_end_item();
245 
246   dump_hdr(&sm->t_state.hdr_info.client_request, "Client Request");
247   dump_hdr(&sm->t_state.hdr_info.server_request, "Server Request");
248   dump_hdr(&sm->t_state.hdr_info.server_response, "Server Response");
249   dump_hdr(&sm->t_state.hdr_info.client_response, "Client Response");
250 
251   dump_tunnel_info(sm);
252   dump_history(sm);
253 
254   return EVENT_DONE;
255 }
256 
257 int
handle_smdetails(int event,void *)258 HttpPagesHandler::handle_smdetails(int event, void * /* data ATS_UNUSED */)
259 {
260   EThread *ethread = this_ethread();
261   HttpSM *sm       = nullptr;
262 
263   switch (event) {
264   case EVENT_NONE:
265   case EVENT_INTERVAL:
266   case EVENT_IMMEDIATE:
267     break;
268   default:
269     ink_assert(0);
270     break;
271   }
272 
273   // Do initial setup if necessary
274   if (state == HP_INIT) {
275     state = HP_RUN;
276 
277     // Get our SM id
278     sm_id = extract_id(request);
279 
280     if (sm_id < 0) {
281       resp_begin("Http Pages Error");
282       resp_add("<b>Unable to extract id</b>\n");
283       resp_end();
284       return handle_callback(EVENT_NONE, nullptr);
285     }
286 
287     resp_begin("Http:SM Details");
288     resp_begin_item();
289     resp_add("Details for SM id  %" PRId64 "", sm_id);
290     resp_end_item();
291   }
292 
293   for (; list_bucket < HTTP_LIST_BUCKETS; list_bucket++) {
294     MUTEX_TRY_LOCK(lock, HttpSMList[list_bucket].mutex, ethread);
295 
296     if (!lock.is_locked()) {
297       eventProcessor.schedule_in(this, HTTP_LIST_RETRY, ET_CALL);
298       return EVENT_DONE;
299     }
300 
301     sm = HttpSMList[list_bucket].sm_list.head;
302 
303     while (sm != nullptr) {
304       if (sm->sm_id == sm_id) {
305         // In this block we try to get the lock of the
306         //   state machine
307         {
308           MUTEX_TRY_LOCK(sm_lock, sm->mutex, ethread);
309           if (sm_lock.is_locked()) {
310             dump_sm(sm);
311             resp_end();
312             return handle_callback(EVENT_NONE, nullptr);
313           } else {
314             // We missed the lock so retry
315             eventProcessor.schedule_in(this, HTTP_LIST_RETRY, ET_CALL);
316             return EVENT_DONE;
317           }
318         }
319       }
320 
321       sm = sm->debug_link.next;
322     }
323   }
324 
325   // If we got here, we did not find our state machine
326   resp_add("<h2>Id %" PRId64 " not found</h2>", sm_id);
327   resp_end();
328   return handle_callback(EVENT_NONE, nullptr);
329 }
330 
331 int
handle_smlist(int event,void *)332 HttpPagesHandler::handle_smlist(int event, void * /* data ATS_UNUSED */)
333 {
334   EThread *ethread = this_ethread();
335   HttpSM *sm;
336 
337   switch (event) {
338   case EVENT_NONE:
339   case EVENT_INTERVAL:
340   case EVENT_IMMEDIATE:
341     break;
342   default:
343     ink_assert(0);
344     break;
345   }
346 
347   if (state == HP_INIT) {
348     resp_begin("Http:SM List");
349     state = HP_RUN;
350   }
351 
352   for (; list_bucket < HTTP_LIST_BUCKETS; list_bucket++) {
353     MUTEX_TRY_LOCK(lock, HttpSMList[list_bucket].mutex, ethread);
354 
355     if (!lock.is_locked()) {
356       eventProcessor.schedule_in(this, HTTP_LIST_RETRY, ET_CALL);
357       return EVENT_DONE;
358     }
359 
360     sm = HttpSMList[list_bucket].sm_list.head;
361 
362     while (sm != nullptr) {
363       char *url          = nullptr;
364       const char *method = nullptr;
365       int method_len;
366       const char *sm_state = nullptr;
367 
368       // In this block we try to get the lock of the
369       //   state machine
370       {
371         MUTEX_TRY_LOCK(sm_lock, sm->mutex, ethread);
372         if (sm_lock.is_locked()) {
373           if (sm->t_state.hdr_info.client_request.valid()) {
374             sm_state = HttpDebugNames::get_action_name(sm->t_state.next_action);
375 
376             method = sm->t_state.hdr_info.client_request.method_get(&method_len);
377             method = arena.str_store(method, method_len);
378             URL *u = sm->t_state.hdr_info.client_request.url_get();
379             if (u->valid()) {
380               url = u->string_get(&arena);
381             }
382           }
383 
384           if (url == nullptr) {
385             url      = arena.str_store("-", 1);
386             sm_state = "READ_REQUEST";
387           }
388         } else {
389           url      = arena.str_store("-", 1);
390           sm_state = "LOCKED";
391         }
392       }
393 
394       resp_begin_item();
395       resp_add("id: <a href=\"./sm_details?id=%" PRId64 "\"> %" PRId64 " </a> | %s %s | %s\n", sm->sm_id, sm->sm_id,
396                method ? method : "", url, sm_state ? sm_state : "");
397       resp_end_item();
398       arena.str_free(url);
399 
400       sm = sm->debug_link.next;
401     }
402   }
403 
404   resp_end();
405   handle_callback(EVENT_NONE, nullptr);
406 
407   return EVENT_DONE;
408 }
409 
410 int
handle_callback(int,void *)411 HttpPagesHandler::handle_callback(int /* event ATS_UNUSED */, void * /* edata ATS_UNUSED */)
412 {
413   MUTEX_TRY_LOCK(trylock, action.mutex, this_ethread());
414   if (!trylock.is_locked()) {
415     SET_HANDLER(&HttpPagesHandler::handle_callback);
416     eventProcessor.schedule_in(this, HTTP_LIST_RETRY, ET_CALL);
417     return EVENT_DONE;
418   }
419 
420   if (!action.cancelled) {
421     if (response) {
422       StatPageData data;
423 
424       data.data   = response;
425       data.type   = ats_strdup("text/html");
426       data.length = response_length;
427       response    = nullptr;
428 
429       action.continuation->handleEvent(STAT_PAGE_SUCCESS, &data);
430     } else {
431       action.continuation->handleEvent(STAT_PAGE_FAILURE, nullptr);
432     }
433   }
434 
435   delete this;
436   return EVENT_DONE;
437 }
438 
439 static Action *
http_pages_callback(Continuation * cont,HTTPHdr * header)440 http_pages_callback(Continuation *cont, HTTPHdr *header)
441 {
442   HttpPagesHandler *handler;
443 
444   handler = new HttpPagesHandler(cont, header);
445   eventProcessor.schedule_imm(handler, ET_CALL);
446 
447   return &handler->action;
448 }
449 
450 void
http_pages_init()451 http_pages_init()
452 {
453   statPagesManager.register_http("http", http_pages_callback);
454 
455   // Create the mutexes for http list protection
456   for (auto &i : HttpSMList) {
457     i.mutex = new_ProxyMutex();
458   }
459 }
460