diff --git a/extensions/soup-fly/ephy-soup-fly-extension.c b/extensions/soup-fly/ephy-soup-fly-extension.c
index 125824d..1b2ac40 100644
--- a/extensions/soup-fly/ephy-soup-fly-extension.c
+++ b/extensions/soup-fly/ephy-soup-fly-extension.c
@@ -137,6 +137,8 @@ ephy_soup_fly_extension_finalize (GObject *object)
 
   LOG ("EphySoupFlyExtension finalizing");
 
+  soup_fly_stop (extension->priv->fly);
+
   gtk_widget_destroy (GTK_WIDGET (extension->priv->fly));
 
   G_OBJECT_CLASS (parent_class)->finalize (object);
diff --git a/extensions/soup-fly/soup-fly.c b/extensions/soup-fly/soup-fly.c
index e5d239c..01f4c49 100644
--- a/extensions/soup-fly/soup-fly.c
+++ b/extensions/soup-fly/soup-fly.c
@@ -26,37 +26,76 @@
 
 #include <glib/gi18n-lib.h>
 #include <gtk/gtk.h>
+#include <glib.h>
+#include <libsoup/soup-misc.h>
 
 #define SOUP_FLY_GET_PRIVATE(object) (G_TYPE_INSTANCE_GET_PRIVATE ((object), TYPE_SOUP_FLY, SoupFlyPrivate))
 
 struct _SoupFlyPrivate {
-  GtkTreeModel *model;
-  GtkWidget *treeview;
+  GtkTreeModel *message_model;
+  GtkTreeModel *message_history_model;
+  GtkTreeModel *connection_model;
+  GtkTreeModel *connection_history_model;
+  GtkWidget *history_widget;
+  GtkLabel *history_label;
+  GtkWidget *message_treeview;
+  GtkWidget *connection_treeview;
+  GtkWidget *message_history_treeview;
+  GtkWidget *connection_history_treeview;
   GtkWidget *clear_button;
+  GtkWidget *message_history_widget;
+  GtkWidget *connection_history_widget;
+
+  GHashTable *message_data;
+  GHashTable *connection_data;
 
-  guint nth;
+  guint message_nth;
+  guint connection_nth;
   gboolean clean_finished;
 };
 
 enum {
-    COL_NUMBER,
-    COL_STATE,
-    COL_PROGRESS,
-    COL_URL,
-    N_COLUMNS
+  MESSAGE_COL_NUMBER,
+  MESSAGE_COL_STATE,
+  MESSAGE_COL_PROGRESS,
+  MESSAGE_COL_CONNECTION,
+  MESSAGE_COL_URL,
+  MESSAGE_N_COLUMNS
+};
+
+enum {
+  CONNECTION_COL_NUMBER,
+  CONNECTION_COL_STATE,
+  CONNECTION_COL_HOST,
+  CONNECTION_N_COLUMNS
+};
+
+enum {
+  MESSAGE_HISTORY_WHEN,
+  MESSAGE_HISTORY_STATE,
+  MESSAGE_HISTORY_URL,
+  MESSAGE_HISTORY_N_COLUMNS
+};
+
+enum {
+  CONNECTION_HISTORY_WHEN,
+  CONNECTION_HISTORY_STATE,
+  CONNECTION_HISTORY_MESSAGE,
+  CONNECTION_HISTORY_MESSAGE_STATE,
+  CONNECTION_HISTORY_N_COLUMNS
 };
 
 enum {
-  STATE_QUEUED,
-  STATE_SENDING,
-  STATE_WAITING,
-  STATE_RECEIVING,
-  STATE_FINISHED
+  MESSAGE_STATE_QUEUED,
+  MESSAGE_STATE_SENDING,
+  MESSAGE_STATE_WAITING,
+  MESSAGE_STATE_RECEIVING,
+  MESSAGE_STATE_FINISHED
 };
 
 const struct {
   const char *name, *color;
-} states[] = {
+} message_states[] = {
   { "Queued", "pink" },
   { "Sending", "green" },
   { "Waiting", "cyan" },
@@ -64,9 +103,96 @@ const struct {
   { "Finished", "gray" }
 };
 
+enum {
+  CONNECTION_STATE_NEW,
+  CONNECTION_STATE_DISCONNECTED,
+  CONNECTION_STATE_CONNECTING,
+  CONNECTION_STATE_TUNNELING,
+  CONNECTION_STATE_IDLE,
+  CONNECTION_STATE_INUSE,
+  CONNECTION_STATE_CLEANEDUP
+};
+
+const struct {
+  const char *name, *color;
+} connection_states[] = {
+  { "New", "pink" },
+  { "Disconnected", "red" },
+  { "Connecting", "yellow" },
+  { "Tunneling", "orange" },
+  { "Idle", "blue" },
+  { "In use", "green" },
+  { "Cleaned up", "gray" }
+};
+
+typedef struct
+{
+  GTimeVal when;
+  gint state;
+  gint entry_number;
+} FlyMessageStateEntry;
+
+typedef struct
+{
+  gint since;
+  gchar *uri;
+} FlyMessageUriEntry;
+
+typedef struct
+{
+  GtkTreeIter iter;
+  SoupFly *logger;
+  goffset content_length, received;
+  GList *state_entries;
+  GList *connections;
+  GList *uri_entries;
+  gint message_number;
+  SoupMessage *soup_message;
+  gboolean death;
+} FlyMessageData;
+
+typedef struct
+{
+  GTimeVal when;
+  gint state;
+  gint message_number;
+  gint message_entry_number;
+} FlyConnectionStateEntry;
+
+typedef struct
+{
+  GtkTreeIter iter;
+  SoupFly *logger;
+  GList *state_entries;
+  gint connection_number;
+  gint current_entry;
+  GObject *soup_connection; //SoupConnection
+  gboolean death;
+} FlyConnectionData;
+
 static void soup_fly_class_init (SoupFlyClass *klass);
 static void soup_fly_init       (SoupFly *logger);
 
+static void message_selected_cb (GtkTreeSelection *selection,
+                                 SoupFly *logger);
+
+static void connection_selected_cb (GtkTreeSelection *selection,
+                                    SoupFly *logger);
+
+static void connection_changed_state_cb (GObject* conn,
+                                         GParamSpec* pspec,
+                                         gpointer udata);
+
+static void connection_changed_message_cb (GObject* conn,
+                                           GParamSpec* pspec,
+                                           gpointer udata);
+
+static void message_disconnect_handlers (gpointer data, gpointer udata);
+
+static void connection_disconnect_handlers (gpointer data, gpointer udata);
+
+static void soup_fly_dispose (GObject *);
+
 static GObjectClass *parent_class = NULL;
 static GType type = 0;
 
@@ -110,10 +236,230 @@ soup_fly_class_init (SoupFlyClass *klass)
   GObjectClass *object_class = G_OBJECT_CLASS (klass);
 
   parent_class = g_type_class_peek_parent (klass);
+  object_class->dispose = soup_fly_dispose;
 
   g_type_class_add_private (object_class, sizeof (SoupFlyPrivate));
 }
 
