[gupnp] [PATCH 1/2] Enable gzip compression for large action response bodies



Enable transparent decoding of gzip compressed HTTP bodies in
libsoup and let GUPnPServiceProxy set the Accept-Encoding header
to "gzip".

Let GUPnPService check for the Accept-Encoding header and
optionally compress large (> 1024 bytes) repsonse bodies. This
saves some bandwidth and gives a noticeable speedup on slow
connections.
---
 libgupnp/gupnp-context.c       |    3 ++
 libgupnp/gupnp-service-proxy.c |    4 +++
 libgupnp/gupnp-service.c       |   44 +++++++++++++++++++++++++++------
 libgupnp/http-headers.c        |   53 ++++++++++++++++++++++++++++++++++++++++
 libgupnp/http-headers.h        |    5 ++++
 5 files changed, 101 insertions(+), 8 deletions(-)

diff --git a/libgupnp/gupnp-context.c b/libgupnp/gupnp-context.c
index 13fa0d3..457d37c 100644
--- a/libgupnp/gupnp-context.c
+++ b/libgupnp/gupnp-context.c
@@ -158,6 +158,9 @@ gupnp_context_constructor (GType                  type,
                                           SOUP_SESSION_FEATURE (logger));
         }
 
+        soup_session_add_feature_by_type (context->priv->session,
+                                          SOUP_TYPE_CONTENT_DECODER);
+
         return object;
 }
 
