summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBranislav Katreniak <bkatreniak@nuvotechnologies.com>2014-02-14 12:25:37 +0100
committerJens Georg <jensg@openismus.com>2014-03-15 08:14:01 +0100
commit9c499f0dfa30c0c97e9a8eb7a94b28e8e2fce5c6 (patch)
tree2b9d68f95b7757f5f0e248d2b90c46d3fa9a4e70
parent027cb95eda57454cf65e841090f45899ddcfa45e (diff)
downloadgssdp-9c499f0dfa30c0c97e9a8eb7a94b28e8e2fce5c6.tar.gz
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
-rw-r--r--libgssdp/gssdp-resource-browser.c122
-rw-r--r--tests/gtest/test-regression.c122
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 ();