+static gint
+connection_current_state (FlyConnectionData *data)
+{
+  GList *last_entry;
+  if ( (last_entry = g_list_last (data->state_entries)) )
+    return ((FlyConnectionStateEntry *)last_entry->data)->state;
+  else
+    return -1;
+}
+
+static gint
+message_current_state (FlyMessageData *data)
+{
+  GList *last_entry;
+  if ( (last_entry = g_list_last (data->state_entries)) )
+    return ((FlyMessageStateEntry *)last_entry->data)->state;
+  else
+    return -1;
+}
+
+/*
+  Returns the URI of a certain message in a certain state.
+  If state < 0, returns the current URI of the message.
+*/
+static gchar *
+message_get_uri (FlyMessageData *data, gint state)
+{
+  GList *entry;
+  GList *next;
+  if (data->uri_entries) {
+    if (state < 0) {
+      entry = g_list_last (data->uri_entries);
+    } else {
+      entry = data->uri_entries;
+      while ( (next = g_list_next(entry)) &&
+              (((FlyMessageUriEntry *)(next->data))->since <= state) )
+        entry = next;
+    }
+    if (entry) {
+      return ((FlyMessageUriEntry *)(entry->data))->uri;
+    }
+  }
+  return _("Unknown");
+}
+
+static void
+message_add_state_entry (FlyMessageData *data, gint state)
+{
+  SoupFlyPrivate *priv = data->logger->priv;
+  FlyMessageStateEntry *new_entry = g_slice_new (FlyMessageStateEntry);
+  gint old_state = message_current_state(data);
+
+  if (state == old_state) return;
+
+  g_get_current_time (&(new_entry->when));
+  new_entry->state = state;
+  new_entry->entry_number = g_list_length (data->state_entries);
+  data->state_entries = g_list_append (data->state_entries, new_entry);
+  gtk_list_store_set (GTK_LIST_STORE (priv->message_model), &(data->iter),
+                      MESSAGE_COL_STATE, state,
+                      -1);
+}
+
+static void
+message_add_uri_entry (FlyMessageData *data, gchar *uri)
+{
+  SoupFlyPrivate *priv = data->logger->priv;
+  FlyMessageUriEntry *new_entry = g_slice_new (FlyMessageUriEntry);
+  gint since;
+
+  since = (g_list_length (data->state_entries));
+
+  new_entry->since = since;
+  new_entry->uri = uri;
+  data->uri_entries = g_list_append (data->uri_entries, new_entry);
+
+  gtk_list_store_set (GTK_LIST_STORE (priv->message_model), &(data->iter),
+                      MESSAGE_COL_URL, uri,
+                      -1);
+}
+
+static void
+connection_add_state_entry (FlyConnectionData *data, gint state)
+{
+  SoupFlyPrivate *priv = data->logger->priv;
+  FlyConnectionStateEntry *new_entry, *previous_entry;
+  GList *previous_entry_l;
+  gint old_state = connection_current_state(data);
+
+  if ((state == old_state) || ((old_state == CONNECTION_STATE_TUNNELING) && (state == CONNECTION_STATE_INUSE)))
+    return;
+
+  new_entry = g_slice_new (FlyConnectionStateEntry);
+  g_get_current_time (&(new_entry->when));
+  new_entry->state = state;
+
+  previous_entry_l = g_list_last (data->state_entries);
+  data->state_entries = g_list_append (data->state_entries, new_entry);
+
+  if (previous_entry_l) {
+    previous_entry = previous_entry_l->data;
+    new_entry->message_number = previous_entry->message_number;
+    new_entry->message_entry_number = previous_entry->message_entry_number;
+  }
+
+  gtk_list_store_set (GTK_LIST_STORE (priv->connection_model), &(data->iter),
+                      CONNECTION_COL_STATE, state,
+                      -1);
+}
+
+static void
+connection_update_host (void *connection, GtkTreeModel *model,
+                        FlyConnectionData *data)
+{
+  SoupAddress *address;
+  g_object_get (connection, "remote-address", &address, NULL);
+
+  gtk_list_store_set (GTK_LIST_STORE (model), &(data->iter),
+                      CONNECTION_COL_HOST, address,
+                      -1);
+}
+
+/*
+   This function may free a message data and remove it from the message_data
+   hashtable.
+   If data has one or more connections in connections list, then only the state entries
+   will be freed and the message data will still be avaliable in message_data hashtable.
+   In that case, message_unlink_connection would be called each time a connection
+   linked to the message will be called, and that function will finally free the
+   FlyMessageData struct and the URL list once the message data has no more connections
+   linked.
+*/
+static void
+free_message_data (FlyMessageData *data, SoupFly *logger)
+{
+  SoupFlyPrivate *priv = logger->priv;
+  if (!data->death) {
+    data->death = TRUE;
+    if (data->soup_message) {
+      g_object_set_data (G_OBJECT (data->soup_message), "FlyMessageData", NULL);
+    }
+    message_disconnect_handlers (data, NULL);
+    GList *entry = data->state_entries;
+    while (entry) {
+      g_slice_free (FlyMessageStateEntry, entry->data);
+      entry = entry->next;
+    }
+    g_list_free (data->state_entries);
+    data->state_entries = NULL;
+  }
+  if (data->connections == NULL) {
+    GList *entry = data->uri_entries;
+    FlyMessageUriEntry *uri_entry;
+    while (entry) {
+      uri_entry = entry->data;
+      g_free (uri_entry->uri);
+      g_slice_free (FlyMessageUriEntry, uri_entry);
+      entry = entry->next;
+    }
+    g_hash_table_remove (priv->message_data, &(data->message_number));
+    g_list_free (data->uri_entries);
+    g_slice_free (FlyMessageData, data);
+  }
+}
+
+/*
+   Removes a connection from the connections list of a message data.
+   It may free the message data if it's connection list is empty
+   and free_message_data was called before.
+*/
+static void
+message_unlink_connection (FlyMessageData *message,
+                           FlyConnectionData *connection,
+                           SoupFly *logger)
+{
+  message->connections = g_list_remove (message->connections, connection);
+  if (message->death &&
+      (message->connections == NULL) ) {
+    free_message_data (message, logger);
+  }
+}
+
+static void
+free_connection_data (FlyConnectionData *data, SoupFly *logger)
+{
+  SoupFlyPrivate *priv = logger->priv;
+  GList *entry = data->state_entries;
+  FlyMessageData *msg_data;
+  FlyConnectionStateEntry *data_entry;
+  data->death = TRUE;
+  if (data->soup_connection) {
+    g_object_set_data (data->soup_connection, "FlyConnectionData", NULL);
+  }
+  connection_disconnect_handlers (data, NULL);
+  while (entry) {
+    data_entry = entry->data;
+    if ( ((data_entry->state == CONNECTION_STATE_INUSE) ||
+          (data_entry->state == CONNECTION_STATE_TUNNELING)) &&
+         (data_entry->message_number >= 0) ){
+      if ( (msg_data = g_hash_table_lookup (priv->message_data, &(data_entry->message_number))) )
+        message_unlink_connection(msg_data, data, logger);
+    }
+    g_slice_free (FlyConnectionStateEntry, data_entry);
+    entry = entry->next;
+  }
+  g_hash_table_remove (priv->connection_data, &(data->connection_number));
+  g_list_free (data->state_entries);
+  g_slice_free (FlyConnectionData, data);
+}
+
+static void
+soup_fly_show_cb (GtkWidget *widget, gpointer data)
+{
+  SoupFly *logger = SOUP_FLY (widget);
+  SoupFlyPrivate *priv = logger->priv;
+
+  gtk_widget_hide (priv->history_widget);
+}
+
 static void
 clear_button_clicked_cb (GtkButton *button, SoupFly *logger)
 {
@@ -121,19 +467,54 @@ clear_button_clicked_cb (GtkButton *button, SoupFly *logger)
   gboolean valid;
   SoupFlyPrivate *priv = logger->priv;
 
-  valid = gtk_tree_model_get_iter_first (priv->model, &iter);
+  valid = gtk_tree_model_get_iter_first (priv->connection_model, &iter);
 
   while (valid) {
     int state;
 
-    gtk_tree_model_get (priv->model, &iter,
-                        COL_STATE, &state,
+    gtk_tree_model_get (priv->connection_model, &iter,
+                        CONNECTION_COL_STATE, &state,
                         -1);
 
-    if (state == STATE_FINISHED)
-      valid = gtk_list_store_remove (GTK_LIST_STORE (priv->model), &iter);
+    if (state == CONNECTION_STATE_CLEANEDUP) {
+      int connection_number;
+      FlyConnectionData *con_data;
+
+      gtk_tree_model_get (priv->connection_model, &iter,
+                          CONNECTION_COL_NUMBER, &connection_number,
+                          -1);
+
+      if ((con_data = g_hash_table_lookup (priv->connection_data, &connection_number)))
+        free_connection_data(con_data, logger);
+
+      valid = gtk_list_store_remove (GTK_LIST_STORE (priv->connection_model), &iter);
+    }
     else
-      valid = gtk_tree_model_iter_next (priv->model, &iter);
+      valid = gtk_tree_model_iter_next (priv->connection_model, &iter);
+  }
+
+  valid = gtk_tree_model_get_iter_first (priv->message_model, &iter);
+
+  while (valid) {
+    int state;
+
+    gtk_tree_model_get (priv->message_model, &iter,
+                        MESSAGE_COL_STATE, &state,
+                        -1);
+
+    if (state == MESSAGE_STATE_FINISHED) {
+      int message_number;
+      FlyMessageData *msg_data;
+      gtk_tree_model_get (priv->message_model, &iter,
+                          MESSAGE_COL_NUMBER, &message_number,
+                          -1);
+      if ( (msg_data = g_hash_table_lookup(priv->message_data, &message_number)) ) {
+        if (msg_data->connections == NULL)
+          free_message_data(msg_data, logger);
+      }
+      valid = gtk_list_store_remove (GTK_LIST_STORE (priv->message_model), &iter);
+    } else
+      valid = gtk_tree_model_iter_next (priv->message_model, &iter);
   }
 }
 
@@ -143,83 +524,438 @@ check_button_toggled_cb (GtkToggleButton *toggle, SoupFly *logger)
   SoupFlyPrivate *priv = logger->priv;
 
   priv->clean_finished = gtk_toggle_button_get_active (toggle);
+  if (priv->clean_finished) {
+    clear_button_clicked_cb (GTK_BUTTON(priv->clear_button), logger);
+  }
 }
 
 static void