diff --git a/libgupnp/gupnp-service-proxy.c b/libgupnp/gupnp-service-proxy.c
index d5707fc..06008b5 100644
--- a/libgupnp/gupnp-service-proxy.c
+++ b/libgupnp/gupnp-service-proxy.c
@@ -700,6 +700,10 @@ begin_action_msg (GUPnPServiceProxy              *proxy,
         /* Specify language */
         http_request_set_accept_language (ret->msg);
 
+        /* Accept gzip encoding */
+        soup_message_headers_append (ret->msg->request_headers,
+				     "Accept-Encoding", "gzip");
+
         /* Set up envelope */
         ret->msg_str = xml_util_new_string ();
 
diff --git a/libgupnp/gupnp-service.c b/libgupnp/gupnp-service.c
index 64a5ca8..ee0c5a1 100644
--- a/libgupnp/gupnp-service.c
+++ b/libgupnp/gupnp-service.c
@@ -193,6 +193,7 @@ struct _GUPnPServiceAction {
         char         *name;
 
         SoupMessage  *msg;
+        gboolean      accept_gzip;
 
         GUPnPXMLDoc  *doc;
         xmlNode      *node;
@@ -252,7 +253,6 @@ static void
 finalize_action (GUPnPServiceAction *action)
 {
         SoupServer *server;
-        char *response_body;
 
         /* Embed action->response_str in a SOAP document */
         g_string_prepend (action->response_str,
@@ -273,13 +273,22 @@ finalize_action (GUPnPServiceAction *action)
                          "</s:Body>"
                          "</s:Envelope>");
 
-        response_body = g_string_free (action->response_str, FALSE);
+        soup_message_headers_replace (action->msg->response_headers,
+                                      "Content-Type",
+                                      "text/xml; charset=\"utf-8\"");
 
-        soup_message_set_response (action->msg,
-                                   "text/xml; charset=\"utf-8\"",
-                                   SOUP_MEMORY_TAKE,
-                                   response_body,
-                                   strlen (response_body));
+        if (action->accept_gzip && action->response_str->len > 1024) {
+                http_response_set_body_gzip (action->msg,
+                                             action->response_str->str,
+                                             action->response_str->len);
+                g_string_free (action->response_str, TRUE);
+        } else {
+                soup_message_body_append (action->msg->response_body,
+                                          SOUP_MEMORY_TAKE,
+                                          action->response_str->str,
+                                          action->response_str->len);
+                g_string_free (action->response_str, FALSE);
+        }
 
         /* Server header on response */
         soup_message_headers_append
@@ -873,6 +882,7 @@ control_server_handler (SoupServer        *server,
         xmlDoc *doc;
         xmlNode *action_node;
         const char *soap_action;
+        const char *accept_encoding;
         char *action_name;
         char *end;
         GUPnPServiceAction *action;
@@ -938,7 +948,7 @@ control_server_handler (SoupServer        *server,
         }
 
         /* Create action structure */
-        action = g_slice_new (GUPnPServiceAction);
+        action = g_slice_new0 (GUPnPServiceAction);
 
         action->ref_count    = 1;
         action->name         = g_strdup (action_name);
@@ -949,6 +959,24 @@ control_server_handler (SoupServer        *server,
                                                         soap_action);
         action->context      = g_object_ref (context);
 
+        /* Get accepted encodings */
+        accept_encoding = soup_message_headers_get_list (msg->request_headers,
+                                                         "Accept-Encoding");
+
+        if (accept_encoding) {
+                GSList *codings;
+
+                codings = soup_header_parse_quality_list (accept_encoding,
+                                                          NULL);
+                if (codings &&
+                    g_slist_find_custom (codings, "gzip",
+                                         (GCompareFunc) g_ascii_strcasecmp)) {
+                       action->accept_gzip = TRUE;
+                }
+
+                soup_header_free_list (codings);
+        }
+
         /* Tell soup server that response is not ready yet */
         soup_server_pause_message (server, msg);
 
diff --git a/libgupnp/http-headers.c b/libgupnp/http-headers.c
index 9b14868..4c06b77 100644
--- a/libgupnp/http-headers.c
+++ b/libgupnp/http-headers.c
@@ -354,3 +354,56 @@ http_response_set_content_range (SoupMessage  *msg,
 
         g_free (content_range);
 }
+
+/* Set Content-Encoding header to gzip and append compressed body */
+void
+http_response_set_body_gzip (SoupMessage *msg,
+                             const char  *body,
+                             const gsize  length)
+{
+        GZlibCompressor *compressor;
+        gboolean finished = FALSE;
+        gsize converted = 0;
+
+        soup_message_headers_append (msg->response_headers,
+                                     "Content-Encoding", "gzip");
+
+        compressor = g_zlib_compressor_new (G_ZLIB_COMPRESSOR_FORMAT_GZIP, -1);
+
+        while (! finished) {
+                GError *error = NULL;
+                char buf[65536];
+                gsize bytes_read = 0;
+                gsize bytes_written = 0;
+
+                switch (g_converter_convert (G_CONVERTER (compressor),
+                                             body + converted,
+                                             length - converted,
+                                             buf, sizeof (buf),
+                                             G_CONVERTER_INPUT_AT_END,
+                                             &bytes_read, &bytes_written,
+                                             &error)) {
+                case G_CONVERTER_ERROR:
+                        g_warning ("Error compressing response: %s",
+                                   error->message);
+                        g_error_free (error);
+                        g_object_unref (compressor);
+                        return;
+                case G_CONVERTER_CONVERTED:
+                        converted += bytes_read;
+                        break;
+                case G_CONVERTER_FINISHED:
+                        finished = TRUE;
+                        break;
+                case G_CONVERTER_FLUSHED:
+                        break;
+                }
+
+                if (bytes_written)
+                        soup_message_body_append (msg->response_body,
+                                                  SOUP_MEMORY_COPY,
+                                                  buf, bytes_written);
+        }
+
+        g_object_unref (compressor);
+}
diff --git a/libgupnp/http-headers.h b/libgupnp/http-headers.h
index 6dbaa94..507adf5 100644
--- a/libgupnp/http-headers.h
+++ b/libgupnp/http-headers.h
@@ -54,6 +54,11 @@ http_response_set_content_range  (SoupMessage  *message,
                                   gsize         length,
                                   gsize         total);
 
+G_GNUC_INTERNAL void
+http_response_set_body_gzip      (SoupMessage   *msg,
+                                  const char    *body,
+                                  const gsize    length);
+
 G_END_DECLS
 
 #endif /* __HTTP_HEADERS_H__ */
-- 
1.7.0.4

--
To unsubscribe send a mail to gupnp+unsubscribe\@o-hand.com



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