[gupnp] ServiceProxy: Parse response in call_finish



commit 291aa2e9051e8e653bbf6489f70dfee753593311
Author: Jens Georg <mail jensge org>
Date:   Sat Apr 2 10:32:49 2022 +0200

    ServiceProxy: Parse response in call_finish
    
    So that all errors that are not related to getting parameters are
    handled when call_finish is called, including SOAP errors.
    
    This removes the weird necessity to call _get on actions to get the SOAP
    errors.

 libgupnp/gupnp-service-proxy-action-private.h |   6 +
 libgupnp/gupnp-service-proxy-action.c         | 316 +++++++++++++-------------
 libgupnp/gupnp-service-proxy.c                |  20 +-
 3 files changed, 175 insertions(+), 167 deletions(-)
---
diff --git a/libgupnp/gupnp-service-proxy-action-private.h b/libgupnp/gupnp-service-proxy-action-private.h
index 4bd1015..3f92e51 100644
--- a/libgupnp/gupnp-service-proxy-action-private.h
+++ b/libgupnp/gupnp-service-proxy-action-private.h
@@ -10,6 +10,7 @@
 #define GUPNP_SERVICE_PROXY_ACTION_H
 
 #include <gobject/gvaluecollector.h>
+#include <libxml/tree.h>
 
 G_BEGIN_DECLS
 
@@ -148,6 +149,8 @@ struct _GUPnPServiceProxyAction {
         GPtrArray *args;
         GHashTable *arg_map;
         gboolean pending;
+        xmlDocPtr doc;
+        xmlNodePtr params;
 };
 
 G_GNUC_INTERNAL GUPnPServiceProxyAction *