-state_data_func (GtkTreeViewColumn *column, GtkCellRenderer *cell,
+message_state_data_func (GtkTreeViewColumn *column, GtkCellRenderer *cell,
+                         GtkTreeModel *model, GtkTreeIter *iter,
+                         gpointer data)
+{
+  int state;
+
+  gtk_tree_model_get (model, iter, MESSAGE_COL_STATE, &state, -1);
+  g_object_set (G_OBJECT (cell),
+                "text", message_states[state].name,
+                "background", message_states[state].color,
+                NULL);
+}
+
+static void
+when_data_func (GtkTreeViewColumn *column, GtkCellRenderer *cell,
+                GtkTreeModel *model, GtkTreeIter *iter,
+                gpointer data)
+{
+  SoupFlyPrivate *priv = ((SoupFly *)data)->priv;
+  double when;
+  int itime;
+  int iusec;
+  int hour;
+  int minute;
+  int mcol;
+  char buffer[16];
+
+  if (model == priv->connection_history_model)
+    mcol = CONNECTION_HISTORY_WHEN;
+  else if (model == priv->message_history_model)
+    mcol = MESSAGE_HISTORY_WHEN;
+  else return;
+
+  gtk_tree_model_get (model, iter, mcol, &when, -1);
+  itime = (int)when;
+  iusec = ((when - (double)itime) * 1000000);
+  hour = ((itime % 86400) / 3600);
+  minute = ((itime % 3600) / 60);
+  g_snprintf (buffer, sizeof(buffer), "%02d:%02d:%02d.%06d", hour, minute, itime%60, iusec);
+  g_object_set (G_OBJECT (cell),
+                "text", buffer,
+                NULL);
+}
+
+static void
+message_connection_data_func (GtkTreeViewColumn *column, GtkCellRenderer *cell,
                  GtkTreeModel *model, GtkTreeIter *iter,
                  gpointer data)
 {
+  int connection;
+  char text[16];
+
+  gtk_tree_model_get (model, iter, MESSAGE_COL_CONNECTION, &connection, -1);
+  if (connection >= 0) {
+    g_snprintf( text, sizeof(text), "%i", connection);
+    g_object_set (G_OBJECT (cell),
+                  "text", text,
+                  NULL);
+  } else {
+    g_object_set (G_OBJECT (cell),
+                  "text", "",
+                  NULL);
+  }
+}
+
+static void
+connection_state_data_func (GtkTreeViewColumn *column, GtkCellRenderer *cell,
+                            GtkTreeModel *model, GtkTreeIter *iter,
+                            gpointer data)
+{
   int state;
 
-  gtk_tree_model_get (model, iter, COL_STATE, &state, -1);
+  gtk_tree_model_get (model, iter, CONNECTION_COL_STATE, &state, -1);
   g_object_set (G_OBJECT (cell),
-                "text", states[state].name,
-                "background", states[state].color,
+                "text", connection_states[state].name,
+                "background", connection_states[state].color,
                 NULL);
 }
 
 static void
-construct_ui (SoupFly *logger)
+connection_message_data_func (GtkTreeViewColumn *column, GtkCellRenderer *cell,
+                 GtkTreeModel *model, GtkTreeIter *iter,
+                 gpointer data)
 {
-  GtkListStore *store;
-  GtkWidget *treeview;
-  GtkCellRenderer *renderer;
-  GtkWidget *scrolled, *hbox, *vbox, *button, *check_button;
-  GtkTreeViewColumn *column;
+  int state;
+  int message_number;
+  char text[16];
+
+  gtk_tree_model_get (model, iter, CONNECTION_HISTORY_STATE, &state, -1);
+  if ((state == CONNECTION_STATE_INUSE) || (state == CONNECTION_STATE_TUNNELING)) {
+    gtk_tree_model_get (model, iter,
+                        CONNECTION_HISTORY_MESSAGE, &message_number,
+                        -1);
+    g_snprintf(text, sizeof(text), "%i", message_number);
+    g_object_set (G_OBJECT (cell),
+                  "text", text,
+                  NULL);
+  } else {
+    g_object_set (G_OBJECT (cell),
+              "text", "",
+              NULL);
+  }
+}
+
+static void
+connection_messageuri_data_func (GtkTreeViewColumn *column, GtkCellRenderer *cell,
+                 GtkTreeModel *model, GtkTreeIter *iter,
+                 gpointer data)
+{
+  SoupFly *logger = (SoupFly *)data;
+  int state;
+  int message_number;
+  int message_entry_number;
+  FlyMessageData *message_data;
   SoupFlyPrivate *priv = logger->priv;
 
-  gtk_window_set_icon_name (GTK_WINDOW (logger),
-                            GTK_STOCK_DIALOG_INFO);
+  gtk_tree_model_get (model, iter, CONNECTION_HISTORY_STATE, &state, -1);
+  if ((state == CONNECTION_STATE_INUSE) || (state == CONNECTION_STATE_TUNNELING)) {
+    gtk_tree_model_get (model, iter,
+                        CONNECTION_HISTORY_MESSAGE, &message_number,
+                        CONNECTION_HISTORY_MESSAGE_STATE, &message_entry_number,
+                        -1);
+    message_data = g_hash_table_lookup (priv->message_data, &message_number);
+    if (message_data) {
+      g_object_set (G_OBJECT (cell),
+                    "text", message_get_uri (message_data, message_entry_number),
+                    NULL);
+    } else {
+      g_object_set (G_OBJECT (cell),
+                    "text", _("Unknown"),
+                    NULL);
+    }
+  } else {
+    g_object_set (G_OBJECT (cell),
+                  "text", "",
+                  NULL);
+  }
+}
 
-  g_signal_connect (logger, "delete-event",
-                    G_CALLBACK (gtk_widget_hide_on_delete), NULL);
+static void
+connection_host_data_func (GtkTreeViewColumn *column, GtkCellRenderer *cell,
+                           GtkTreeModel *model, GtkTreeIter *iter,
+                           gpointer data)
+{
+  SoupAddress *host;
+  const char *hostName;
+  const char *physical;
+  guint port;
+  char buffer[256];
 
-  treeview = gtk_tree_view_new ();
-  gtk_tree_view_set_rules_hint (GTK_TREE_VIEW (treeview), TRUE);
+  gtk_tree_model_get (model, iter, CONNECTION_COL_HOST, &host, -1);
 
-  store = gtk_list_store_new (N_COLUMNS,
-                              G_TYPE_UINT,
-                              G_TYPE_INT,
-                              G_TYPE_INT,
-                              G_TYPE_STRING);
-  gtk_tree_view_set_model (GTK_TREE_VIEW (treeview), GTK_TREE_MODEL (store));
+  if ( !SOUP_IS_ADDRESS(host) )
+    return;
+
+  hostName = soup_address_get_name (host);
+  physical = soup_address_get_physical (host);
+  port = soup_address_get_port (host);
+  if (hostName && physical)
+    g_snprintf(buffer, sizeof(buffer), "%s[%s]:%i", hostName, physical, port);
+  else if (hostName)
+    g_snprintf(buffer, sizeof(buffer), "%s:%i", hostName, port);
+  else if (physical)
+    g_snprintf(buffer, sizeof(buffer), "%s:%i", physical, port);
+  else
+    g_snprintf(buffer, sizeof(buffer), "[%s]:%i", _("Unknown"), port);
+
+  g_object_set (G_OBJECT (cell),
+                "text", buffer,
+                NULL);
+}
+
+static void
+generate_treemodels (SoupFly *logger)
+{
+  SoupFlyPrivate *priv = logger->priv;
+  priv->message_model = GTK_TREE_MODEL (gtk_list_store_new (MESSAGE_N_COLUMNS,
+                                        G_TYPE_UINT,
+                                        G_TYPE_INT,
+                                        G_TYPE_INT,
+                                        G_TYPE_INT,
+                                        G_TYPE_STRING));
+
+  priv->connection_model = GTK_TREE_MODEL (gtk_list_store_new(CONNECTION_N_COLUMNS,
+                                           G_TYPE_UINT,
+                                           G_TYPE_INT,
+                                           G_TYPE_POINTER));
+
+  priv->message_history_model = GTK_TREE_MODEL (gtk_list_store_new (MESSAGE_HISTORY_N_COLUMNS,
+                                                G_TYPE_DOUBLE, /* WHEN */
+                                                G_TYPE_INT,    /* STATE */
+                                                G_TYPE_STRING
+                                                ));
+
+  priv->connection_history_model = GTK_TREE_MODEL (gtk_list_store_new (CONNECTION_HISTORY_N_COLUMNS,
+                                                   G_TYPE_DOUBLE,  /* WHEN */
+                                                   G_TYPE_INT,     /* STATE */
+                                                   G_TYPE_INT,     /* Message number */
+                                                   G_TYPE_INT      /* Message state */
+                                                   ));
+}
+
+static void
+generate_treeviews (SoupFly *logger)
+{
+  SoupFlyPrivate *priv = logger->priv;
+  GtkCellRenderer *renderer;
+  GtkTreeViewColumn *column;
+  GtkTreeView *treeview;
+
+  /* Message treeview */
+  treeview = GTK_TREE_VIEW(gtk_tree_view_new ());
+  priv->message_treeview = GTK_WIDGET(treeview);
+  renderer = gtk_cell_renderer_text_new ();
+  gtk_tree_view_insert_column_with_attributes (treeview,
+                                               MESSAGE_COL_NUMBER, "#",
+                                               renderer,
+                                               "text", MESSAGE_COL_NUMBER,
+                                               NULL);
+
+  column = gtk_tree_view_column_new ();
+  gtk_tree_view_column_set_title (column, _("State"));
+  renderer = gtk_cell_renderer_text_new ();
+  gtk_tree_view_column_pack_start (column, renderer, FALSE);
+  gtk_tree_view_column_set_cell_data_func (column, renderer,
+                                           message_state_data_func,
+                                           NULL, NULL);
+  gtk_tree_view_append_column (treeview, column);
+
+  column = gtk_tree_view_column_new ();
+  /* Translators: "C." is an abbreviation of "Connection"*/
+  gtk_tree_view_column_set_title (column, _("C. #"));
+  renderer = gtk_cell_renderer_text_new ();
+  gtk_tree_view_column_pack_start (column, renderer, FALSE);
+  gtk_tree_view_column_set_cell_data_func (column, renderer,
+                                           message_connection_data_func,
+                                           NULL, NULL);
+  gtk_tree_view_append_column (treeview, column);
 
   renderer = gtk_cell_renderer_text_new ();
-  gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (treeview),
-                                               COL_NUMBER, "#",
+  gtk_tree_view_insert_column_with_attributes (treeview,
+                                               MESSAGE_COL_URL, "URL",
                                                renderer,
-                                               "text", COL_NUMBER,
+                                               "text", MESSAGE_COL_URL,
                                                NULL);
 
+  /* Message history treeview */
+  treeview = GTK_TREE_VIEW(gtk_tree_view_new ());
+  priv->message_history_treeview = GTK_WIDGET(treeview);
+  column = gtk_tree_view_column_new ();
+  gtk_tree_view_column_set_title (column, _("When"));
+  renderer = gtk_cell_renderer_text_new ();
+  gtk_tree_view_column_pack_start (column, renderer, FALSE);
+  gtk_tree_view_column_set_cell_data_func (column, renderer,
+                                           when_data_func,
+                                           logger, NULL);
+  gtk_tree_view_append_column (treeview, column);
+
   column = gtk_tree_view_column_new ();
   gtk_tree_view_column_set_title (column, _("State"));
   renderer = gtk_cell_renderer_text_new ();
   gtk_tree_view_column_pack_start (column, renderer, FALSE);
   gtk_tree_view_column_set_cell_data_func (column, renderer,
-                                           state_data_func,
+                                           message_state_data_func,
                                            NULL, NULL);
-  gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column); 
+  gtk_tree_view_append_column (treeview, column);
+
+  renderer = gtk_cell_renderer_text_new ();
+  gtk_tree_view_insert_column_with_attributes (treeview,
+                                               MESSAGE_HISTORY_URL, "URL",
+                                               renderer,
+                                               "text", MESSAGE_HISTORY_URL,
+                                               NULL);
 
-  renderer = gtk_cell_renderer_progress_new ();
-  g_object_set (G_OBJECT (renderer), "text-xalign", 0.0, NULL);
-  gtk_tree_view_insert_column_with_attributes (GTK_TREE_VIEW (treeview),
-                                               COL_URL, "URL",
+  gtk_tree_view_set_rules_hint (treeview, TRUE);
+
+  /* Connection treeview */
+  treeview = GTK_TREE_VIEW(gtk_tree_view_new ());
+  priv->connection_treeview = GTK_WIDGET(treeview);
+  renderer = gtk_cell_renderer_text_new ();
+  gtk_tree_view_insert_column_with_attributes (treeview,
+                                               CONNECTION_COL_NUMBER, "#",
                                                renderer,
-                                               "text", COL_URL,
-                                               "value", COL_PROGRESS,
+                                               "text", CONNECTION_COL_NUMBER,
                                                NULL);
 
-  scrolled = gtk_scrolled_window_new (NULL, NULL);
-  gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled),
+  column = gtk_tree_view_column_new ();
+  gtk_tree_view_column_set_title (column, _("State"));
+  renderer = gtk_cell_renderer_text_new ();
+  gtk_tree_view_column_pack_start (column, renderer, FALSE);
+  gtk_tree_view_column_set_cell_data_func (column, renderer,
+                                           connection_state_data_func,
+                                           NULL, NULL);
+  gtk_tree_view_append_column (GTK_TREE_VIEW (treeview), column);
+
+  column = gtk_tree_view_column_new ();
+  gtk_tree_view_column_set_title (column, _("Host"));
+  renderer = gtk_cell_renderer_text_new ();
+  gtk_tree_view_column_pack_start (column, renderer, FALSE);
+  gtk_tree_view_column_set_cell_data_func (column, renderer,
+                                           connection_host_data_func,
+                                           logger, NULL);
+  gtk_tree_view_append_column (treeview, column);
+  gtk_tree_view_set_rules_hint (treeview, TRUE);
+
+  /* Connection history treeview */
+  treeview = GTK_TREE_VIEW(gtk_tree_view_new ());
+  priv->connection_history_treeview = GTK_WIDGET(treeview);
+  column = gtk_tree_view_column_new ();
+  gtk_tree_view_column_set_title (column, _("When"));
+  renderer = gtk_cell_renderer_text_new ();
+  gtk_tree_view_column_pack_start (column, renderer, FALSE);
+  gtk_tree_view_column_set_cell_data_func (column, renderer,
+                                           when_data_func,
+                                           logger, NULL);
+  gtk_tree_view_append_column (treeview, column);
+
+  column = gtk_tree_view_column_new ();
+  gtk_tree_view_column_set_title (column, _("State"));
+  renderer = gtk_cell_renderer_text_new ();
+  gtk_tree_view_column_pack_start (column, renderer, FALSE);
+  gtk_tree_view_column_set_cell_data_func (column, renderer,
+                                           connection_state_data_func,
+                                           NULL, NULL);
+  gtk_tree_view_append_column (treeview, column);
+  column = gtk_tree_view_column_new ();
+  gtk_tree_view_column_set_title (column, _("Message #"));
+  renderer = gtk_cell_renderer_text_new ();
+  gtk_tree_view_column_pack_start (column, renderer, FALSE);
+  gtk_tree_view_column_set_cell_data_func (column, renderer,
+                                           connection_message_data_func,
+                                           NULL, NULL);
+  gtk_tree_view_append_column (treeview, column);
+  column = gtk_tree_view_column_new ();
+  gtk_tree_view_column_set_title (column, _("Message URL"));
+  renderer = gtk_cell_renderer_text_new ();
+  gtk_tree_view_column_pack_start (column, renderer, FALSE);
+  gtk_tree_view_column_set_cell_data_func (column, renderer,
+                                           connection_messageuri_data_func,
+                                           logger, NULL);
+  gtk_tree_view_append_column (treeview, column);
+  gtk_tree_view_set_rules_hint (treeview, TRUE);
+
+  g_signal_connect (gtk_tree_view_get_selection(GTK_TREE_VIEW(priv->message_treeview)),
+                    "changed",
+                    G_CALLBACK (message_selected_cb),
+                    logger);
+
+  g_signal_connect (gtk_tree_view_get_selection(GTK_TREE_VIEW(priv->connection_treeview)),
+                    "changed",
+                    G_CALLBACK (connection_selected_cb),
+                    logger);
+
+  gtk_tree_view_set_model (GTK_TREE_VIEW (priv->message_treeview), GTK_TREE_MODEL (priv->message_model));
+  gtk_tree_view_set_model (GTK_TREE_VIEW (priv->message_history_treeview), GTK_TREE_MODEL (priv->message_history_model));
+  gtk_tree_view_set_model (GTK_TREE_VIEW (priv->connection_treeview), GTK_TREE_MODEL(priv->connection_model));
+  gtk_tree_view_set_model (GTK_TREE_VIEW (priv->connection_history_treeview), GTK_TREE_MODEL(priv->connection_history_model));
+}
+
+static void
+generate_containers_and_buttons (SoupFly *logger)
+{
+  SoupFlyPrivate *priv = logger->priv;
+  GtkWidget *connection_scrolled, *message_scrolled, *scrolled2, *vpaned, *sup_area,
+            *hbox, *vbox, *check_button, *vhistorybox, *connection_box, *message_box,
+            *connection_list_label, *message_list_label;
+
+  priv->history_label = GTK_LABEL (gtk_label_new ((const gchar *)""));
+  message_scrolled = gtk_scrolled_window_new (NULL, NULL);
+  gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (message_scrolled),
                                   GTK_POLICY_AUTOMATIC,
                                   GTK_POLICY_AUTOMATIC);
