Commit 3da630da authored by Sven Neumann's avatar Sven Neumann Committed by Ross Burton
Browse files

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.
parent 9114ed82
......@@ -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;
}
......
......@@ -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 ();
......
......@@ -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);
......
......@@ -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);
}
......@@ -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__ */
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment