libsoup r1029 - in branches/libsoup-2.4: . docs/reference libsoup
- From: danw svn gnome org
- To: svn-commits-list gnome org
- Subject: libsoup r1029 - in branches/libsoup-2.4: . docs/reference libsoup
- Date: Mon, 14 Jan 2008 14:10:30 +0000 (GMT)
Author: danw
Date: Mon Jan 14 14:10:30 2008
New Revision: 1029
URL: http://svn.gnome.org/viewvc/libsoup?rev=1029&view=rev
Log:
* libsoup/soup-logger.c: New HTTP debug logging object. (Based on
E2K_DEBUG and its clones.)
* libsoup/soup-message.c (soup_message_class_init)
(soup_message_add_header_handler)
(soup_message_add_status_code_handler): Change things around a
little; remove the "requeuing or cancelling the message stops
signal emission" rule, and instead make that be a feature of just
the header and status code handlers. (Makes the basic signal
handlers behave more predictably.)
Added:
branches/libsoup-2.4/libsoup/soup-logger.c
branches/libsoup-2.4/libsoup/soup-logger.h
Modified:
branches/libsoup-2.4/ChangeLog
branches/libsoup-2.4/docs/reference/libsoup-docs.sgml
branches/libsoup-2.4/docs/reference/libsoup-sections.txt
branches/libsoup-2.4/libsoup/Makefile.am
branches/libsoup-2.4/libsoup/soup-message.c
branches/libsoup-2.4/libsoup/soup.h
Modified: branches/libsoup-2.4/docs/reference/libsoup-docs.sgml
==============================================================================
--- branches/libsoup-2.4/docs/reference/libsoup-docs.sgml (original)
+++ branches/libsoup-2.4/docs/reference/libsoup-docs.sgml Mon Jan 14 14:10:30 2008
@@ -20,6 +20,7 @@
<xi:include href="xml/soup-auth-domain.xml"/>
<xi:include href="xml/soup-auth-domain-basic.xml"/>
<xi:include href="xml/soup-auth-domain-digest.xml"/>
+ <xi:include href="xml/soup-logger.xml"/>
<xi:include href="xml/soup-message.xml"/>
<xi:include href="xml/soup-message-headers.xml"/>
<xi:include href="xml/soup-message-body.xml"/>
Modified: branches/libsoup-2.4/docs/reference/libsoup-sections.txt
==============================================================================
--- branches/libsoup-2.4/docs/reference/libsoup-sections.txt (original)
+++ branches/libsoup-2.4/docs/reference/libsoup-sections.txt Mon Jan 14 14:10:30 2008
@@ -561,3 +561,29 @@
<SUBSECTION Private>
soup_byte_array_get_type
</SECTION>
+
+<SECTION>
+<FILE>soup-logger</FILE>
+<TITLE>SoupLogger</TITLE>
+SoupLogger
+SoupLoggerLogLevel
+soup_logger_new
+soup_logger_attach
+soup_logger_detach
+<SUBSECTION>
+SoupLoggerFilter
+soup_logger_set_request_filter
+soup_logger_set_response_filter
+<SUBSECTION>
+SoupLoggerPrinter
+soup_logger_set_printer
+<SUBSECTION Standard>
+SoupLoggerClass
+soup_logger_get_type
+SOUP_IS_LOGGER
+SOUP_IS_LOGGER_CLASS
+SOUP_LOGGER
+SOUP_LOGGER_CLASS
+SOUP_LOGGER_GET_CLASS
+SOUP_TYPE_LOGGER
+</SECTION>
Modified: branches/libsoup-2.4/libsoup/Makefile.am
==============================================================================
--- branches/libsoup-2.4/libsoup/Makefile.am (original)
+++ branches/libsoup-2.4/libsoup/Makefile.am Mon Jan 14 14:10:30 2008
@@ -55,6 +55,7 @@
soup-date.h \
soup-form.h \
soup-headers.h \
+ soup-logger.h \
soup-message.h \
soup-message-body.h \
soup-message-headers.h \
@@ -115,6 +116,7 @@
soup-form.c \
soup-gnutls.c \
soup-headers.c \
+ soup-logger.c \
soup-md5-utils.h \
soup-md5-utils.c \
soup-message.c \
Added: branches/libsoup-2.4/libsoup/soup-logger.c
==============================================================================
--- (empty file)
+++ branches/libsoup-2.4/libsoup/soup-logger.c Mon Jan 14 14:10:30 2008
@@ -0,0 +1,638 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * soup-logger.c
+ *
+ * Copyright (C) 2001-2004 Novell, Inc.
+ * Copyright (C) 2008 Red Hat, Inc.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <string.h>
+
+#include "soup-logger.h"
+#include "soup-message.h"
+#include "soup-uri.h"
+
+/**
+ * SECTION:soup-logger
+ * @short_description: Debug logging support
+ *
+ * #SoupLogger watches a #SoupSession and logs the HTTP traffic that
+ * it generates, for debugging purposes. Many applications use an
+ * environment variable to determine whether or not to use
+ * #SoupLogger, and to determine the amount of debugging output.
+ *
+ * To use #SoupLogger, first create a logger with soup_logger_new(),
+ * optionally configure it with soup_logger_set_request_filter(),
+ * soup_logger_set_response_filter(), and soup_logger_set_printer(),
+ * and then attach it to a session (or multiple sessions) with
+ * soup_logger_attach().
+ *
+ * By default, the debugging output is sent to %stdout, and looks
+ * something like:
+ *
+ * <informalexample><screen>
+ * > POST /unauth HTTP/1.1
+ * > Soup-Debug-Timestamp: 1200171744
+ * > Soup-Debug: session 1 (0x612190), msg 1 (0x617000), conn 1 (0x612220)
+ * > Host: localhost
+ * > Content-Type: text/plain
+ * > Connection: close
+ * >
+ * > This is a test.
+ *
+ * < HTTP/1.1 201 Created
+ * < Soup-Debug-Timestamp: 1200171744
+ * < Soup-Debug: msg 1 (0x617000)
+ * < Date: Sun, 12 Jan 2008 21:02:24 GMT
+ * < Content-Length: 0
+ * </screen></informalexample>
+ *
+ * The <literal>Soup-Debug-Timestamp</literal> line gives the time (as
+ * a #time_t) when the request was sent, or the response fully
+ * received.
+ *
+ * The <literal>Soup-Debug</literal> line gives further debugging
+ * information about the #SoupSession, #SoupMessage, and #SoupSocket
+ * involved; the hex numbers are the addresses of the objects in
+ * question (which may be useful if you are running in a debugger).
+ * The decimal IDs are simply counters that uniquely identify objects
+ * across the lifetime of the #SoupLogger. In particular, this can be
+ * used to identify when multiple messages are sent across the same
+ * connection.
+ **/
+
+G_DEFINE_TYPE (SoupLogger, soup_logger, G_TYPE_OBJECT)
+
+typedef struct {
+ /* We use a mutex so that if requests are being run in
+ * multiple threads, we don't mix up the output.
+ */
+ GMutex *lock;
+
+ GQuark tag;
+ GHashTable *ids;
+
+ SoupLoggerLogLevel level;
+ int max_body_size;
+
+ SoupLoggerFilter request_filter;
+ gpointer request_filter_data;
+ GDestroyNotify request_filter_dnotify;
+
+ SoupLoggerFilter response_filter;
+ gpointer response_filter_data;
+ GDestroyNotify response_filter_dnotify;
+
+ SoupLoggerPrinter printer;
+ gpointer printer_data;
+ GDestroyNotify printer_dnotify;
+ char direction;
+} SoupLoggerPrivate;
+#define SOUP_LOGGER_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), SOUP_TYPE_LOGGER, SoupLoggerPrivate))
+
+static void
+soup_logger_init (SoupLogger *logger)
+{
+ SoupLoggerPrivate *priv = SOUP_LOGGER_GET_PRIVATE (logger);
+
+ priv->lock = g_mutex_new ();
+ priv->tag = g_quark_from_static_string (g_strdup_printf ("SoupLogger-%p", logger));
+ priv->ids = g_hash_table_new (NULL, NULL);
+}
+
+static void
+finalize (GObject *object)
+{
+ SoupLoggerPrivate *priv = SOUP_LOGGER_GET_PRIVATE (object);
+
+ g_hash_table_destroy (priv->ids);
+
+ if (priv->request_filter_dnotify)
+ priv->request_filter_dnotify (priv->request_filter_data);
+ if (priv->response_filter_dnotify)
+ priv->response_filter_dnotify (priv->response_filter_data);
+ if (priv->printer_dnotify)
+ priv->printer_dnotify (priv->printer_data);
+
+ g_mutex_free (priv->lock);
+
+ G_OBJECT_CLASS (soup_logger_parent_class)->finalize (object);
+}
+
+static void
+soup_logger_class_init (SoupLoggerClass *logger_class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (logger_class);
+
+ g_type_class_add_private (logger_class, sizeof (SoupLoggerPrivate));
+
+ object_class->finalize = finalize;
+}
+
+/**
+ * SoupLoggerLogLevel:
+ * @SOUP_LOGGER_LOG_NONE: No logging
+ * @SOUP_LOGGER_LOG_MINIMAL: Log the Request-Line or Status-Line and
+ * the Soup-Debug pseudo-headers
+ * @SOUP_LOGGER_LOG_HEADERS: Log the full request/response headers
+ * @SOUP_LOGGER_LOG_BODY: Log the full headers and request/response
+ * bodies.
+ *
+ * Describes the level of logging output to provide.
+ **/
+
+/**
+ * soup_logger_new:
+ * @level: the debug level
+ * @max_body_size: the maximum body size to output, or -1
+ *
+ * Creates a new #SoupLogger with the given debug level. If @level is
+ * %SOUP_LOGGER_LOG_BODY, @max_body_size gives the maximum number of
+ * bytes of the body that will be logged. (-1 means "no limit".)
+ *
+ * If you need finer control over what message parts are and aren't
+ * logged, use soup_logger_set_request_filter() and
+ * soup_logger_set_response_filter().
+ **/
+SoupLogger *
+soup_logger_new (SoupLoggerLogLevel level, int max_body_size)
+{
+ SoupLogger *logger;
+ SoupLoggerPrivate *priv;
+
+ logger = g_object_new (SOUP_TYPE_LOGGER, NULL);
+
+ priv = SOUP_LOGGER_GET_PRIVATE (logger);
+ priv->level = level;
+ priv->max_body_size = max_body_size;
+
+ return logger;
+}
+
+/**
+ * SoupLoggerFilter:
+ * @logger: the #SoupLogger
+ * @msg: the message being logged
+ * @user_data: the data passed to soup_logger_set_request_filter()
+ * or soup_logger_set_response_filter()
+ *
+ * The prototype for a logging filter. The filter callback will be
+ * invoked for each request or response, and should analyze it and
+ * return a #SoupLoggerLogLevel value indicating how much of the
+ * message to log. Eg, it might choose between %SOUP_LOGGER_LOG_BODY
+ * and %SOUP_LOGGER_LOG_HEADERS depending on the Content-Type.
+ *
+ * Return value: a #SoupLoggerLogLevel value indicating how much of
+ * the message to log
+ **/
+
+/**
+ * soup_logger_set_request_filter:
+ * @logger: a #SoupLogger
+ * @request_filter: the callback for request debugging
+ * @filter_data: data to pass to the callback
+ * @destroy: a #GDestroyNotify to free @filter_data
+ *
+ * Sets up a filter to determine the log level for a given request.
+ * For each HTTP request @logger will invoke @request_filter to
+ * determine how much (if any) of that request to log. (If you do not
+ * set a request filter, @logger will just always log requests at the
+ * level passed to soup_logger_new().)
+ **/
+void
+soup_logger_set_request_filter (SoupLogger *logger,
+ SoupLoggerFilter request_filter,
+ gpointer filter_data,
+ GDestroyNotify destroy)
+{
+ SoupLoggerPrivate *priv = SOUP_LOGGER_GET_PRIVATE (logger);
+
+ priv->request_filter = request_filter;
+ priv->request_filter_data = filter_data;
+ priv->request_filter_dnotify = destroy;
+}
+
+/**
+ * soup_logger_set_response_filter:
+ * @logger: a #SoupLogger
+ * @response_filter: the callback for response debugging
+ * @filter_data: data to pass to the callback
+ * @destroy: a #GDestroyNotify to free @filter_data
+ *
+ * Sets up a filter to determine the log level for a given response.
+ * For each HTTP response @logger will invoke @response_filter to
+ * determine how much (if any) of that response to log. (If you do not
+ * set a response filter, @logger will just always log responses at
+ * the level passed to soup_logger_new().)
+ **/
+void
+soup_logger_set_response_filter (SoupLogger *logger,
+ SoupLoggerFilter response_filter,
+ gpointer filter_data,
+ GDestroyNotify destroy)
+{
+ SoupLoggerPrivate *priv = SOUP_LOGGER_GET_PRIVATE (logger);
+
+ priv->response_filter = response_filter;
+ priv->response_filter_data = filter_data;
+ priv->response_filter_dnotify = destroy;
+}
+
+/**
+ * SoupLoggerPrinter:
+ * @logger: the #SoupLogger
+ * @level: the level of the information being printed.
+ * @direction: a single-character prefix to @data
+ * @data: data to print
+ * @user_data: the data passed to soup_logger_set_printer()
+ *
+ * The prototype for a custom printing callback.
+ *
+ * @level indicates what kind of information is being printed. Eg, it
+ * will be %SOUP_LOGGER_LOG_HEADERS if @data is header data.
+ *
+ * @direction is either '<', '>', or ' ', and @data is the single line
+ * to print; the printer is expected to add a terminating newline.
+ *
+ * To get the effect of the default printer, you would do:
+ *
+ * <informalexample><programlisting>
+ * printf ("%c %s\n", direction, data);
+ * </programlisting></informalexample>
+ **/
+
+/**
+ * soup_logger_set_printer:
+ * @logger: a #SoupLogger
+ * @printer: the callback for printing logging output
+ * @printer_data: data to pass to the callback
+ * @destroy: a #GDestroyNotify to free @printer_data
+ *
+ * Sets up an alternate log printing routine, if you don't want
+ * the log to go to %stdout.
+ **/
+void
+soup_logger_set_printer (SoupLogger *logger,
+ SoupLoggerPrinter printer,
+ gpointer printer_data,
+ GDestroyNotify destroy)
+{
+ SoupLoggerPrivate *priv = SOUP_LOGGER_GET_PRIVATE (logger);
+
+ priv->printer = printer;
+ priv->printer_data = printer_data;
+ priv->printer_dnotify = destroy;
+}
+
+static guint
+soup_logger_get_id (SoupLogger *logger, gpointer object)
+{
+ SoupLoggerPrivate *priv = SOUP_LOGGER_GET_PRIVATE (logger);
+
+ return GPOINTER_TO_UINT (g_object_get_qdata (object, priv->tag));
+}
+
+static guint
+soup_logger_set_id (SoupLogger *logger, gpointer object)
+{
+ SoupLoggerPrivate *priv = SOUP_LOGGER_GET_PRIVATE (logger);
+ gpointer klass = G_OBJECT_GET_CLASS (object);
+ gpointer id;
+
+ id = g_hash_table_lookup (priv->ids, klass);
+ id = (char *)id + 1;
+ g_hash_table_insert (priv->ids, klass, id);
+
+ g_object_set_qdata (object, priv->tag, id);
+ return GPOINTER_TO_UINT (id);
+}
+
+static void
+soup_logger_clear_id (SoupLogger *logger, gpointer object)
+{
+ SoupLoggerPrivate *priv = SOUP_LOGGER_GET_PRIVATE (logger);
+
+ g_object_set_qdata (object, priv->tag, NULL);
+}
+
+static void request_started (SoupSession *session, SoupMessage *msg,
+ SoupSocket *socket, gpointer user_data);
+
+/**
+ * soup_logger_attach:
+ * @logger: a #SoupLogger
+ * @session: a #SoupSession
+ *
+ * Sets @logger to watch @session and print debug information for
+ * its messages.
+ *
+ * (The session will take a reference on @logger, which will be
+ * removed when you call soup_logger_detach(), or when the session is
+ * destroyed.)
+ **/
+void
+soup_logger_attach (SoupLogger *logger,
+ SoupSession *session)
+{
+ SoupLoggerPrivate *priv = SOUP_LOGGER_GET_PRIVATE (logger);
+
+ if (!soup_logger_get_id (logger, session))
+ soup_logger_set_id (logger, session);
+ g_signal_connect (session, "request_started",
+ G_CALLBACK (request_started), logger);
+
+ g_object_set_qdata_full (G_OBJECT (session), priv->tag,
+ g_object_ref (logger),
+ g_object_unref);
+}
+
+/**
+ * soup_logger_detach:
+ * @logger: a #SoupLogger
+ * @session: a #SoupSession
+ *
+ * Stops @logger from watching @session.
+ **/
+void
+soup_logger_detach (SoupLogger *logger,
+ SoupSession *session)
+{
+ SoupLoggerPrivate *priv = SOUP_LOGGER_GET_PRIVATE (logger);
+
+ g_signal_handlers_disconnect_by_func (session, request_started, logger);
+
+ g_object_set_qdata (G_OBJECT (session), priv->tag, NULL);
+}
+
+static void
+soup_logger_print (SoupLogger *logger, SoupLoggerLogLevel level,
+ const char *format, ...)
+{
+ SoupLoggerPrivate *priv = SOUP_LOGGER_GET_PRIVATE (logger);
+ va_list args;
+ char *data, *line, *end;
+
+ va_start (args, format);
+ data = g_strdup_vprintf (format, args);
+ va_end (args);
+
+ if (level == SOUP_LOGGER_LOG_BODY && priv->max_body_size > 0) {
+ if (strlen (data) > priv->max_body_size + 6)
+ strcpy (data + priv->max_body_size, "\n[...]");
+ }
+
+ line = data;
+ do {
+ end = strchr (line, '\n');
+ if (end)
+ *end = '\0';
+ if (priv->printer) {
+ priv->printer (logger, level, priv->direction,
+ line, priv->printer_data);
+ } else
+ printf ("%c %s\n", priv->direction, line);
+
+ line = end + 1;
+ } while (end && *line);
+
+ g_free (data);
+}
+
+static void
+print_header (const char *name, const char *value, gpointer logger)
+{
+ if (!g_ascii_strcasecmp (name, "Authorization") &&
+ !g_ascii_strncasecmp (value, "Basic ", 6)) {
+ char *decoded, *p;
+ gsize len;
+
+ decoded = (char *)g_base64_decode (value + 6, &len);
+ if (!decoded)
+ decoded = g_strdup (value);
+ p = strchr (decoded, ':');
+ if (p) {
+ while (++p < decoded + len)
+ *p = '*';
+ }
+ soup_logger_print (logger, SOUP_LOGGER_LOG_HEADERS,
+ "%s: Basic [%.*s]", name, len, decoded);
+ g_free (decoded);
+ } else {
+ soup_logger_print (logger, SOUP_LOGGER_LOG_HEADERS,
+ "%s: %s", name, value);
+ }
+}
+
+static void
+print_request (SoupLogger *logger, SoupMessage *msg,
+ SoupSession *session, SoupSocket *socket,
+ gboolean restarted)
+{
+ SoupLoggerPrivate *priv = SOUP_LOGGER_GET_PRIVATE (logger);
+ SoupLoggerLogLevel log_level;
+ SoupURI *uri;
+
+ if (priv->request_filter) {
+ log_level = priv->request_filter (logger, msg,
+ priv->request_filter_data);
+ } else
+ log_level = priv->level;
+
+ if (log_level == SOUP_LOGGER_LOG_NONE)
+ return;
+
+ priv->direction = '>';
+
+ uri = soup_message_get_uri (msg);
+ if (msg->method == SOUP_METHOD_CONNECT) {
+ soup_logger_print (logger, SOUP_LOGGER_LOG_MINIMAL,
+ "CONNECT %s:%u HTTP/1.%d",
+ uri->host, uri->port,
+ soup_message_get_http_version (msg));
+ } else {
+ soup_logger_print (logger, SOUP_LOGGER_LOG_MINIMAL,
+ "%s %s%s%s HTTP/1.%d",
+ msg->method, uri->path,
+ uri->query ? "?" : "",
+ uri->query ? uri->query : "",
+ soup_message_get_http_version (msg));
+ }
+
+ soup_logger_print (logger, SOUP_LOGGER_LOG_MINIMAL,
+ "Soup-Debug-Timestamp: %lu",
+ (unsigned long)time (0));
+ soup_logger_print (logger, SOUP_LOGGER_LOG_MINIMAL,
+ "Soup-Debug: session %u (%p), msg %u (%p), conn %u (%p)%s",
+ soup_logger_get_id (logger, session), session,
+ soup_logger_get_id (logger, msg), msg,
+ soup_logger_get_id (logger, socket), socket,
+ restarted ? ", restarted" : "");
+
+ if (log_level == SOUP_LOGGER_LOG_MINIMAL)
+ return;
+
+ print_header ("Host", uri->host, logger);
+ soup_message_headers_foreach (msg->request_headers,
+ print_header, logger);
+ if (log_level == SOUP_LOGGER_LOG_HEADERS)
+ return;
+
+ if (msg->request_body->length) {
+ SoupBuffer *request;
+
+ request = soup_message_body_flatten (msg->request_body);
+ soup_buffer_free (request);
+
+ if (soup_message_headers_get_expectations (msg->request_headers) != SOUP_EXPECTATION_CONTINUE) {
+ soup_logger_print (logger, SOUP_LOGGER_LOG_BODY,
+ "\n%s", msg->request_body->data);
+ }
+ }
+}
+
+static void
+print_response (SoupLogger *logger, SoupMessage *msg)
+{
+ SoupLoggerPrivate *priv = SOUP_LOGGER_GET_PRIVATE (logger);
+ SoupLoggerLogLevel log_level;
+
+ if (priv->response_filter) {
+ log_level = priv->response_filter (logger, msg,
+ priv->response_filter_data);
+ } else
+ log_level = priv->level;
+
+ if (log_level == SOUP_LOGGER_LOG_NONE)
+ return;
+
+ priv->direction = '<';
+
+ soup_logger_print (logger, SOUP_LOGGER_LOG_MINIMAL,
+ "HTTP/1.%d %u %s\n",
+ soup_message_get_http_version (msg),
+ msg->status_code, msg->reason_phrase);
+
+ soup_logger_print (logger, SOUP_LOGGER_LOG_MINIMAL,
+ "Soup-Debug-Timestamp: %lu",
+ (unsigned long)time (0));
+ soup_logger_print (logger, SOUP_LOGGER_LOG_MINIMAL,
+ "Soup-Debug: msg %u (%p)",
+ soup_logger_get_id (logger, msg), msg);
+
+ if (log_level == SOUP_LOGGER_LOG_MINIMAL)
+ return;
+
+ soup_message_headers_foreach (msg->response_headers,
+ print_header, logger);
+ if (log_level == SOUP_LOGGER_LOG_HEADERS)
+ return;
+
+ if (msg->response_body->length) {
+ soup_logger_print (logger, SOUP_LOGGER_LOG_BODY,
+ "\n%s", msg->response_body->data);
+ }
+}
+
+static void
+got_informational (SoupMessage *msg, gpointer user_data)
+{
+ SoupLogger *logger = user_data;
+ SoupLoggerPrivate *priv = SOUP_LOGGER_GET_PRIVATE (logger);
+
+ g_mutex_lock (priv->lock);
+
+ print_response (logger, msg);
+ priv->direction = ' ';
+ soup_logger_print (logger, SOUP_LOGGER_LOG_MINIMAL, "");
+
+ if (msg->status_code == SOUP_STATUS_CONTINUE && msg->request_body->data) {
+ SoupLoggerLogLevel log_level;
+
+ priv->direction = '>';
+ soup_logger_print (logger, SOUP_LOGGER_LOG_MINIMAL,
+ "[Now sending request body...]");
+
+ if (priv->request_filter) {
+ log_level = priv->request_filter (logger, msg,
+ priv->request_filter_data);
+ } else
+ log_level = priv->level;
+
+ if (log_level == SOUP_LOGGER_LOG_BODY) {
+ soup_logger_print (logger, SOUP_LOGGER_LOG_BODY,
+ "%s", msg->request_body->data);
+ }
+
+ priv->direction = ' ';
+ soup_logger_print (logger, SOUP_LOGGER_LOG_MINIMAL, "");
+ }
+
+ g_mutex_unlock (priv->lock);
+}
+
+static void
+got_body (SoupMessage *msg, gpointer user_data)
+{
+ SoupLogger *logger = user_data;
+ SoupLoggerPrivate *priv = SOUP_LOGGER_GET_PRIVATE (logger);
+
+ g_mutex_lock (priv->lock);
+
+ print_response (logger, msg);
+ priv->direction = ' ';
+ soup_logger_print (logger, SOUP_LOGGER_LOG_MINIMAL, "");
+
+ g_mutex_unlock (priv->lock);
+}
+
+static void
+finished_handler (SoupMessage *msg, gpointer user_data)
+{
+ SoupLogger *logger = user_data;
+
+ g_signal_handlers_disconnect_by_func (msg, got_informational, logger);
+ g_signal_handlers_disconnect_by_func (msg, got_body, logger);
+ g_signal_handlers_disconnect_by_func (msg, finished_handler, logger);
+
+ soup_logger_clear_id (logger, msg);
+}
+
+static void
+request_started (SoupSession *session, SoupMessage *msg,
+ SoupSocket *socket, gpointer user_data)
+{
+ SoupLogger *logger = user_data;
+ SoupLoggerPrivate *priv = SOUP_LOGGER_GET_PRIVATE (logger);
+ gboolean restarted;
+ guint msg_id;
+
+ msg_id = soup_logger_get_id (logger, msg);
+ if (msg_id)
+ restarted = TRUE;
+ else {
+ msg_id = soup_logger_set_id (logger, msg);
+ restarted = FALSE;
+
+ g_signal_connect (msg, "got-informational",
+ G_CALLBACK (got_informational),
+ logger);
+ g_signal_connect (msg, "got-body",
+ G_CALLBACK (got_body),
+ logger);
+ g_signal_connect (msg, "finished",
+ G_CALLBACK (finished_handler),
+ logger);
+ }
+
+ if (!soup_logger_get_id (logger, socket))
+ soup_logger_set_id (logger, socket);
+
+ print_request (logger, msg, session, socket, restarted);
+ priv->direction = ' ';
+ soup_logger_print (logger, SOUP_LOGGER_LOG_MINIMAL, "");
+}
Added: branches/libsoup-2.4/libsoup/soup-logger.h
==============================================================================
--- (empty file)
+++ branches/libsoup-2.4/libsoup/soup-logger.h Mon Jan 14 14:10:30 2008
@@ -0,0 +1,71 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2008 Red Hat, Inc.
+ */
+
+#ifndef SOUP_LOGGER_H
+#define SOUP_LOGGER_H 1
+
+#include <libsoup/soup-types.h>
+
+#define SOUP_TYPE_LOGGER (soup_logger_get_type ())
+#define SOUP_LOGGER(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SOUP_TYPE_LOGGER, SoupLogger))
+#define SOUP_LOGGER_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), SOUP_TYPE_LOGGER, SoupLoggerClass))
+#define SOUP_IS_LOGGER(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SOUP_TYPE_LOGGER))
+#define SOUP_IS_LOGGER_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), SOUP_TYPE_LOGGER))
+#define SOUP_LOGGER_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), SOUP_TYPE_LOGGER, SoupLoggerClass))
+
+typedef struct SoupLogger SoupLogger;
+typedef struct SoupLoggerClass SoupLoggerClass;
+
+typedef enum {
+ SOUP_LOGGER_LOG_NONE,
+ SOUP_LOGGER_LOG_MINIMAL,
+ SOUP_LOGGER_LOG_HEADERS,
+ SOUP_LOGGER_LOG_BODY
+} SoupLoggerLogLevel;
+
+typedef SoupLoggerLogLevel (*SoupLoggerFilter) (SoupLogger *logger,
+ SoupMessage *msg,
+ gpointer user_data);
+
+typedef void (*SoupLoggerPrinter) (SoupLogger *logger,
+ SoupLoggerLogLevel level,
+ char direction,
+ const char *data,
+ gpointer user_data);
+
+struct SoupLogger {
+ GObject parent;
+
+};
+
+struct SoupLoggerClass {
+ GObjectClass parent_class;
+
+};
+
+GType soup_logger_get_type (void);
+
+SoupLogger *soup_logger_new (SoupLoggerLogLevel log_level,
+ int max_body_size);
+void soup_logger_attach (SoupLogger *logger,
+ SoupSession *session);
+void soup_logger_detach (SoupLogger *logger,
+ SoupSession *session);
+
+void soup_logger_set_request_filter (SoupLogger *logger,
+ SoupLoggerFilter request_filter,
+ gpointer filter_data,
+ GDestroyNotify destroy);
+void soup_logger_set_response_filter (SoupLogger *logger,
+ SoupLoggerFilter response_filter,
+ gpointer filter_data,
+ GDestroyNotify destroy);
+
+void soup_logger_set_printer (SoupLogger *logger,
+ SoupLoggerPrinter printer,
+ gpointer printer_data,
+ GDestroyNotify destroy);
+
+#endif /* SOUP_LOGGER_H */
Modified: branches/libsoup-2.4/libsoup/soup-message.c
==============================================================================
--- branches/libsoup-2.4/libsoup/soup-message.c (original)
+++ branches/libsoup-2.4/libsoup/soup-message.c Mon Jan 14 14:10:30 2008
@@ -87,12 +87,6 @@
LAST_PROP
};
-static void got_foo_signal_wrapper (GClosure *closure, GValue *return_value,
- guint n_param_values,
- const GValue *param_values,
- gpointer invocation_hint,
- gpointer marshal_data);
-
static void got_body (SoupMessage *req);
static void restarted (SoupMessage *req);
static void finished (SoupMessage *req);
@@ -241,8 +235,8 @@
* be erased after this signal is done.
*
* If you cancel or requeue @msg while processing this signal,
- * the signal emission will be stopped, and @msg's connection
- * will be closed.
+ * then the current HTTP I/O will be stopped after this signal
+ * emission finished, and @msg's connection will be closed.
**/
signals[GOT_INFORMATIONAL] =
g_signal_new ("got_informational",
@@ -250,7 +244,7 @@
G_SIGNAL_RUN_FIRST,
G_STRUCT_OFFSET (SoupMessageClass, got_informational),
NULL, NULL,
- got_foo_signal_wrapper,
+ soup_marshal_NONE__NONE,
G_TYPE_NONE, 0);
/**
@@ -268,11 +262,12 @@
* to connect to a subset of emissions of this signal.
*
* If you cancel or requeue @msg while processing this signal,
- * the signal emission will be stopped, and @msg's connection
- * will be closed. (If you need to requeue a message--eg,
- * after handling authentication or redirection--it is usually
- * better to requeue it from a #SoupMessage::got_body handler
- * rather than a #SoupMessage::got_header handler, so that the
+ * then the current HTTP I/O will be stopped after this signal
+ * emission finished, and @msg's connection will be closed.
+ * (If you need to requeue a message--eg, after handling
+ * authentication or redirection--it is usually better to
+ * requeue it from a #SoupMessage::got_body handler rather
+ * than a #SoupMessage::got_header handler, so that the
* existing HTTP connection can be reused.)
**/
signals[GOT_HEADERS] =
@@ -281,7 +276,7 @@
G_SIGNAL_RUN_FIRST,
G_STRUCT_OFFSET (SoupMessageClass, got_headers),
NULL, NULL,
- got_foo_signal_wrapper,
+ soup_marshal_NONE__NONE,
G_TYPE_NONE, 0);
/**
@@ -295,8 +290,8 @@
* the other side.
*
* If you cancel or requeue @msg while processing this signal,
- * the signal emission will be stopped, and @msg's connection
- * will be closed.
+ * then the current HTTP I/O will be stopped after this signal
+ * emission finished, and @msg's connection will be closed.
**/
signals[GOT_CHUNK] =
g_signal_new ("got_chunk",
@@ -304,7 +299,7 @@
G_SIGNAL_RUN_FIRST,
G_STRUCT_OFFSET (SoupMessageClass, got_chunk),
NULL, NULL,
- got_foo_signal_wrapper,
+ soup_marshal_NONE__BOXED,
G_TYPE_NONE, 1,
SOUP_TYPE_BUFFER);
@@ -320,13 +315,6 @@
* See also soup_message_add_header_handler() and
* soup_message_add_status_code_handler(), which can be used
* to connect to a subset of emissions of this signal.
- *
- * If you cancel or requeue @msg while processing this signal,
- * the signal emission will be stopped. However, unlike with
- * the other "got" signals, this will not necessarily result
- * in @msg's connection being closed, since the server is
- * already done with the message at this point and so the
- * connection is already ready for a new request.
**/
signals[GOT_BODY] =
g_signal_new ("got_body",
@@ -334,7 +322,7 @@
G_SIGNAL_RUN_FIRST,
G_STRUCT_OFFSET (SoupMessageClass, got_body),
NULL, NULL,
- got_foo_signal_wrapper,
+ soup_marshal_NONE__NONE,
G_TYPE_NONE, 0);
/**
@@ -650,33 +638,6 @@
g_signal_emit (msg, signals[WROTE_BODY], 0);
}
-/* Wraps the marshaller for got_informational, got_headers, got_chunk,
- * and got_body, and stops the emission afterward if the message has
- * been requeued. We can't use a GSignalAccumulator, because that
- * doesn't have access to the object it's accumulating for. Hrmph.
- */
-static void
-got_foo_signal_wrapper (GClosure *closure, GValue *return_value,
- guint n_param_values, const GValue *param_values,
- gpointer invocation_hint, gpointer marshal_data)
-{
- SoupMessage *msg = g_value_get_object (¶m_values[0]);
- GSignalInvocationHint *hint = invocation_hint;
-
- if (hint->signal_id == signals[GOT_CHUNK]) {
- soup_marshal_NONE__BOXED (closure, return_value,
- n_param_values, param_values,
- invocation_hint, marshal_data);
- } else {
- soup_marshal_NONE__NONE (closure, return_value,
- n_param_values, param_values,
- invocation_hint, marshal_data);
- }
-
- if (SOUP_MESSAGE_IS_STARTING (msg))
- g_signal_stop_emission (msg, hint->signal_id, hint->detail);
-}
-
/**
* soup_message_got_informational:
* @msg: a #SoupMessage
@@ -788,18 +749,10 @@
g_signal_emit (msg, signals[FINISHED], 0);
}
-typedef struct {
- SoupMessage *msg;
- char *header_name;
-} SoupMessageHeaderHandlerData;
-
static void
-header_handler_free (gpointer data, GClosure *closure)
+header_handler_free (gpointer header_name, GClosure *closure)
{
- SoupMessageHeaderHandlerData *hhd = data;
-
- g_free (hhd->header_name);
- g_slice_free (SoupMessageHeaderHandlerData, hhd);
+ g_free (header_name);
}
static void
@@ -807,16 +760,22 @@
guint n_param_values, const GValue *param_values,
gpointer invocation_hint, gpointer marshal_data)
{
- SoupMessageHeaderHandlerData *hhd = marshal_data;
+ SoupMessage *msg = g_value_get_object (¶m_values[0]);
+ SoupMessagePrivate *priv = SOUP_MESSAGE_GET_PRIVATE (msg);
+ const char *header_name = marshal_data;
SoupMessageHeaders *hdrs;
- /* If status_code is 0, we're still processing the request
- * side; if it's not, we're processing the response side.
+ if (priv->io_status != SOUP_MESSAGE_IO_STATUS_RUNNING)
+ return;
+
+ /* If status_code is SOUP_STATUS_NONE, we're still processing
+ * the request side; if it's not, we're processing the
+ * response side.
*/
- hdrs = (hhd->msg->status_code == 0) ?
- hhd->msg->request_headers : hhd->msg->response_headers;
+ hdrs = (msg->status_code == SOUP_STATUS_NONE) ?
+ msg->request_headers : msg->response_headers;
- if (soup_message_headers_get (hdrs, hhd->header_name)) {
+ if (soup_message_headers_get (hdrs, header_name)) {
closure->marshal (closure, return_value, n_param_values,
param_values, invocation_hint,
((GCClosure *)closure)->callback);
@@ -832,8 +791,9 @@
* @user_data: data to pass to @handler_cb
*
* Adds a signal handler to @msg for @signal, as with
- * g_signal_connect(). However, @callback will only be run if @msg has
- * a header named @header.
+ * g_signal_connect(), but with two differences: the @callback will
+ * only be run if @msg has a header named @header, and it will only be
+ * run if no earlier handler cancelled or requeued the message.
*
* If @signal is one of the "got" signals (eg, "got_headers"), or
* "finished" or "restarted", then @header is matched against the
@@ -853,7 +813,7 @@
{
SoupMessagePrivate *priv;
GClosure *closure;
- SoupMessageHeaderHandlerData *hhd;
+ char *header_name;
g_return_val_if_fail (SOUP_IS_MESSAGE (msg), 0);
g_return_val_if_fail (signal != NULL, 0);
@@ -864,11 +824,11 @@
closure = g_cclosure_new (callback, user_data, NULL);
- hhd = g_slice_new (SoupMessageHeaderHandlerData);
- hhd->msg = msg;
- hhd->header_name = g_strdup (header);
- g_closure_set_meta_marshal (closure, hhd, header_handler_metamarshal);
- g_closure_add_finalize_notifier (closure, hhd, header_handler_free);
+ header_name = g_strdup (header);
+ g_closure_set_meta_marshal (closure, header_name,
+ header_handler_metamarshal);
+ g_closure_add_finalize_notifier (closure, header_name,
+ header_handler_free);
return g_signal_connect_closure (msg, signal, closure, FALSE);
}
@@ -879,8 +839,12 @@
gpointer invocation_hint, gpointer marshal_data)
{
SoupMessage *msg = g_value_get_object (¶m_values[0]);
+ SoupMessagePrivate *priv = SOUP_MESSAGE_GET_PRIVATE (msg);
guint status = GPOINTER_TO_UINT (marshal_data);
+ if (priv->io_status != SOUP_MESSAGE_IO_STATUS_RUNNING)
+ return;
+
if (msg->status_code == status) {
closure->marshal (closure, return_value, n_param_values,
param_values, invocation_hint,
@@ -897,8 +861,9 @@
* @user_data: data to pass to @handler_cb
*
* Adds a signal handler to @msg for @signal, as with
- * g_signal_connect(). However, @callback will only be run if @msg has
- * the status @status_code.
+ * g_signal_connect() but with two differences: the @callback will
+ * only be run if @msg has the status @status_code, and it will only
+ * be run if no earlier handler cancelled or requeued the message.
*
* @signal must be a signal that will be emitted after @msg's status
* is set. For a client #SoupMessage, this means it can't be a "wrote"
Modified: branches/libsoup-2.4/libsoup/soup.h
==============================================================================
--- branches/libsoup-2.4/libsoup/soup.h (original)
+++ branches/libsoup-2.4/libsoup/soup.h Mon Jan 14 14:10:30 2008
@@ -19,6 +19,7 @@
#include <libsoup/soup-enum-types.h>
#include <libsoup/soup-form.h>
#include <libsoup/soup-headers.h>
+#include <libsoup/soup-logger.h>
#include <libsoup/soup-message.h>
#include <libsoup/soup-method.h>
#include <libsoup/soup-misc.h>
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]