-  gtk_container_add (GTK_CONTAINER (scrolled), treeview);
+  gtk_container_add (GTK_CONTAINER (message_scrolled), priv->message_treeview);
+
+  connection_scrolled = gtk_scrolled_window_new (NULL, NULL);
+  gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (connection_scrolled),
+                                  GTK_POLICY_AUTOMATIC,
+                                  GTK_POLICY_AUTOMATIC);
+  gtk_container_add (GTK_CONTAINER (connection_scrolled), priv->connection_treeview);
+
+  message_list_label = gtk_label_new (_("Message list:"));
+  connection_list_label = gtk_label_new (_("Connection list:"));
+  message_box = gtk_vbox_new (FALSE, 10);
+  gtk_box_pack_start (GTK_BOX(message_box), message_list_label, FALSE, FALSE, 0);
+  gtk_box_pack_start (GTK_BOX(message_box), message_scrolled, TRUE, TRUE, 0);
+  connection_box = gtk_vbox_new (FALSE, 10);
+  gtk_box_pack_start (GTK_BOX(connection_box), connection_list_label, FALSE, FALSE, 0);
+  gtk_box_pack_start (GTK_BOX(connection_box), connection_scrolled, TRUE, TRUE, 0);
+
+  sup_area = gtk_hpaned_new ();
+  gtk_paned_pack1( GTK_PANED(sup_area), message_box, TRUE, FALSE);
+  gtk_paned_pack2( GTK_PANED(sup_area), connection_box, TRUE, FALSE);
+
+  priv->message_history_widget = gtk_scrolled_window_new (NULL, NULL);
+  gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (priv->message_history_widget),
+                                  GTK_POLICY_AUTOMATIC,
+                                  GTK_POLICY_AUTOMATIC);
+  gtk_container_add (GTK_CONTAINER (priv->message_history_widget), priv->message_history_treeview);
+
+  priv->connection_history_widget = gtk_scrolled_window_new (NULL, NULL);
+  gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (priv->connection_history_widget),
+                                  GTK_POLICY_AUTOMATIC,
+                                  GTK_POLICY_AUTOMATIC);
+  gtk_container_add (GTK_CONTAINER (priv->connection_history_widget), priv->connection_history_treeview);
+
+  vhistorybox = gtk_vbox_new (FALSE, 10);
+  gtk_box_pack_start (GTK_BOX(vhistorybox), GTK_WIDGET(priv->history_label), FALSE, FALSE, 0);
+  gtk_box_pack_start (GTK_BOX(vhistorybox), GTK_WIDGET(priv->message_history_widget), TRUE, TRUE, 0);
+  gtk_box_pack_start (GTK_BOX(vhistorybox), GTK_WIDGET(priv->connection_history_widget), TRUE, TRUE, 0);
+
+  priv->history_widget = GTK_WIDGET(vhistorybox);
+
+  scrolled2 = gtk_scrolled_window_new (NULL, NULL);
+  gtk_scrolled_window_set_policy (GTK_SCROLLED_WINDOW (scrolled2),
+                                  GTK_POLICY_AUTOMATIC,
+                                  GTK_POLICY_AUTOMATIC);
+  gtk_container_add (GTK_CONTAINER (scrolled2), vhistorybox);
+
+  vpaned = gtk_vpaned_new ();
+  gtk_paned_pack1( GTK_PANED(vpaned), sup_area, TRUE, FALSE);
+  gtk_paned_pack2( GTK_PANED(vpaned), scrolled2, FALSE, FALSE);
 
   hbox = gtk_hbox_new (FALSE, 10);