@@ -161,6 +164,9 @@ gupnp_service_proxy_action_get_result_valist (GUPnPServiceProxyAction *action,
 G_GNUC_INTERNAL void
 gupnp_service_proxy_action_serialize (GUPnPServiceProxyAction *action,
                                       const char *service_type);
+
+G_GNUC_INTERNAL void
+gupnp_service_proxy_action_check_response (GUPnPServiceProxyAction *action);
 G_END_DECLS
 
 #endif /* GUPNP_SERVICE_PROXY_ACTION_H */
diff --git a/libgupnp/gupnp-service-proxy-action.c b/libgupnp/gupnp-service-proxy-action.c
index a4f151f..ca148c8 100644
--- a/libgupnp/gupnp-service-proxy-action.c
+++ b/libgupnp/gupnp-service-proxy-action.c
@@ -54,151 +54,6 @@ read_out_parameter (const char *arg_name,
 }
 
 
-/* Checks an action response for errors and returns the parsed
- * xmlDoc object. */
-static xmlDoc *
-check_action_response (G_GNUC_UNUSED GUPnPServiceProxy *proxy,
-                       GUPnPServiceProxyAction         *action,
-                       xmlNode                        **params,
-                       GError                         **error)
-{
-        xmlDoc *response;
-        int code;
-
-        if (action->msg == NULL || action->response == NULL) {
-                g_set_error (error,
-                             GUPNP_SERVER_ERROR,
-                             GUPNP_SERVER_ERROR_INVALID_RESPONSE,
-                             "No message, the action was not sent?");
-
-                return NULL;
-        }
-
-        SoupStatus status = soup_message_get_status (action->msg);
-
-        /* Check for errors */
-        switch (status) {
-        case SOUP_STATUS_OK:
-        case SOUP_STATUS_INTERNAL_SERVER_ERROR:
-                break;
-        default:
-                _gupnp_error_set_server_error (error, action->msg);
-
-                return NULL;
-        }
-
-        /* Parse response */
-        gconstpointer data;
-        gsize length;
-        data = g_bytes_get_data (action->response, &length);
-        response = xmlRecoverMemory (data, length);
-        g_clear_pointer (&action->response, g_bytes_unref);
-
-        if (!response) {
-                if (status == SOUP_STATUS_OK) {
-                        g_set_error (error,
-                                     GUPNP_SERVER_ERROR,
-                                     GUPNP_SERVER_ERROR_INVALID_RESPONSE,
-                                     "Could not parse SOAP response");
-                } else {
-                        g_set_error_literal (
-                                error,
-                                GUPNP_SERVER_ERROR,
-                                GUPNP_SERVER_ERROR_INTERNAL_SERVER_ERROR,
-                                soup_message_get_reason_phrase (action->msg));
-                }
-
-                return NULL;
-        }
-
-        /* Get parameter list */
-        *params = xml_util_get_element ((xmlNode *) response,
-                                        "Envelope",
-                                        NULL);
-        if (*params != NULL)
-                *params = xml_util_real_node ((*params)->children);
-
-        if (*params != NULL) {
-                if (strcmp ((const char *) (*params)->name, "Header") == 0)
-                        *params = xml_util_real_node ((*params)->next);
-
-                if (*params != NULL)
-                        if (strcmp ((const char *) (*params)->name, "Body") != 0)
-                                *params = NULL;
-        }
-
-        if (*params != NULL)
-                *params = xml_util_real_node ((*params)->children);
-
-        if (*params == NULL) {
-                g_set_error (error,
-                             GUPNP_SERVER_ERROR,
-                             GUPNP_SERVER_ERROR_INVALID_RESPONSE,
-                             "Invalid Envelope");
-
-                xmlFreeDoc (response);
-
-                return NULL;
-        }
-
-        /* Check whether we have a Fault */
-        if (status == SOUP_STATUS_INTERNAL_SERVER_ERROR) {
-                xmlNode *param;
-                char *desc;
-
-                param = xml_util_get_element (*params,
-                                              "detail",
-                                              "UPnPError",
-                                              NULL);
-
-                if (!param) {
-                        g_set_error (error,
-                                     GUPNP_SERVER_ERROR,
-                                     GUPNP_SERVER_ERROR_INVALID_RESPONSE,
-                                     "Invalid Fault");
-
-                        xmlFreeDoc (response);
-
-                        return NULL;
-                }
-
-                /* Code */
-                code = xml_util_get_child_element_content_int
-                                        (param, "errorCode");
-                if (code == -1) {
-                        g_set_error (error,
-                                     GUPNP_SERVER_ERROR,
-                                     GUPNP_SERVER_ERROR_INVALID_RESPONSE,
-                                     "Invalid Fault");
-
-                        xmlFreeDoc (response);
-
-                        return NULL;
-                }
-
-                /* Description */
-                desc = xml_util_get_child_element_content_glib
-                                        (param, "errorDescription");
-                if (desc == NULL)
-                        desc = g_strdup (
-                                soup_message_get_reason_phrase (action->msg));
-
-                g_set_error_literal (error,
-                                     GUPNP_CONTROL_ERROR,
-                                     code,
-                                     desc);
-
-                g_free (desc);
-
-                xmlFreeDoc (response);
-
-                return NULL;
-        }
-
-        return response;
-}
-
-
 GUPnPServiceProxyAction *
 gupnp_service_proxy_action_new_internal (const char *action) {
         GUPnPServiceProxyAction *ret;
@@ -244,6 +99,7 @@ action_dispose (GUPnPServiceProxyAction *action)
         g_hash_table_destroy (action->arg_map);
         g_ptr_array_unref (action->args);
         g_clear_pointer (&action->response, g_bytes_unref);
+        g_clear_pointer (&action->doc, xmlFreeDoc);
 
         g_free (action->name);
 }
@@ -494,6 +350,151 @@ gupnp_service_proxy_action_serialize (GUPnPServiceProxyAction *action,
         g_string_insert (action->msg_str, action->header_pos, "\">");
 }
 
+/* Checks an action response for errors and returns the parsed
+ * xmlDoc object. */
+void
+gupnp_service_proxy_action_check_response (GUPnPServiceProxyAction *action)
+{
+        xmlDoc *response;
+        int code;
+
+        if (action->doc != NULL) {
+                return;
+        }
+
+        if (action->error != NULL) {
+                return;
+        }
+
+        if (action->msg == NULL || action->response == NULL) {
+                g_set_error_literal (&action->error,
+                                     GUPNP_SERVER_ERROR,
+                                     GUPNP_SERVER_ERROR_INVALID_RESPONSE,
+                                     "No message, the action was not sent?");
+
+                return;
+        }
+
+        SoupStatus status = soup_message_get_status (action->msg);
+
+        if (status != SOUP_STATUS_OK &&
+            status != SOUP_STATUS_INTERNAL_SERVER_ERROR) {
+                _gupnp_error_set_server_error (&action->error, action->msg);
+                return;
+        }
+
+        /* Parse response */
+        gconstpointer data;
+        gsize length;
+        data = g_bytes_get_data (action->response, &length);
+        response = xmlRecoverMemory (data, length);
+        g_clear_pointer (&action->response, g_bytes_unref);
+
+        if (!response) {
+                if (status == SOUP_STATUS_OK) {
+                        g_set_error_literal (
+                                &action->error,
+                                GUPNP_SERVER_ERROR,
+                                GUPNP_SERVER_ERROR_INVALID_RESPONSE,
+                                "Could not parse SOAP response");
+                } else {
+                        g_set_error_literal (
+                                &action->error,
+                                GUPNP_SERVER_ERROR,
+                                GUPNP_SERVER_ERROR_INTERNAL_SERVER_ERROR,
+                                soup_message_get_reason_phrase (action->msg));
+                }
+
+                return;
+        }
+
+        xmlNodePtr params = NULL;
+        /* Get parameter list */
+        params = xml_util_get_element ((xmlNode *) response, "Envelope", NULL);
+        if (params != NULL)
+                params = xml_util_real_node (params->children);
+
+        if (params != NULL) {
+                if (strcmp ((const char *) params->name, "Header") == 0)
+                        params = xml_util_real_node (params->next);
+
+                if (params != NULL)
+                        if (strcmp ((const char *) params->name, "Body") != 0)
+                                params = NULL;
+        }
+
+        if (params != NULL)
+                params = xml_util_real_node (params->children);
+
+        if (params == NULL) {
+                g_set_error (&action->error,
+                             GUPNP_SERVER_ERROR,
+                             GUPNP_SERVER_ERROR_INVALID_RESPONSE,
+                             "Invalid Envelope");
+
+                xmlFreeDoc (response);
+
+                return;
+        }
+
+        /* Check whether we have a Fault */
+        if (status == SOUP_STATUS_INTERNAL_SERVER_ERROR) {
+                xmlNode *param;
+                char *desc;
+
+                param = xml_util_get_element (params,
+                                              "detail",
+                                              "UPnPError",
+                                              NULL);
+
+                if (!param) {
+                        g_set_error (&action->error,
+                                     GUPNP_SERVER_ERROR,
+                                     GUPNP_SERVER_ERROR_INVALID_RESPONSE,
+                                     "Invalid Fault");
+
+                        xmlFreeDoc (response);
+
+                        return;
+                }
+
+                /* Code */
+                code = xml_util_get_child_element_content_int (param,
+                                                               "errorCode");
+                if (code == -1) {
+                        g_set_error (&action->error,
+                                     GUPNP_SERVER_ERROR,
+                                     GUPNP_SERVER_ERROR_INVALID_RESPONSE,
+                                     "Invalid Fault");
+
+                        xmlFreeDoc (response);
+
+                        return;
+                }
+
+                /* Description */
+                desc = xml_util_get_child_element_content_glib (
+                        param,
+                        "errorDescription");
+                if (desc == NULL)
+                        desc = g_strdup (
+                                soup_message_get_reason_phrase (action->msg));
+
+                g_set_error_literal (&action->error,
+                                     GUPNP_CONTROL_ERROR,
+                                     code,
+                                     desc);
+
+                g_free (desc);
+
+                xmlFreeDoc (response);
+
+                return;
+        }
+
+        action->doc = response;
+}
+
 /**
  * gupnp_service_proxy_action_get_result_list:
  * @action: A #GUPnPServiceProxyAction handle
@@ -568,8 +569,6 @@ gupnp_service_proxy_action_get_result_list (GUPnPServiceProxyAction *action,
                                             GList                  **out_values,
                                             GError                 **error)
 {
-        xmlDoc *response;
-        xmlNode *params;
         GList *names;
         GList *types;
         GList *out_values_list;
@@ -586,8 +585,8 @@ gupnp_service_proxy_action_get_result_list (GUPnPServiceProxyAction *action,
         }
 
         /* Check response for errors and do initial parsing */
-        response = check_action_response (NULL, action, &params, &action->error);
-        if (response == NULL) {
+        gupnp_service_proxy_action_check_response (action);
+        if (action->error != NULL) {
                 g_propagate_error (error, g_error_copy (action->error));
 
                 return FALSE;
@@ -601,7 +600,7 @@ gupnp_service_proxy_action_get_result_list (GUPnPServiceProxyAction *action,
                 val = g_new0 (GValue, 1);
                 g_value_init (val, (GType) types->data);
 
-                read_out_parameter (names->data, val, params);
+                read_out_parameter (names->data, val, action->params);
 
                 out_values_list = g_list_append (out_values_list, val);
 
@@ -610,9 +609,6 @@ gupnp_service_proxy_action_get_result_list (GUPnPServiceProxyAction *action,
 
         *out_values = out_values_list;
 
-        /* Cleanup */
-        xmlFreeDoc (response);
-
         return TRUE;
 
 }
@@ -679,9 +675,6 @@ gupnp_service_proxy_action_get_result_hash (GUPnPServiceProxyAction *action,
                                             GHashTable              *hash,
                                             GError                 **error)
 {
-        xmlDoc *response;
-        xmlNode *params;
-
         g_return_val_if_fail (action, FALSE);
 
         /* Check for saved error from begin_action() */
@@ -692,18 +685,17 @@ gupnp_service_proxy_action_get_result_hash (GUPnPServiceProxyAction *action,
         }
 
         /* Check response for errors and do initial parsing */
-        response = check_action_response (NULL, action, &params, &action->error);
-        if (response == NULL) {
+        gupnp_service_proxy_action_check_response (action);
+        if (action->error != NULL) {
                 g_propagate_error (error, g_error_copy (action->error));
 
                 return FALSE;
         }
 
         /* Read arguments */
-        g_hash_table_foreach (hash, (GHFunc) read_out_parameter, params);
-
-        /* Cleanup */
-        xmlFreeDoc (response);
+        g_hash_table_foreach (hash,
+                              (GHFunc) read_out_parameter,
+                              action->params);
 
         return TRUE;
 
diff --git a/libgupnp/gupnp-service-proxy.c b/libgupnp/gupnp-service-proxy.c
index 60b290e..3c08c93 100644
--- a/libgupnp/gupnp-service-proxy.c
+++ b/libgupnp/gupnp-service-proxy.c
@@ -14,14 +14,16 @@
 #include <locale.h>
 #include <errno.h>
 
-#include "gupnp-service-proxy.h"
-#include "gupnp-service-proxy-action-private.h"
+#include "gena-protocol.h"
 #include "gupnp-context-private.h"
+#include "gupnp-error-private.h"
 #include "gupnp-error.h"
+#include "gupnp-service-proxy.h"
+#include "gupnp-service-proxy-action-private.h"
 #include "gupnp-types.h"
-#include "gena-protocol.h"
-#include "http-headers.h"
 #include "gvalue-util.h"
+#include "http-headers.h"
+#include "xml-util.h"
 
 struct _GUPnPServiceProxyPrivate {
         gboolean subscribed;
@@ -466,7 +468,15 @@ action_task_got_response (GObject *source,
                 break;
 
         default:
-                g_task_return_pointer (task, g_task_get_task_data (task), NULL);
+                gupnp_service_proxy_action_check_response (action);
+                if (action->error != NULL) {
+                        g_task_return_error (task,
+                                             g_error_copy (action->error));
+                } else {
+                        g_task_return_pointer (task,
+                                               g_task_get_task_data (task),
+                                               NULL);
+                }
 
                 g_object_unref (task);
 


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