2 * http_stat 2003 Jean-Michel FAYARD
4 * $Id: http_stat.c,v 1.6 2003/09/26 02:09:44 guy Exp $
6 * Ethereal - Network traffic analyzer
7 * By Gerald Combs <gerald@ethereal.com>
8 * Copyright 1998 Gerald Combs
10 * This program is free software; you can redistribute it and/or
11 * modify it under the terms of the GNU General Public License
12 * as published by the Free Software Foundation; either version 2
13 * of the License, or (at your option) any later version.
15 * This program is distributed in the hope that it will be useful,
16 * but WITHOUT ANY WARRANTY; without even the implied warranty of
17 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
18 * GNU General Public License for more details.
20 * You should have received a copy of the GNU General Public License
21 * along with this program; if not, write to the Free Software
22 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
30 #include "epan/packet_info.h"
31 #include "epan/epan.h"
33 #include "simple_dialog.h"
34 #include "dlg_utils.h"
36 #include "../register.h"
37 #include "../globals.h"
38 #include "compat_macros.h"
39 #include "../packet-http.h"
43 /* used to keep track of the statictics for an entire program interface */
44 typedef struct _http_stats_t {
47 GHashTable *hash_responses;
48 GHashTable *hash_requests;
49 guint32 packets; /* number of http packets, including HTTP continuation */
50 GtkWidget *packets_label;
52 GtkWidget *request_box; /* container for GET, ... */
54 GtkWidget *informational_table; /* Status code between 100 and 199 */
55 GtkWidget *success_table; /* 200 and 299 */
56 GtkWidget *redirection_table; /* 300 and 399 */
57 GtkWidget *client_error_table; /* 400 and 499 */
58 GtkWidget *server_errors_table; /* 500 and 599 */
61 /* used to keep track of the stats for a specific response code
62 * for example it can be { 3, 404, "Not Found" ,...}
63 * which means we captured 3 reply http/1.1 404 Not Found */
64 typedef struct _http_response_code_t {
65 guint32 packets; /* 3 */
66 guint response_code; /* 404 */
67 gchar *name; /* Not Found */
68 GtkWidget *widget; /* Label where we display it */
69 GtkWidget *table; /* Table in which we put it, e.g. client_error_box */
71 } http_response_code_t;
73 /* used to keep track of the stats for a specific request string */
74 typedef struct _http_request_methode_t {
75 gchar *response; /* eg. : GET */
79 } http_request_methode_t;
81 static GtkWidget *dlg=NULL;
82 static GtkWidget *filter_entry;
84 static const value_string vals_status_code[] = {
86 { 101, "Switching Protocols" },
87 { 199, "Informational - Others" },
92 { 203, "Non-authoritative Information"},
94 { 205, "Reset Content"},
95 { 206, "Partial Content"},
96 { 299, "Success - Others"}, /* used to keep track of others Success packets */
98 { 300, "Multiple Choices"},
99 { 301, "Moved Permanently"},
100 { 302, "Moved Temporarily"},
102 { 304, "Not Modified"},
104 { 399, "Redirection - Others"},
106 { 400, "Bad Request"},
107 { 401, "Unauthorized"},
108 { 402, "Payment Required"},
111 { 405, "Method Not Allowed"},
112 { 406, "Not Acceptable"},
113 { 407, "Proxy Authentication Required"},
114 { 408, "Request Time-out"},
117 { 411, "Length Required"},
118 { 412, "Precondition Failed"},
119 { 413, "Request Entity Too Large"},
120 { 414, "Request-URI Too Large"},
121 { 415, "Unsupported Media Type"},
122 { 499, "Client Error - Others"},
124 { 500, "Internal Server Error"},
125 { 501, "Not Implemented"},
126 { 502, "Bad Gateway"},
127 { 503, "Service Unavailable"},
128 { 504, "Gateway Time-out"},
129 { 505, "HTTP Version not supported"},
130 { 599, "Server Error - Others"},
135 /* insert some entries */
137 http_init_hash( httpstat_t *sp)
141 sp->hash_responses = g_hash_table_new( g_int_hash, g_int_equal);
143 for (i=0 ; vals_status_code[i].strptr ; i++ )
145 gint *key = g_malloc (sizeof(gint));
146 http_response_code_t *sc = g_malloc (sizeof(http_response_code_t));
147 *key = vals_status_code[i].value;
149 sc->response_code = *key;
150 sc->name=vals_status_code[i].strptr;
154 g_hash_table_insert( sc->sp->hash_responses, key, sc);
156 sp->hash_requests = g_hash_table_new( g_str_hash, g_str_equal);
159 http_draw_hash_requests( gchar *key _U_ , http_request_methode_t *data, gchar * string_buff)
161 if (data->packets==0)
163 sprintf(string_buff, " %-11s : %3d packets", data->response, data->packets);
164 if (data->widget==NULL){
165 data->widget=gtk_label_new( string_buff );
166 gtk_misc_set_alignment(GTK_MISC(data->widget), 0.0, 0.5);
167 gtk_box_pack_start(GTK_BOX(data->sp->request_box), data->widget,FALSE,FALSE, 0);
168 gtk_widget_show(data->widget);
170 gtk_label_set( GTK_LABEL(data->widget), string_buff);
176 http_draw_hash_responses( gint * key _U_ , http_response_code_t *data, gchar * string_buff)
179 g_warning("C'est quoi ce borderl key=%d\n", *key);
180 if (data->packets==0)
182 /*sprintf(string_buff, "%d packets %d:%s", data->packets, data->response_code, data->name); */
183 if (data->widget==NULL){ /* create an entry in the relevant box of the window */
186 guint i = data->response_code;
188 if ( (i<100)||(i>=600) )
191 data->table = data->sp->informational_table;
193 data->table = data->sp->success_table;
195 data->table = data->sp->redirection_table;
197 data->table = data->sp->client_error_table;
199 data->table = data->sp->server_errors_table;
200 x=GTK_TABLE( data->table)->nrows;
202 sprintf(string_buff, "HTTP %3d %s ", data->response_code, data->name );
203 tmp = gtk_label_new( string_buff );
205 gtk_table_attach_defaults( GTK_TABLE(data->table), tmp, 0,1, x, x+1);
206 gtk_label_set_justify( GTK_LABEL(tmp), GTK_JUSTIFY_LEFT);
207 gtk_widget_show( tmp );
209 sprintf(string_buff, "%9d", data->packets);
210 data->widget=gtk_label_new( string_buff);
212 gtk_table_attach_defaults( GTK_TABLE(data->table), data->widget, 1, 2,x,x+1);
213 gtk_label_set_justify( GTK_LABEL(data->widget), GTK_JUSTIFY_RIGHT);
214 gtk_widget_show( data->widget );
216 gtk_table_resize( GTK_TABLE(data->table), x+1, 4);
219 /* Just update the label string */
220 sprintf(string_buff, "%9d", data->packets );
221 gtk_label_set( GTK_LABEL(data->widget), string_buff);
228 http_free_hash( gpointer key, gpointer value, gpointer user_data _U_ )
234 http_reset_hash_responses(gchar *key _U_ , http_response_code_t *data, gpointer ptr _U_ )
239 http_reset_hash_requests(gchar *key _U_ , http_request_methode_t *data, gpointer ptr _U_ )
245 httpstat_reset(void *psp )
249 g_hash_table_foreach( sp->hash_responses, (GHFunc)http_reset_hash_responses, NULL);
250 g_hash_table_foreach( sp->hash_responses, (GHFunc)http_reset_hash_requests, NULL);
255 httpstat_packet(void *psp , packet_info *pinfo _U_, epan_dissect_t *edt _U_, void *pri)
257 http_info_value_t *value=pri;
258 httpstat_t *sp=(httpstat_t *) psp;
260 /* total number of packets, including HTTP continuation packets */
263 /* We are only interested in reply packets with a status code */
264 /* Request or reply packets ? */
265 if (value->response_code!=0) {
266 guint *key=g_malloc( sizeof(guint) );
267 http_response_code_t *sc;
269 *key=value->response_code;
270 sc = g_hash_table_lookup(
274 /* non standard status code ; we classify it as others
275 * in the relevant category (Informational,Success,Redirection,Client Error,Server Error)
277 int i = value->response_code;
278 if ((i<100) || (i>=600)) {
282 *key=199; /* Hopefully, this status code will never be used */
296 sc = g_hash_table_lookup(
304 else if (value->request_method){
305 http_request_methode_t *sc;
307 sc = g_hash_table_lookup(
309 value->request_method);
311 sc=g_malloc( sizeof(http_request_methode_t) );
312 sc->response=g_strdup( value->request_method );
316 g_hash_table_insert( sp->hash_requests, sc->response, sc);
320 /* g_free( value->request_method ); */
328 #define SUM_STR_MAX 1024
330 httpstat_draw(void *psp )
332 gchar string_buff[SUM_STR_MAX];
335 sprintf( string_buff, "HTTP stats (%d packets)", sp->packets);
336 gtk_label_set( GTK_LABEL(sp->packets_label), string_buff);
337 g_hash_table_foreach( sp->hash_responses, (GHFunc)http_draw_hash_responses, string_buff);
338 g_hash_table_foreach( sp->hash_requests, (GHFunc)http_draw_hash_requests, string_buff);
342 /* since the gtk2 implementation of tap is multithreaded we must protect
343 * remove_tap_listener() from modifying the list while draw_tap_listener()
344 * is running. the other protected block is in main.c
346 * there should not be any other critical regions in gtk2
348 void protect_thread_critical_region(void);
349 void unprotect_thread_critical_region(void);
351 win_destroy_cb(GtkWindow *win _U_, gpointer data)
353 httpstat_t *sp=(httpstat_t *)data;
355 protect_thread_critical_region();
356 remove_tap_listener(sp);
357 unprotect_thread_critical_region();
359 g_hash_table_foreach( sp->hash_responses, (GHFunc)http_free_hash, NULL);
360 g_hash_table_destroy( sp->hash_responses);
361 g_hash_table_foreach( sp->hash_requests, (GHFunc)http_free_hash, NULL);
362 g_hash_table_destroy( sp->hash_requests);
368 /* When called, this function will create a new instance of gtk_httpstat.
371 gtk_httpstat_init(char *optarg)
376 GString *error_string;
377 GtkWidget *main_vb, *separator,
378 *informational_fr, *success_fr, *redirection_fr,
379 *client_errors_fr, *server_errors_fr, *request_fr;
381 if (!strncmp (optarg, "http,stat,", 10)){
387 sp = g_malloc( sizeof(httpstat_t) );
389 sp->filter=g_malloc(strlen(filter)+1);
390 strcpy(sp->filter,filter);
391 title=g_strdup_printf("HTTP statistics with filter: %s", filter);
394 title=g_strdup("HTTP statistics");
397 /* top level window */
398 sp->win = gtk_window_new( GTK_WINDOW_TOPLEVEL);
399 gtk_window_set_title( GTK_WINDOW(sp->win), title );
401 SIGNAL_CONNECT( sp->win, "destroy", win_destroy_cb, sp);
403 /* container for each group of status code */
404 main_vb = gtk_vbox_new(FALSE, 10);
405 gtk_container_border_width(GTK_CONTAINER(main_vb), 10);
406 gtk_container_add(GTK_CONTAINER(sp->win), main_vb);
407 gtk_widget_show(main_vb);
409 /* number of packets */
411 sp->packets_label = gtk_label_new("HTTP stats (0 HTTP packets)");
412 gtk_container_add( GTK_CONTAINER(main_vb), sp->packets_label);
413 gtk_widget_show(sp->packets_label);
415 /* Informational response frame */
416 informational_fr = gtk_frame_new("Informational HTTP 1xx");
417 gtk_container_add(GTK_CONTAINER(main_vb), informational_fr);
418 gtk_widget_show(informational_fr);
420 sp->informational_table = gtk_table_new( 0, 2, FALSE);
421 gtk_container_add(GTK_CONTAINER(informational_fr), sp->informational_table);
422 gtk_widget_show(sp->informational_table);
425 /* success response frame */
426 success_fr = gtk_frame_new ("Success HTTP 2xx");
427 gtk_container_add(GTK_CONTAINER(main_vb), success_fr);
428 gtk_widget_show(success_fr);
430 sp->success_table = gtk_table_new( 0, 2, FALSE);
431 gtk_container_add(GTK_CONTAINER(success_fr), sp->success_table);
432 gtk_widget_show(sp->success_table);
435 /* redirection response frame */
436 redirection_fr = gtk_frame_new ("Redirection HTTP 3xx");
437 gtk_container_add(GTK_CONTAINER(main_vb), redirection_fr);
438 gtk_widget_show(redirection_fr);
440 sp->redirection_table = gtk_table_new( 0, 2, FALSE);
441 gtk_container_add(GTK_CONTAINER(redirection_fr), sp->redirection_table);
442 gtk_widget_show(sp->redirection_table);
445 /* client_errors response frame */
446 client_errors_fr = gtk_frame_new("Client errors HTTP 4xx");
447 gtk_container_add(GTK_CONTAINER(main_vb), client_errors_fr);
448 gtk_widget_show(client_errors_fr);
450 sp->client_error_table = gtk_table_new( 0, 2, FALSE);
451 gtk_container_add(GTK_CONTAINER(client_errors_fr), sp->client_error_table);
452 gtk_widget_show(sp->client_error_table);
455 /* server_errors response frame */
456 server_errors_fr = gtk_frame_new("Server errors HTTP 5xx");
457 gtk_container_add(GTK_CONTAINER(main_vb), server_errors_fr);
458 gtk_widget_show(server_errors_fr);
460 sp->server_errors_table = gtk_table_new( 0, 2, FALSE);
461 gtk_container_add(GTK_CONTAINER(server_errors_fr), sp->server_errors_table);
462 gtk_widget_show(sp->server_errors_table);
465 separator = gtk_hseparator_new();
466 gtk_container_add(GTK_CONTAINER(main_vb), separator );
468 /* request response frame */
469 request_fr = gtk_frame_new("List of request methods");
470 gtk_container_add(GTK_CONTAINER(main_vb), request_fr);
471 gtk_container_border_width(GTK_CONTAINER(request_fr), 0);
472 gtk_widget_show(request_fr);
474 sp->request_box = gtk_vbox_new(FALSE, 10);
475 gtk_container_add(GTK_CONTAINER(request_fr), sp->request_box);
476 gtk_widget_show(sp->request_box);
478 error_string = register_tap_listener(
486 /* error, we failed to attach to the tap. clean up */
487 simple_dialog( ESD_TYPE_WARN, NULL, error_string->str );
490 g_string_free(error_string, TRUE);
495 gtk_widget_destroy( dlg );
498 gtk_widget_show_all( sp->win );
500 redissect_packets(&cfile);
506 httpstat_start_button_clicked(GtkWidget *item _U_, gpointer data _U_)
511 str = g_string_new("http,stat,");
512 filter=(char *)gtk_entry_get_text(GTK_ENTRY(filter_entry));
513 str = g_string_append(str, filter);
514 gtk_httpstat_init(str->str);
515 g_string_free(str, TRUE);
525 dlg_cancel_cb(GtkWidget *cancel_bt _U_, gpointer parent_w)
527 gtk_widget_destroy(GTK_WIDGET(parent_w));
531 gtk_httpstat_cb(GtkWidget *w _U_, gpointer d _U_)
534 GtkWidget *filter_box, *filter_label;
535 GtkWidget *bbox, *start_button, *cancel_button;
537 /* if the window is already open, bring it to front */
539 gdk_window_raise(dlg->window);
543 dlg=dlg_window_new("Ethereal: Compute HTTP statistics");
544 SIGNAL_CONNECT(dlg, "destroy", dlg_destroy_cb, NULL);
546 dlg_box=gtk_vbox_new(FALSE, 10);
547 gtk_container_border_width(GTK_CONTAINER(dlg_box), 10);
548 gtk_container_add(GTK_CONTAINER(dlg), dlg_box);
549 gtk_widget_show(dlg_box);
552 filter_box=gtk_hbox_new(FALSE, 3);
555 filter_label=gtk_label_new("Filter:");
556 gtk_box_pack_start(GTK_BOX(filter_box), filter_label, FALSE, FALSE, 0);
557 gtk_widget_show(filter_label);
560 filter_entry=gtk_entry_new();
561 gtk_widget_set_usize(filter_entry, 300, -2);
562 gtk_box_pack_start(GTK_BOX(filter_box), filter_entry, TRUE, TRUE, 0);
563 gtk_widget_show(filter_entry);
565 gtk_box_pack_start(GTK_BOX(dlg_box), filter_box, TRUE, TRUE, 0);
566 gtk_widget_show(filter_box);
569 bbox = gtk_hbutton_box_new();
570 gtk_button_box_set_layout(GTK_BUTTON_BOX(bbox), GTK_BUTTONBOX_DEFAULT_STYLE);
571 gtk_button_box_set_spacing(GTK_BUTTON_BOX(bbox), 5);
572 gtk_box_pack_start(GTK_BOX(dlg_box), bbox, FALSE, FALSE, 0);
573 gtk_widget_show(bbox);
575 /* the start button */
576 start_button=gtk_button_new_with_label("Create Stat");
577 SIGNAL_CONNECT_OBJECT(start_button, "clicked",
578 httpstat_start_button_clicked, NULL);
579 gtk_box_pack_start(GTK_BOX(bbox), start_button, TRUE, TRUE, 0);
580 GTK_WIDGET_SET_FLAGS(start_button, GTK_CAN_DEFAULT);
581 gtk_widget_grab_default(start_button);
582 gtk_widget_show(start_button);
584 #if GTK_MAJOR_VERSION < 2
585 cancel_button=gtk_button_new_with_label("Cancel");
587 cancel_button=gtk_button_new_from_stock(GTK_STOCK_CANCEL);
589 SIGNAL_CONNECT(cancel_button, "clicked", dlg_cancel_cb, dlg);
590 GTK_WIDGET_SET_FLAGS(cancel_button, GTK_CAN_DEFAULT);
591 gtk_box_pack_start(GTK_BOX(bbox), cancel_button, TRUE, TRUE, 0);
592 gtk_widget_show(cancel_button);
594 /* Catch the "activate" signal on the filter text entry, so that
595 if the user types Return there, we act as if the "Create Stat"
596 button had been selected, as happens if Return is typed if
597 some widget that *doesn't* handle the Return key has the input
599 dlg_set_activate(filter_entry, start_button);
601 /* Catch the "key_press_event" signal in the window, so that we can
602 catch the ESC key being pressed and act as if the "Cancel" button
603 had been selected. */
604 dlg_set_cancel(dlg, cancel_button);
606 /* Give the initial focus to the "Filter" entry box. */
607 gtk_widget_grab_focus(filter_entry);
609 gtk_widget_show_all(dlg);
614 register_tap_listener_gtkhttpstat(void)
616 register_ethereal_tap("http,stat,", gtk_httpstat_init);
620 register_tap_menu_gtkhttpstat(void)
622 register_tap_menu_item("Statistics/Watch protocol/HTTP...",
623 gtk_httpstat_cb, NULL, NULL);