-  button = gtk_button_new_with_label (_("Clear finished"));
-  g_signal_connect (button, "clicked", G_CALLBACK (clear_button_clicked_cb), logger);
-  gtk_container_add (GTK_CONTAINER (hbox), button);
+  priv->clear_button = gtk_button_new_with_label (_("Clear finished"));
+  g_signal_connect (priv->clear_button, "clicked", G_CALLBACK (clear_button_clicked_cb), logger);
+  gtk_container_add (GTK_CONTAINER (hbox), priv->clear_button);
 
   check_button = gtk_check_button_new_with_label (_("Automatically remove finished messages"));
   gtk_toggle_button_set_active (GTK_TOGGLE_BUTTON (check_button), priv->clean_finished);
@@ -227,50 +963,62 @@ construct_ui (SoupFly *logger)
   gtk_container_add (GTK_CONTAINER (hbox), check_button);
 
   vbox = gtk_vbox_new (FALSE, 10);
-  gtk_box_pack_start (GTK_BOX (vbox), scrolled, TRUE, TRUE, 0);
+  gtk_box_pack_start (GTK_BOX (vbox), vpaned, TRUE, TRUE, 0);
   gtk_box_pack_start (GTK_BOX (vbox), hbox, FALSE, TRUE, 0);
 
   gtk_container_add (GTK_CONTAINER (logger), vbox);
