[gssdp] Recover from lost bye bye packets.



commit 9c499f0dfa30c0c97e9a8eb7a94b28e8e2fce5c6
Author: Branislav Katreniak <bkatreniak nuvotechnologies com>
Date:   Fri Feb 14 12:25:37 2014 +0100

    Recover from lost bye bye packets.
    
    When ssdp server is forcefully stopped and started again,
    it sends BYE BYE packets at its start.
    But if server does not follow SSDP specification
    or if we miss the BYE BYE packets for any other reason,
    gssdp_resource_browser believes that the server was never restarted.
    If location changed after server restart,
    we are not able to talk to the server any more.
    
    This patch checks whether location matches previous location.
    If not, BYE BYE packet is simulated.
    
    Compared to the previous code, ssdp:alive packets with no location
    and no AL are dropped.
    
    Signed-off-by: Branislav Katreniak <bkatreniak nuvotechnologies com>
    
    https://bugzilla.gnome.org/show_bug.cgi?id=724030

 libgssdp/gssdp-resource-browser.c |  122 +++++++++++++++++++++++-------------
 tests/gtest/test-regression.c     |  122 +++++++++++++++++++++++++++++++++++++
 2 files changed, 200 insertions(+), 44 deletions(-)
---
diff --git a/libgssdp/gssdp-resource-browser.c b/libgssdp/gssdp-resource-browser.c
index 7db5d1f..5aaa3d0 100644
--- a/libgssdp/gssdp-resource-browser.c
+++ b/libgssdp/gssdp-resource-browser.c
@@ -91,6 +91,7 @@ typedef struct {
         GSSDPResourceBrowser *resource_browser;
         char                 *usn;
         GSource              *timeout_src;
+        GList                *locations;
 } Resource;
 
 /* Function prototypes */
@@ -118,6 +119,9 @@ static void
 stop_discovery                   (GSSDPResourceBrowser *resource_browser);
 static gboolean
 refresh_cache                    (gpointer data);
+static void
+resource_unavailable             (GSSDPResourceBrowser *resource_browser,
+                                  SoupMessageHeaders   *headers);
 
 static void
 gssdp_resource_browser_init (GSSDPResourceBrowser *resource_browser)
