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