+}
+
+static void
+construct_ui (SoupFly *logger)
+{
+  gtk_window_set_icon_name (GTK_WINDOW (logger),
+                            GTK_STOCK_DIALOG_INFO);
+
+  g_signal_connect (logger, "delete-event",
+                    G_CALLBACK (gtk_widget_hide_on_delete), NULL);
+
+  generate_treemodels (logger);
+  generate_treeviews (logger);
+  generate_containers_and_buttons (logger);
 
   gtk_window_set_title (GTK_WINDOW (logger), "Soup Fly");
   gtk_container_set_border_width (GTK_CONTAINER (logger), 5);
   gtk_window_set_default_size (GTK_WINDOW (logger), 640, 480);
 
-  priv->model = GTK_TREE_MODEL (store);
-  priv->treeview = treeview;
-  priv->clear_button = button;
+  g_signal_connect (logger, "show",
+                    G_CALLBACK (soup_fly_show_cb), NULL);
 }
 
 static void
 soup_fly_init (SoupFly *logger)
 {
   logger->priv = SOUP_FLY_GET_PRIVATE (logger);
-  
-  logger->priv->nth = 0;
+
+  logger->priv->message_nth = 0;
+  logger->priv->connection_nth = 0;
+
+  logger->priv->message_data = g_hash_table_new (g_int_hash, g_int_equal);
+  logger->priv->connection_data = g_hash_table_new (g_int_hash, g_int_equal);
 
   construct_ui (logger);
 }
 
-typedef struct
-{
-  GtkTreeIter iter;
-  SoupFly *logger;
-  goffset content_length, received;
-} FlyMessageData;
-
 static void
 message_finished_cb (SoupMessage *message, FlyMessageData *data)
 {
   SoupFlyPrivate *priv = data->logger->priv;
 
+  message_add_state_entry (data, MESSAGE_STATE_FINISHED);
+  gtk_list_store_set (GTK_LIST_STORE (priv->message_model), &data->iter,
+                      MESSAGE_COL_PROGRESS, 0,
+                      MESSAGE_COL_STATE, MESSAGE_STATE_FINISHED,
+                      -1);
+
   if (priv->clean_finished)
-    gtk_list_store_remove (GTK_LIST_STORE (priv->model), &data->iter);
-  else
-    gtk_list_store_set (GTK_LIST_STORE (priv->model), &data->iter,
-                        COL_PROGRESS, 0,
-                        COL_STATE, STATE_FINISHED,
-                        -1);
-  g_slice_free (FlyMessageData, data);
+    clear_button_clicked_cb (GTK_BUTTON(priv->clear_button), data->logger);
+
+  data->soup_message = NULL;
 }
 
 static void