@@ -679,12 +683,49 @@ resource_available (GSSDPResourceBrowser *resource_browser,
         gboolean was_cached;
         guint timeout;
         GList *locations;
+        gboolean destroyLocations;
+        GList *it1, *it2;
         char *canonical_usn;
 
         usn = soup_message_headers_get_one (headers, "USN");
         if (!usn)
                 return; /* No USN specified */
 
+        /* Build list of locations */
+        locations = NULL;
+        destroyLocations = TRUE;
+
+        header = soup_message_headers_get_one (headers, "Location");
+        if (header)
+                locations = g_list_append (locations, g_strdup (header));
+
+        header = soup_message_headers_get_one (headers, "AL");
+        if (header) {
+                /* Parse AL header. The format is:
+                 * <uri1><uri2>... */
+                const char *start, *end;
+                char *uri;
+
+                start = header;
+                while ((start = strchr (start, '<'))) {
+                        start += 1;
+                        if (!start || !*start)
+                                break;
+
+                        end = strchr (start, '>');
+                        if (!end || !*end)
+                                break;
+
+                        uri = g_strndup (start, end - start);
+                        locations = g_list_append (locations, uri);
+
+                        start = end;
+                }
+        }
+
+        if (!locations)
+                return; /* No location specified */
+
         if (resource_browser->priv->version > 0) {
                 char *version;
 
@@ -704,6 +745,22 @@ resource_available (GSSDPResourceBrowser *resource_browser,
                                   g_strdup (canonical_usn));
         }
 
+        /* If location does not match, expect that we missed bye bye packet */
+        if (resource) {
+                for (it1 = locations, it2 = resource->locations;
+                     it1 && it2;
+                     it1 = it1->next, it2 = it2->next
+                ) {
+                        if (strcmp((const char *) it1->data,
+                            (const char *) it2->data) != 0
+                        ) {
+                               resource_unavailable(resource_browser, headers);
+                               resource = NULL;
+                               break;
+                        }
+                }
+        }
+
         if (resource) {
                 /* Remove old timeout */
                 g_source_destroy (resource->timeout_src);
@@ -715,6 +772,8 @@ resource_available (GSSDPResourceBrowser *resource_browser,
 
                 resource->resource_browser = resource_browser;
                 resource->usn              = g_strdup (usn);
+                resource->locations        = locations;
+                destroyLocations = FALSE; /* Ownership passed to resource */
                 
                 g_hash_table_insert (resource_browser->priv->resources,
                                      canonical_usn,
@@ -804,52 +863,21 @@ resource_available (GSSDPResourceBrowser *resource_browser,
 
         /* Only continue with signal emission if this resource was not
          * cached already */
-        if (was_cached)
-                return;
-
-        /* Build list of locations */
-        locations = NULL;
-
-        header = soup_message_headers_get_one (headers, "Location");
-        if (header)
-                locations = g_list_append (locations, g_strdup (header));
-
-        header = soup_message_headers_get_one (headers, "AL");
-        if (header) {
-                /* Parse AL header. The format is:
-                 * <uri1><uri2>... */
-                const char *start, *end;
-                char *uri;
-                
-                start = header;
-                while ((start = strchr (start, '<'))) {
-                        start += 1;
-                        if (!start || !*start)
-                                break;
-
-                        end = strchr (start, '>');
-                        if (!end || !*end)
-                                break;
-
-                        uri = g_strndup (start, end - start);
-                        locations = g_list_append (locations, uri);
-
-                        start = end;
-                }
+        if (!was_cached) {
+                /* Emit signal */
+                g_signal_emit (resource_browser,
+                               signals[RESOURCE_AVAILABLE],
+                               0,
+                               usn,
+                               locations);
         }
-
-        /* Emit signal */
-        g_signal_emit (resource_browser,
-                       signals[RESOURCE_AVAILABLE],
-                       0,
-                       usn,
-                       locations);
-
         /* Cleanup */
-        while (locations) {
-                g_free (locations->data);
+        if (destroyLocations) {
+                while (locations) {
+                        g_free (locations->data);
 
-                locations = g_list_delete_link (locations, locations);
+                        locations = g_list_delete_link (locations, locations);
+                }
         }
 }
 
@@ -1016,13 +1044,19 @@ static void
 resource_free (gpointer data)
 {
         Resource *resource;
-
         resource = data;
+        GList    *locations;
+        locations = resource->locations;
 
         g_free (resource->usn);
 
         g_source_destroy (resource->timeout_src);
 
+        while (locations) {
+                g_free (locations->data);
+                locations = g_list_delete_link (locations, locations);
+        }
+
         g_slice_free (Resource, resource);
 }
 
diff --git a/tests/gtest/test-regression.c b/tests/gtest/test-regression.c
index a68e44c..aabba51 100644
--- a/tests/gtest/test-regression.c
+++ b/tests/gtest/test-regression.c
@@ -248,6 +248,127 @@ void test_bgo682099 (void)
  * ============================================================================
  */
 
+/* BEGIN Regression test
+ * https://bugzilla.gnome.org/show_bug.cgi?id=724030
+ * ============================================================================
+ * - Start a resource browser and send a two SSDP packets with different locations.
+ * - Check that there are 2 "resource-unavailable" signals.
+ * - Shut down the ResourceBrowser and assert that there is NO
+ *   "resource-unavailable" signal.
+ */
+#define UUID_MISSED_BYE_BYE_1 "uuid:81909e94-ebf4-469e-ac68-81f2f18816ac"
+#define USN_MISSED_BYE_BYE "urn:org-gupnp:device:RegressionTestMissedByeBye:2"
+#define USN_MISSED_BYE_BYE_1 "urn:org-gupnp:device:RegressionTestMissedByeBye:1"
+#define NT_MISSED_BYE_BYE_1 UUID_MISSED_BYE_BYE_1"::"USN_MISSED_BYE_BYE_1
+#define LOCATION_MISSED_BYE_BYE_1 "http://127.0.0.1:1234";
+#define LOCATION_MISSED_BYE_BYE_2 "http://127.0.0.1:1235";
+
+char *
+create_alive_message_bgo724030 (const char *location)
+{
+        char *msg;
+
+        msg = g_strdup_printf (SSDP_ALIVE_MESSAGE,
+                               5,
+                               location,
+                               "",
+                               "Linux/3.0 UPnP/1.0 GSSDPTesting/0.0.0",
+                               NT_MISSED_BYE_BYE_1,
+                               USN_MISSED_BYE_BYE_1);
+
+        return msg;
+}
+
+static gboolean
+announce_ressource_bgo724030_1 (gpointer user_data)
+{
+        send_packet (create_alive_message_bgo724030 (LOCATION_MISSED_BYE_BYE_1));
+
+        return FALSE;
+}
+
+static gboolean
+announce_ressource_bgo724030_2 (gpointer user_data)
+{
+        send_packet (create_alive_message_bgo724030 (LOCATION_MISSED_BYE_BYE_2));
+
+        return FALSE;
+}
+
+static void
+resource_availabe_bgo724030_1 (GSSDPResourceBrowser *src,
+                               const char           *usn,
+                               GList                *locations,
+                               gpointer              user_data)
+{
+        g_assert_cmpstr (usn, ==, USN_MISSED_BYE_BYE_1);
+        g_assert_cmpstr ((const char *) locations->data, ==, LOCATION_MISSED_BYE_BYE_1);
+        g_main_loop_quit ((GMainLoop *) user_data);
+}
+
+static void
+resource_availabe_bgo724030_2 (GSSDPResourceBrowser *src,
+                               const char           *usn,
+                               GList                *locations,
+                               gpointer              user_data)
+{
+        g_assert_cmpstr (usn, ==, USN_MISSED_BYE_BYE_1);
+        g_assert_cmpstr ((const char *) locations->data, ==, LOCATION_MISSED_BYE_BYE_2);
+        g_main_loop_quit ((GMainLoop *) user_data);
+}
+
+static void
+resource_unavailabe_bgo724030 (GSSDPResourceBrowser *src,
+                               const char           *usn,
+                               gpointer              user_data)
+{
+        g_assert_cmpstr (usn, ==, USN_MISSED_BYE_BYE_1);
+        g_main_loop_quit ((GMainLoop *) user_data);
+}
+
+void test_bgo724030 (void)
+{
+        GSSDPClient *dest;
+        GSSDPResourceBrowser *browser;
+        GError *error = NULL;
+        GMainLoop *loop;
+        gulong available_signal_id;
+
+        loop = g_main_loop_new (NULL, FALSE);
+
+        dest = gssdp_client_new (NULL, "lo", &error);
+        g_assert (dest != NULL);
+        g_assert (error == NULL);
+
+        browser = gssdp_resource_browser_new (dest, USN_MISSED_BYE_BYE_1);
+        available_signal_id = g_signal_connect (browser,
+                                      "resource-available",
+                                      G_CALLBACK (resource_availabe_bgo724030_1),
+                                      loop);
+        g_signal_connect (browser,
+                                      "resource-unavailable",
+                                      G_CALLBACK (resource_unavailabe_bgo724030),
+                                      loop);
+        gssdp_resource_browser_set_active (browser, TRUE);
+        g_timeout_add_seconds (2, announce_ressource_bgo724030_1, NULL);
+        g_timeout_add_seconds (3, announce_ressource_bgo724030_2, NULL);
+        g_main_loop_run (loop);  /* available */
+        g_signal_handler_disconnect (browser, available_signal_id);
+        available_signal_id = g_signal_connect (browser,
+                                      "resource-available",
+                                      G_CALLBACK (resource_availabe_bgo724030_2),
+                                      loop);
+        g_main_loop_run (loop);  /* unavailable + available */
+        g_main_loop_run (loop);  /* unavailable */
+        unref_object(browser);
+}
+
+/* END Regression test
+ * https://bugzilla.gnome.org/show_bug.cgi?id=724030
+ * ============================================================================
+ */
+
+
 int main (int argc, char *argv[])
 {
 #if !GLIB_CHECK_VERSION (2, 35, 0)
@@ -258,6 +379,7 @@ int main (int argc, char *argv[])
         if (g_test_slow ()) {
                g_test_add_func ("/bugs/gnome/673150", test_bgo673150);
                g_test_add_func ("/bugs/gnome/682099", test_bgo682099);
+               g_test_add_func ("/bugs/gnome/724030", test_bgo724030);
         }
 
         g_test_run ();


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]