@@ -281,8 +1029,8 @@ message_got_chunk_cb (SoupMessage *message, SoupBuffer *chunk, FlyMessageData *d
   data->received += chunk->length;
   if (data->content_length && data->received) {
     gint percentage = CLAMP ((int)(data->received * 100 / data->content_length), 0, 100);
-    gtk_list_store_set (GTK_LIST_STORE (priv->model), &data->iter,
-                        COL_PROGRESS, percentage,
+    gtk_list_store_set (GTK_LIST_STORE (priv->message_model), &data->iter,
+                        MESSAGE_COL_PROGRESS, percentage,
                         -1);
   }
 }
@@ -293,34 +1041,281 @@ message_got_headers_cb (SoupMessage *message, FlyMessageData *data)
   SoupFlyPrivate *priv = data->logger->priv;
 
   data->content_length = soup_message_headers_get_content_length (message->response_headers);
-  gtk_list_store_set (GTK_LIST_STORE (priv->model), &data->iter,
-                      COL_STATE, STATE_RECEIVING,
-                      COL_PROGRESS, 0,
+  gtk_list_store_set (GTK_LIST_STORE (priv->message_model), &data->iter,
+                      MESSAGE_COL_PROGRESS, 0,
                       -1);
+  message_add_state_entry (data, MESSAGE_STATE_RECEIVING);
 }
 
 static void
 message_wrote_body_cb (SoupMessage *message, FlyMessageData *data)
 {
-  SoupFlyPrivate *priv = data->logger->priv;
+  message_add_state_entry (data, MESSAGE_STATE_WAITING);
+}
+
+static void insert_message_entry (gpointer data,
+                                    gpointer udata)
+{
+  FlyMessageStateEntry *entry = (FlyMessageStateEntry *)data;
+  gpointer *params = udata;  /* params[0] -> list store
+                                params[1] -> message data */
+  GtkListStore *model = GTK_LIST_STORE(params[0]);
+  FlyMessageData *msg_data = (FlyMessageData *)params[1];
+  GtkTreeIter iter;
+  double dwhen;
+
+  gtk_list_store_append (GTK_LIST_STORE (model), &iter);
+  dwhen = entry->when.tv_sec + entry->when.tv_usec / 1000000.0;
+  gtk_list_store_set (GTK_LIST_STORE (model), &iter,
+                      MESSAGE_HISTORY_WHEN, dwhen,
+                      MESSAGE_HISTORY_STATE, entry->state,
+                      MESSAGE_HISTORY_URL,
+                          message_get_uri (msg_data, entry->entry_number),
+                      -1);
+}
 
-  gtk_list_store_set (GTK_LIST_STORE (priv->model), &data->iter,
-                      COL_STATE, STATE_WAITING,
+static void insert_connection_entry (gpointer data,
+                                       gpointer udata)
+{
+  FlyConnectionStateEntry *entry = (FlyConnectionStateEntry *)data;
+  GtkListStore *model = GTK_LIST_STORE(udata);
+  GtkTreeIter iter;
+  double dwhen;
+
+  gtk_list_store_append (GTK_LIST_STORE (model), &iter);
+  dwhen = entry->when.tv_sec + entry->when.tv_usec / 1000000.0;
+  gtk_list_store_set (GTK_LIST_STORE (model), &iter,
+                      CONNECTION_HISTORY_WHEN, dwhen,
+                      CONNECTION_HISTORY_STATE, entry->state,
+                      CONNECTION_HISTORY_MESSAGE, entry->message_number,
+                      CONNECTION_HISTORY_MESSAGE_STATE, entry->message_entry_number,
                       -1);
 }
 
+static void message_selected_cb (GtkTreeSelection *selection,
+                                 SoupFly *logger)
+{
+  GtkTreeIter iter;
+  GtkTreeModel *model;
+  SoupFlyPrivate *priv = logger->priv;
+  FlyMessageData *message_data;
+  gint message_number;
+  char label_text[40];
+  gpointer udata[2];
+
+  if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
+    gtk_tree_model_get (model, &iter, MESSAGE_COL_NUMBER, &message_number, -1);
+    g_return_if_fail ((message_data = g_hash_table_lookup (priv->message_data, &message_number)));
+    g_snprintf (label_text, sizeof(label_text),
+                _("Message #%i state history:"),
+                message_data->message_number);
+    gtk_label_set_text (priv->history_label, label_text);
+    gtk_list_store_clear (GTK_LIST_STORE(priv->message_history_model));
+
+    udata[0] = priv->message_history_model;
+    udata[1] = message_data;
+
+    g_list_foreach (message_data->state_entries, insert_message_entry, &udata);
+    gtk_widget_show (priv->history_widget);
+    gtk_widget_show (priv->message_history_widget);
+    gtk_widget_hide (priv->connection_history_widget);
+  }
+}
+
+static void connection_selected_cb (GtkTreeSelection *selection,
+                                    SoupFly *logger)
+{
+  GtkTreeIter iter;
+  GtkTreeModel *model;
+  SoupFlyPrivate *priv = logger->priv;
+  FlyConnectionData *connection_data;
+  gint connection_number;
+  char label_text[40];
+
+  if (gtk_tree_selection_get_selected (selection, &model, &iter)) {
+    gtk_tree_model_get (model, &iter,
+                        MESSAGE_COL_NUMBER, &connection_number,
+                        -1);
+    g_return_if_fail ((connection_data = g_hash_table_lookup (priv->connection_data,
+                                                              &connection_number)));
+    g_snprintf (label_text, sizeof(label_text),
+                _("Connection #%i state history"),
+                connection_data->connection_number);
+    gtk_label_set_text (priv->history_label, label_text);
+    gtk_list_store_clear (GTK_LIST_STORE (priv->connection_history_model));
+    g_list_foreach (connection_data->state_entries, insert_connection_entry,
+                    priv->connection_history_model);
+    gtk_widget_show (priv->history_widget);
+    gtk_widget_show (priv->connection_history_widget);
+    gtk_widget_hide (priv->message_history_widget);
+  }
+}
+
+static void
+connection_destroyed_cb (gpointer data, GObject *conn)
+{
+  FlyConnectionData *con_data = (FlyConnectionData *)data;
+  SoupFly *logger = SOUP_FLY (con_data->logger);
+  SoupFlyPrivate *priv = logger->priv;
+
+  con_data->soup_connection = NULL;
+
+  connection_add_state_entry (con_data, CONNECTION_STATE_CLEANEDUP);
+
+  if (priv->clean_finished) {
+    clear_button_clicked_cb (GTK_BUTTON (priv->clear_button), logger);
+  }
+}
+
+static void
+connection_created_cb (SoupSession *session, void *connection,
+                       SoupFly *logger)
+{
+  FlyConnectionData *data;
+  GObject *obj;
+  g_return_if_fail ( (obj = G_OBJECT (connection)) );
+  g_return_if_fail ( !g_object_get_data (obj, "FlyConnectionData") );
+  data = g_slice_new0 (FlyConnectionData);
+  data->connection_number = logger->priv->connection_nth++;
+  data->soup_connection = G_OBJECT(connection);
+  g_object_set_data (obj, "FlyConnectionData", data);
+  g_hash_table_insert (logger->priv->connection_data,
+                       &(data->connection_number), data);
+  data->logger = logger;
+  data->state_entries = NULL;
+
+  gtk_list_store_append (GTK_LIST_STORE (logger->priv->connection_model),
+                         &(data->iter));
+
+  gtk_list_store_set (GTK_LIST_STORE (logger->priv->connection_model), &(data->iter),
+                      CONNECTION_COL_NUMBER, data->connection_number,
+                      -1);
+  connection_add_state_entry (data, CONNECTION_STATE_NEW);
+  connection_update_host (connection, logger->priv->connection_model, data);
+
+  g_signal_connect (obj, "notify::state", G_CALLBACK (connection_changed_state_cb), logger);
+  g_signal_connect (obj, "notify::message", G_CALLBACK (connection_changed_message_cb), logger);
+  g_object_weak_ref (obj, connection_destroyed_cb, data);
+}
+
+static void
+tunneling_cb (SoupSession *session, void *connection,
+                       SoupFly *logger)
+{
+  FlyConnectionData *data;
+  GObject *obj;
+  g_return_if_fail ( (obj = G_OBJECT (connection)) );
+  g_return_if_fail ( (data = g_object_get_data (obj, "FlyConnectionData")) );
+
+  connection_add_state_entry (data, CONNECTION_STATE_TUNNELING);
+}
+
+static void
+connection_changed_message_cb (GObject* conn, GParamSpec* pspec, gpointer udata)
+{
+  SoupFly *logger;
+  SoupFlyPrivate *priv;
+  FlyConnectionData *data;
+  FlyMessageData *msg_data;
+  GObject *msg;
+  gint message_number, message_entry_number;
+  GList *current_entry;
+
+  logger = SOUP_FLY(udata);
+  priv = logger->priv;
+
+  g_return_if_fail ( (data = g_object_get_data (conn, "FlyConnectionData")) );
+
+  g_object_get (conn,
+                "message", &msg,
+                NULL);
+
+  if (!msg || !SOUP_IS_MESSAGE (msg))
+    return;
+
+  msg_data = g_object_get_data (msg, "FlyMessageData");
+
+  if (!msg_data) {
+    message_number = -1;
+    message_entry_number = -1;
+  } else {
+    if ( !g_list_find (msg_data->connections, data) )
+      msg_data->connections = g_list_append(msg_data->connections, data);
+
+    message_number = msg_data->message_number;
+    message_entry_number = g_list_length (msg_data->state_entries);
+
+    gtk_list_store_set (GTK_LIST_STORE (priv->message_model), &(msg_data->iter),
+                        MESSAGE_COL_CONNECTION, data->connection_number,
+                        -1);
+  }
+
+  current_entry = g_list_last (data->state_entries);
+  if (current_entry) {
+    FlyConnectionStateEntry *entry = (FlyConnectionStateEntry *)current_entry->data;
+    entry->message_number = message_number;
+    entry->message_entry_number = message_entry_number;
+  }
+}
+
+static void
+connection_changed_state_cb (GObject* conn, GParamSpec* pspec, gpointer udata)
+{
+  SoupConnectionState state;
+  FlyConnectionData *data;
+  SoupFly *logger = SOUP_FLY(udata);
+
+  g_return_if_fail ( (data = g_object_get_data (conn, "FlyConnectionData")) );
+
+  g_object_get (conn,
+                "state", &state,
+                NULL);
+
+  switch (state) {
+    case SOUP_CONNECTION_IDLE:
+      connection_add_state_entry (data, CONNECTION_STATE_IDLE);
+      break;
+    case SOUP_CONNECTION_DISCONNECTED:
+    case SOUP_CONNECTION_REMOTE_DISCONNECTED:
+      connection_add_state_entry (data, CONNECTION_STATE_DISCONNECTED);
+      break;
+    case SOUP_CONNECTION_IN_USE:
+      connection_add_state_entry (data, CONNECTION_STATE_INUSE);
+      break;
+    case SOUP_CONNECTION_CONNECTING:
+      connection_add_state_entry (data, CONNECTION_STATE_CONNECTING);
+      break;
+    case SOUP_CONNECTION_NEW:
+      connection_add_state_entry (data, CONNECTION_STATE_NEW);
+      break;
+  }
+  connection_update_host (conn, logger->priv->connection_model, data);
+}
+
+static void
+message_changed_uri_cb (SoupMessage* msg, GParamSpec* pspec, gpointer udata)
+{
+  FlyMessageData *data;
+  SoupURI *uri;
+  gchar *uri_string;
+
+  g_return_if_fail ((data = g_object_get_data (G_OBJECT (msg), "FlyMessageData")));
+
+  uri =  soup_message_get_uri (msg);
+  uri_string = soup_uri_to_string (uri, FALSE);
+  message_add_uri_entry (data, uri_string);
+}
+
 static void
 request_started_cb (SoupSession *session, SoupMessage *message,
                     SoupSocket *socket, SoupFly *logger)
 {
-  SoupFlyPrivate *priv = logger->priv;
   FlyMessageData *data = g_object_get_data (G_OBJECT (message), "FlyMessageData");
 
   if (!data)
     return;
-  gtk_list_store_set (GTK_LIST_STORE (priv->model), &data->iter,
-                      COL_STATE, STATE_SENDING,
-                      -1);
+
+  message_add_state_entry (data, MESSAGE_STATE_SENDING);
 }
 
 static void
@@ -335,24 +1330,93 @@ request_queued_cb (SoupSession *session, SoupMessage *message, SoupFly *logger)
   uri = soup_message_get_uri (message);
   uri_string = soup_uri_to_string (uri, FALSE);
 
-  gtk_list_store_append (GTK_LIST_STORE (priv->model), &iter);
-  gtk_list_store_set (GTK_LIST_STORE (priv->model), &iter,
-                      COL_NUMBER, priv->nth++,
-                      COL_URL, uri_string,
-                      COL_STATE, STATE_QUEUED,
-                      -1);
-  g_free (uri_string);
-  
+  gtk_list_store_append (GTK_LIST_STORE (priv->message_model), &iter);
+
   data = g_slice_new0 (FlyMessageData);
+  data->message_number = priv->message_nth++;
+  data->soup_message = message;
+  g_hash_table_insert (priv->message_data, &(data->message_number), data);
+
+  gtk_list_store_set (GTK_LIST_STORE (priv->message_model), &iter,
+                      MESSAGE_COL_NUMBER, data->message_number,
+                      MESSAGE_COL_URL, uri_string,
+                      MESSAGE_COL_STATE, MESSAGE_STATE_QUEUED,
+                      -1);
+
   data->iter = iter;
   data->logger = logger;
+  message_add_state_entry (data, MESSAGE_STATE_QUEUED);
+  message_add_uri_entry (data, uri_string);
+
   g_object_set_data (G_OBJECT (message), "FlyMessageData", data);
+  g_signal_connect (message, "notify::uri", G_CALLBACK (message_changed_uri_cb), data);
   g_signal_connect (message, "wrote-body", G_CALLBACK (message_wrote_body_cb), data);
   g_signal_connect (message, "got-headers", G_CALLBACK (message_got_headers_cb), data);
   g_signal_connect (message, "got-chunk", G_CALLBACK (message_got_chunk_cb), data);
   g_signal_connect (message, "finished", G_CALLBACK (message_finished_cb), data);
 }
 
+static void
+message_disconnect_handlers (gpointer data, gpointer udata)
+{
+  FlyMessageData *message_data = data;
+  SoupMessage *message = message_data->soup_message;
+  if (message) {
+    g_signal_handlers_disconnect_by_func (message, G_CALLBACK (message_changed_uri_cb), data);
+    g_signal_handlers_disconnect_by_func (message, G_CALLBACK (message_wrote_body_cb), data);
+    g_signal_handlers_disconnect_by_func (message, G_CALLBACK (message_got_headers_cb), data);
+    g_signal_handlers_disconnect_by_func (message, G_CALLBACK (message_got_chunk_cb), data);
+    g_signal_handlers_disconnect_by_func (message, G_CALLBACK (message_finished_cb), data);
+  }
+}
+
+static void
+connection_disconnect_handlers (gpointer data, gpointer udata)
+{
+  FlyConnectionData *connection_data = data;
+  GObject *connection = connection_data->soup_connection;
+  if (G_OBJECT (connection)) {
+    g_signal_handlers_disconnect_by_func (connection, G_CALLBACK (connection_changed_state_cb), connection_data->logger);
+    g_signal_handlers_disconnect_by_func (connection, G_CALLBACK (connection_changed_message_cb), connection_data->logger);
+    g_object_weak_unref (connection,  connection_destroyed_cb, data);
+  }
+}
+
+static void
+soup_fly_dispose (GObject *o)
+{
+  SoupFly *logger = SOUP_FLY (o);
+  SoupFlyPrivate *priv;
+  GList *hash_values;
+  GList *value;
+  g_return_if_fail (logger);
+  priv = logger->priv;
+
+  g_object_unref (priv->message_model);
+  g_object_unref (priv->message_history_model);
+  g_object_unref (priv->connection_model);
+  g_object_unref (priv->connection_history_model);
+
+  hash_values = g_hash_table_get_values (priv->connection_data);
+  value = hash_values;
+  while (value) {
+    free_connection_data (value->data, logger);
+    value = value->next;
+  }
+  g_list_free (hash_values);
+
+  hash_values = g_hash_table_get_values (priv->message_data);
+  value = hash_values;
+  while (value) {
+    free_message_data (value->data, logger);
+    value = value->next;
+  }
+  g_list_free (hash_values);
+
+  g_hash_table_destroy (priv->connection_data);
+  g_hash_table_destroy (priv->message_data);
+}
+
 /* Public API */
 
 /**
@@ -368,12 +1432,15 @@ soup_fly_start (SoupFly *logger)
   SoupFlyPrivate *priv;
 
   g_return_if_fail (IS_SOUP_FLY (logger));
-  
+
   priv = logger->priv;
   session = webkit_get_default_session ();
   g_return_if_fail (session);
   g_signal_connect (session, "request-queued", G_CALLBACK (request_queued_cb), logger);
   g_signal_connect (session, "request-started", G_CALLBACK (request_started_cb), logger);
+
+  g_signal_connect (session, "connection-created", G_CALLBACK (connection_created_cb), logger);
+  g_signal_connect (session, "tunneling", G_CALLBACK (tunneling_cb), logger);
 }
 
 /**
@@ -386,11 +1453,29 @@ void
 soup_fly_stop (SoupFly *logger)
 {
   SoupSession *session;
+  GList *messages;
+  GList *connections;
 
   g_return_if_fail (IS_SOUP_FLY (logger));
-  
+
+  gtk_widget_hide (GTK_WIDGET (logger));
+
   session = webkit_get_default_session ();
+
   g_signal_handlers_disconnect_by_func (session, G_CALLBACK (request_queued_cb), logger);
+  g_signal_handlers_disconnect_by_func (session, G_CALLBACK (request_started_cb), logger);
+
+  g_signal_handlers_disconnect_by_func (session, G_CALLBACK (connection_created_cb), logger);
+  g_signal_handlers_disconnect_by_func (session, G_CALLBACK (tunneling_cb), logger);
+
+  messages = g_hash_table_get_values (logger->priv->message_data);
+  connections = g_hash_table_get_values (logger->priv->connection_data);
+
+  g_list_foreach (messages, message_disconnect_handlers, NULL);
+  g_list_foreach (connections, connection_disconnect_handlers, NULL);
+
+  g_list_free (messages);
+  g_list_free (connections);
 }
 
 /**
