Blame libsoup/soup-logger.c

Packit Service ca3877
/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
Packit Service ca3877
/*
Packit Service ca3877
 * soup-logger.c
Packit Service ca3877
 *
Packit Service ca3877
 * Copyright (C) 2001-2004 Novell, Inc.
Packit Service ca3877
 * Copyright (C) 2008 Red Hat, Inc.
Packit Service ca3877
 * Copyright (C) 2013 Igalia, S.L.
Packit Service ca3877
 */
Packit Service ca3877
Packit Service ca3877
#ifdef HAVE_CONFIG_H
Packit Service ca3877
#include <config.h>
Packit Service ca3877
#endif
Packit Service ca3877
Packit Service ca3877
#include <stdio.h>
Packit Service ca3877
#include <string.h>
Packit Service ca3877
Packit Service ca3877
#include "soup-logger.h"
Packit Service ca3877
#include "soup-connection.h"
Packit Service ca3877
#include "soup-message-private.h"
Packit Service ca3877
#include "soup.h"
Packit Service ca3877
Packit Service ca3877
/**
Packit Service ca3877
 * SECTION:soup-logger
Packit Service ca3877
 * @short_description: Debug logging support
Packit Service ca3877
 *
Packit Service ca3877
 * #SoupLogger watches a #SoupSession and logs the HTTP traffic that
Packit Service ca3877
 * it generates, for debugging purposes. Many applications use an
Packit Service ca3877
 * environment variable to determine whether or not to use
Packit Service ca3877
 * #SoupLogger, and to determine the amount of debugging output.
Packit Service ca3877
 *
Packit Service ca3877
 * To use #SoupLogger, first create a logger with soup_logger_new(),
Packit Service ca3877
 * optionally configure it with soup_logger_set_request_filter(),
Packit Service ca3877
 * soup_logger_set_response_filter(), and soup_logger_set_printer(),
Packit Service ca3877
 * and then attach it to a session (or multiple sessions) with
Packit Service ca3877
 * soup_session_add_feature().
Packit Service ca3877
 *
Packit Service ca3877
 * By default, the debugging output is sent to
Packit Service ca3877
 * <literal>stdout</literal>, and looks something like:
Packit Service ca3877
 *
Packit Service ca3877
 * <informalexample><screen>
Packit Service ca3877
 * > POST /unauth HTTP/1.1
Packit Service ca3877
 * > Soup-Debug-Timestamp: 1200171744
Packit Service ca3877
 * > Soup-Debug: SoupSessionAsync 1 (0x612190), SoupMessage 1 (0x617000), SoupSocket 1 (0x612220)
Packit Service ca3877
 * > Host: localhost
Packit Service ca3877
 * > Content-Type: text/plain
Packit Service ca3877
 * > Connection: close
Packit Service ca3877
 * >
Packit Service ca3877
 * > This is a test.
Packit Service ca3877
 *
Packit Service ca3877
 * < HTTP/1.1 201 Created
Packit Service ca3877
 * < Soup-Debug-Timestamp: 1200171744
Packit Service ca3877
 * < Soup-Debug: SoupMessage 1 (0x617000)
Packit Service ca3877
 * < Date: Sun, 12 Jan 2008 21:02:24 GMT
Packit Service ca3877
 * < Content-Length: 0
Packit Service ca3877
 * </screen></informalexample>
Packit Service ca3877
 *
Packit Service ca3877
 * The <literal>Soup-Debug-Timestamp</literal> line gives the time (as
Packit Service ca3877
 * a <type>time_t</type>) when the request was sent, or the response fully
Packit Service ca3877
 * received.
Packit Service ca3877
 *
Packit Service ca3877
 * The <literal>Soup-Debug</literal> line gives further debugging
Packit Service ca3877
 * information about the #SoupSession, #SoupMessage, and #SoupSocket
Packit Service ca3877
 * involved; the hex numbers are the addresses of the objects in
Packit Service ca3877
 * question (which may be useful if you are running in a debugger).
Packit Service ca3877
 * The decimal IDs are simply counters that uniquely identify objects
Packit Service ca3877
 * across the lifetime of the #SoupLogger. In particular, this can be
Packit Service ca3877
 * used to identify when multiple messages are sent across the same
Packit Service ca3877
 * connection.
Packit Service ca3877
 *
Packit Service ca3877
 * Currently, the request half of the message is logged just before
Packit Service ca3877
 * the first byte of the request gets written to the network (from the
Packit Service ca3877
 * #SoupMessage::starting signal), which means that if you have
Packit Service ca3877
 * not made the complete request body available at that point, it will
Packit Service ca3877
 * not be logged.
Packit Service ca3877
 *
Packit Service ca3877
 * The response is logged just after the last byte of the response
Packit Service ca3877
 * body is read from the network (from the #SoupMessage::got_body or
Packit Service ca3877
 * #SoupMessage::got_informational signal), which means that the
Packit Service ca3877
 * #SoupMessage::got_headers signal, and anything triggered off it
Packit Service ca3877
 * (such as #SoupSession::authenticate) will be emitted
Packit Service ca3877
 * <emphasis>before</emphasis> the response headers are actually
Packit Service ca3877
 * logged.
Packit Service ca3877
 *
Packit Service ca3877
 * If the response doesn't happen to trigger the
Packit Service ca3877
 * #SoupMessage::got_body nor #SoupMessage::got_informational signals
Packit Service ca3877
 * due to, for example, a cancellation before receiving the last byte
Packit Service ca3877
 * of the response body, the response will still be logged on the
Packit Service ca3877
 * event of the #SoupMessage::finished signal.
Packit Service ca3877
 **/
Packit Service ca3877
Packit Service ca3877
typedef struct {
Packit Service ca3877
	/* We use a mutex so that if requests are being run in
Packit Service ca3877
	 * multiple threads, we don't mix up the output.
Packit Service ca3877
	 */
Packit Service ca3877
	GMutex             lock;
Packit Service ca3877
Packit Service ca3877
	GQuark              tag;
Packit Service ca3877
	GHashTable         *ids;
Packit Service ca3877
Packit Service ca3877
	SoupSession        *session;
Packit Service ca3877
	SoupLoggerLogLevel  level;
Packit Service ca3877
	int                 max_body_size;
Packit Service ca3877
Packit Service ca3877
	SoupLoggerFilter    request_filter;
Packit Service ca3877
	gpointer            request_filter_data;
Packit Service ca3877
	GDestroyNotify      request_filter_dnotify;
Packit Service ca3877
Packit Service ca3877
	SoupLoggerFilter    response_filter;
Packit Service ca3877
	gpointer            response_filter_data;
Packit Service ca3877
	GDestroyNotify      response_filter_dnotify;
Packit Service ca3877
Packit Service ca3877
	SoupLoggerPrinter   printer;
Packit Service ca3877
	gpointer            printer_data;
Packit Service ca3877
	GDestroyNotify      printer_dnotify;
Packit Service ca3877
} SoupLoggerPrivate;
Packit Service ca3877
Packit Service ca3877
enum {
Packit Service ca3877
	PROP_0,
Packit Service ca3877
Packit Service ca3877
	PROP_LEVEL,
Packit Service ca3877
	PROP_MAX_BODY_SIZE,
Packit Service ca3877
Packit Service ca3877
	LAST_PROP
Packit Service ca3877
};
Packit Service ca3877
Packit Service ca3877
static SoupSessionFeatureInterface *soup_logger_default_feature_interface;
Packit Service ca3877
static void soup_logger_session_feature_init (SoupSessionFeatureInterface *feature_interface, gpointer interface_data);
Packit Service ca3877
Packit Service ca3877
G_DEFINE_TYPE_WITH_CODE (SoupLogger, soup_logger, G_TYPE_OBJECT,
Packit Service ca3877
                         G_ADD_PRIVATE (SoupLogger)
Packit Service ca3877
			 G_IMPLEMENT_INTERFACE (SOUP_TYPE_SESSION_FEATURE,
Packit Service ca3877
						soup_logger_session_feature_init))
Packit Service ca3877
Packit Service ca3877
static void
Packit Service ca3877
soup_logger_init (SoupLogger *logger)
Packit Service ca3877
{
Packit Service ca3877
	SoupLoggerPrivate *priv = soup_logger_get_instance_private (logger);
Packit Service ca3877
Packit Service ca3877
	g_mutex_init (&priv->lock);
Packit Service ca3877
	priv->tag = g_quark_from_static_string (g_strdup_printf ("SoupLogger-%p", logger));
Packit Service ca3877
	priv->ids = g_hash_table_new (NULL, NULL);
Packit Service ca3877
}
Packit Service ca3877
Packit Service ca3877
static void
Packit Service ca3877
soup_logger_finalize (GObject *object)
Packit Service ca3877
{
Packit Service ca3877
	SoupLogger *logger = SOUP_LOGGER (object);
Packit Service ca3877
	SoupLoggerPrivate *priv = soup_logger_get_instance_private (logger);
Packit Service ca3877
Packit Service ca3877
	g_hash_table_destroy (priv->ids);
Packit Service ca3877
Packit Service ca3877
	if (priv->request_filter_dnotify)
Packit Service ca3877
		priv->request_filter_dnotify (priv->request_filter_data);
Packit Service ca3877
	if (priv->response_filter_dnotify)
Packit Service ca3877
		priv->response_filter_dnotify (priv->response_filter_data);
Packit Service ca3877
	if (priv->printer_dnotify)
Packit Service ca3877
		priv->printer_dnotify (priv->printer_data);
Packit Service ca3877
Packit Service ca3877
	g_mutex_clear (&priv->lock);
Packit Service ca3877
Packit Service ca3877
	G_OBJECT_CLASS (soup_logger_parent_class)->finalize (object);
Packit Service ca3877
}
Packit Service ca3877
Packit Service ca3877
static void
Packit Service ca3877
soup_logger_set_property (GObject *object, guint prop_id,
Packit Service ca3877
			  const GValue *value, GParamSpec *pspec)
Packit Service ca3877
{
Packit Service ca3877
	SoupLogger *logger = SOUP_LOGGER (object);
Packit Service ca3877
	SoupLoggerPrivate *priv = soup_logger_get_instance_private (logger);
Packit Service ca3877
Packit Service ca3877
	switch (prop_id) {
Packit Service ca3877
	case PROP_LEVEL:
Packit Service ca3877
		priv->level = g_value_get_enum (value);
Packit Service ca3877
		break;
Packit Service ca3877
	case PROP_MAX_BODY_SIZE:
Packit Service ca3877
		priv->max_body_size = g_value_get_int (value);
Packit Service ca3877
		break;
Packit Service ca3877
	default:
Packit Service ca3877
		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
Packit Service ca3877
		break;
Packit Service ca3877
	}
Packit Service ca3877
}
Packit Service ca3877
Packit Service ca3877
static void
Packit Service ca3877
soup_logger_get_property (GObject *object, guint prop_id,
Packit Service ca3877
			   GValue *value, GParamSpec *pspec)
Packit Service ca3877
{
Packit Service ca3877
	SoupLogger *logger = SOUP_LOGGER (object);
Packit Service ca3877
	SoupLoggerPrivate *priv = soup_logger_get_instance_private (logger);
Packit Service ca3877
Packit Service ca3877
	switch (prop_id) {
Packit Service ca3877
	case PROP_LEVEL:
Packit Service ca3877
		g_value_set_enum (value, priv->level);
Packit Service ca3877
		break;
Packit Service ca3877
	case PROP_MAX_BODY_SIZE:
Packit Service ca3877
		g_value_set_int (value, priv->max_body_size);
Packit Service ca3877
		break;
Packit Service ca3877
	default:
Packit Service ca3877
		G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
Packit Service ca3877
		break;
Packit Service ca3877
	}
Packit Service ca3877
}
Packit Service ca3877
Packit Service ca3877
static void
Packit Service ca3877
soup_logger_class_init (SoupLoggerClass *logger_class)
Packit Service ca3877
{
Packit Service ca3877
	GObjectClass *object_class = G_OBJECT_CLASS (logger_class);
Packit Service ca3877
Packit Service ca3877
	object_class->finalize = soup_logger_finalize;
Packit Service ca3877
	object_class->set_property = soup_logger_set_property;
Packit Service ca3877
	object_class->get_property = soup_logger_get_property;
Packit Service ca3877
Packit Service ca3877
	/* properties */
Packit Service ca3877
	/**
Packit Service ca3877
	 * SoupLogger:level:
Packit Service ca3877
	 *
Packit Service ca3877
	 * The level of logging output
Packit Service ca3877
	 *
Packit Service ca3877
	 * Since: 2.56
Packit Service ca3877
	 */
Packit Service ca3877
	/**
Packit Service ca3877
	 * SOUP_LOGGER_LEVEL:
Packit Service ca3877
	 *
Packit Service ca3877
	 * Alias for the #SoupLogger:level property, qv.
Packit Service ca3877
	 *
Packit Service ca3877
	 * Since: 2.56
Packit Service ca3877
	 **/
Packit Service ca3877
	g_object_class_install_property (
Packit Service ca3877
		object_class, PROP_LEVEL,
Packit Service ca3877
		g_param_spec_enum (SOUP_LOGGER_LEVEL,
Packit Service ca3877
				    "Level",
Packit Service ca3877
				    "The level of logging output",
Packit Service ca3877
				    SOUP_TYPE_LOGGER_LOG_LEVEL,
Packit Service ca3877
				    SOUP_LOGGER_LOG_MINIMAL,
Packit Service ca3877
				    G_PARAM_READWRITE));
Packit Service ca3877
Packit Service ca3877
	/**
Packit Service ca3877
	 * SoupLogger:max-body-size:
Packit Service ca3877
	 *
Packit Service ca3877
	 * If #SoupLogger:level is %SOUP_LOGGER_LOG_BODY, this gives
Packit Service ca3877
	 * the maximum number of bytes of the body that will be logged.
Packit Service ca3877
	 * (-1 means "no limit".)
Packit Service ca3877
	 *
Packit Service ca3877
	 * Since: 2.56
Packit Service ca3877
	 */
Packit Service ca3877
	/**
Packit Service ca3877
	 * SOUP_LOGGER_MAX_BODY_SIZE:
Packit Service ca3877
	 *
Packit Service ca3877
	 * Alias for the #SoupLogger:max-body-size property, qv.
Packit Service ca3877
	 *
Packit Service ca3877
	 * Since: 2.56
Packit Service ca3877
	 **/
Packit Service ca3877
	g_object_class_install_property (
Packit Service ca3877
		object_class, PROP_MAX_BODY_SIZE,
Packit Service ca3877
		g_param_spec_int (SOUP_LOGGER_MAX_BODY_SIZE,
Packit Service ca3877
				    "Max Body Size",
Packit Service ca3877
				    "The maximum body size to output",
Packit Service ca3877
				    -1,
Packit Service ca3877
				    G_MAXINT,
Packit Service ca3877
				    -1,
Packit Service ca3877
				    G_PARAM_READWRITE));
Packit Service ca3877
}
Packit Service ca3877
Packit Service ca3877
/**
Packit Service ca3877
 * SoupLoggerLogLevel:
Packit Service ca3877
 * @SOUP_LOGGER_LOG_NONE: No logging
Packit Service ca3877
 * @SOUP_LOGGER_LOG_MINIMAL: Log the Request-Line or Status-Line and
Packit Service ca3877
 * the Soup-Debug pseudo-headers
Packit Service ca3877
 * @SOUP_LOGGER_LOG_HEADERS: Log the full request/response headers
Packit Service ca3877
 * @SOUP_LOGGER_LOG_BODY: Log the full headers and request/response
Packit Service ca3877
 * bodies.
Packit Service ca3877
 *
Packit Service ca3877
 * Describes the level of logging output to provide.
Packit Service ca3877
 **/
Packit Service ca3877
Packit Service ca3877
/**
Packit Service ca3877
 * soup_logger_new:
Packit Service ca3877
 * @level: the debug level
Packit Service ca3877
 * @max_body_size: the maximum body size to output, or -1
Packit Service ca3877
 *
Packit Service ca3877
 * Creates a new #SoupLogger with the given debug level. If @level is
Packit Service ca3877
 * %SOUP_LOGGER_LOG_BODY, @max_body_size gives the maximum number of
Packit Service ca3877
 * bytes of the body that will be logged. (-1 means "no limit".)
Packit Service ca3877
 *
Packit Service ca3877
 * If you need finer control over what message parts are and aren't
Packit Service ca3877
 * logged, use soup_logger_set_request_filter() and
Packit Service ca3877
 * soup_logger_set_response_filter().
Packit Service ca3877
 *
Packit Service ca3877
 * Returns: a new #SoupLogger
Packit Service ca3877
 **/
Packit Service ca3877
SoupLogger *
Packit Service ca3877
soup_logger_new (SoupLoggerLogLevel level, int max_body_size)
Packit Service ca3877
{
Packit Service ca3877
	return g_object_new (SOUP_TYPE_LOGGER,
Packit Service ca3877
			     SOUP_LOGGER_LEVEL, level,
Packit Service ca3877
			     SOUP_LOGGER_MAX_BODY_SIZE, max_body_size,
Packit Service ca3877
			     NULL);
Packit Service ca3877
}
Packit Service ca3877
Packit Service ca3877
/**
Packit Service ca3877
 * SoupLoggerFilter:
Packit Service ca3877
 * @logger: the #SoupLogger
Packit Service ca3877
 * @msg: the message being logged
Packit Service ca3877
 * @user_data: the data passed to soup_logger_set_request_filter()
Packit Service ca3877
 * or soup_logger_set_response_filter()
Packit Service ca3877
 *
Packit Service ca3877
 * The prototype for a logging filter. The filter callback will be
Packit Service ca3877
 * invoked for each request or response, and should analyze it and
Packit Service ca3877
 * return a #SoupLoggerLogLevel value indicating how much of the
Packit Service ca3877
 * message to log. Eg, it might choose between %SOUP_LOGGER_LOG_BODY
Packit Service ca3877
 * and %SOUP_LOGGER_LOG_HEADERS depending on the Content-Type.
Packit Service ca3877
 *
Packit Service ca3877
 * Return value: a #SoupLoggerLogLevel value indicating how much of
Packit Service ca3877
 * the message to log
Packit Service ca3877
 **/
Packit Service ca3877
Packit Service ca3877
/**
Packit Service ca3877
 * soup_logger_set_request_filter:
Packit Service ca3877
 * @logger: a #SoupLogger
Packit Service ca3877
 * @request_filter: the callback for request debugging
Packit Service ca3877
 * @filter_data: data to pass to the callback
Packit Service ca3877
 * @destroy: a #GDestroyNotify to free @filter_data
Packit Service ca3877
 *
Packit Service ca3877
 * Sets up a filter to determine the log level for a given request.
Packit Service ca3877
 * For each HTTP request @logger will invoke @request_filter to
Packit Service ca3877
 * determine how much (if any) of that request to log. (If you do not
Packit Service ca3877
 * set a request filter, @logger will just always log requests at the
Packit Service ca3877
 * level passed to soup_logger_new().)
Packit Service ca3877
 **/
Packit Service ca3877
void
Packit Service ca3877
soup_logger_set_request_filter (SoupLogger       *logger,
Packit Service ca3877
				SoupLoggerFilter  request_filter,
Packit Service ca3877
				gpointer          filter_data,
Packit Service ca3877
				GDestroyNotify    destroy)
Packit Service ca3877
{
Packit Service ca3877
	SoupLoggerPrivate *priv = soup_logger_get_instance_private (logger);
Packit Service ca3877
Packit Service ca3877
	priv->request_filter         = request_filter;
Packit Service ca3877
	priv->request_filter_data    = filter_data;
Packit Service ca3877
	priv->request_filter_dnotify = destroy;
Packit Service ca3877
}
Packit Service ca3877
Packit Service ca3877
/**
Packit Service ca3877
 * soup_logger_set_response_filter:
Packit Service ca3877
 * @logger: a #SoupLogger
Packit Service ca3877
 * @response_filter: the callback for response debugging
Packit Service ca3877
 * @filter_data: data to pass to the callback
Packit Service ca3877
 * @destroy: a #GDestroyNotify to free @filter_data
Packit Service ca3877
 *
Packit Service ca3877
 * Sets up a filter to determine the log level for a given response.
Packit Service ca3877
 * For each HTTP response @logger will invoke @response_filter to
Packit Service ca3877
 * determine how much (if any) of that response to log. (If you do not
Packit Service ca3877
 * set a response filter, @logger will just always log responses at
Packit Service ca3877
 * the level passed to soup_logger_new().)
Packit Service ca3877
 **/
Packit Service ca3877
void
Packit Service ca3877
soup_logger_set_response_filter (SoupLogger       *logger,
Packit Service ca3877
				 SoupLoggerFilter  response_filter,
Packit Service ca3877
				 gpointer          filter_data,
Packit Service ca3877
				 GDestroyNotify    destroy)
Packit Service ca3877
{
Packit Service ca3877
	SoupLoggerPrivate *priv = soup_logger_get_instance_private (logger);
Packit Service ca3877
Packit Service ca3877
	priv->response_filter         = response_filter;
Packit Service ca3877
	priv->response_filter_data    = filter_data;
Packit Service ca3877
	priv->response_filter_dnotify = destroy;
Packit Service ca3877
}
Packit Service ca3877
Packit Service ca3877
/**
Packit Service ca3877
 * SoupLoggerPrinter:
Packit Service ca3877
 * @logger: the #SoupLogger
Packit Service ca3877
 * @level: the level of the information being printed.
Packit Service ca3877
 * @direction: a single-character prefix to @data
Packit Service ca3877
 * @data: data to print
Packit Service ca3877
 * @user_data: the data passed to soup_logger_set_printer()
Packit Service ca3877
 *
Packit Service ca3877
 * The prototype for a custom printing callback.
Packit Service ca3877
 *
Packit Service ca3877
 * @level indicates what kind of information is being printed. Eg, it
Packit Service ca3877
 * will be %SOUP_LOGGER_LOG_HEADERS if @data is header data.
Packit Service ca3877
 *
Packit Service ca3877
 * @direction is either '<', '>', or ' ', and @data is the single line
Packit Service ca3877
 * to print; the printer is expected to add a terminating newline.
Packit Service ca3877
 *
Packit Service ca3877
 * To get the effect of the default printer, you would do:
Packit Service ca3877
 *
Packit Service ca3877
 * <informalexample><programlisting>
Packit Service ca3877
 *	printf ("%c %s\n", direction, data);
Packit Service ca3877
 * </programlisting></informalexample>
Packit Service ca3877
 **/
Packit Service ca3877
Packit Service ca3877
/**
Packit Service ca3877
 * soup_logger_set_printer:
Packit Service ca3877
 * @logger: a #SoupLogger
Packit Service ca3877
 * @printer: the callback for printing logging output
Packit Service ca3877
 * @printer_data: data to pass to the callback
Packit Service ca3877
 * @destroy: a #GDestroyNotify to free @printer_data
Packit Service ca3877
 *
Packit Service ca3877
 * Sets up an alternate log printing routine, if you don't want
Packit Service ca3877
 * the log to go to <literal>stdout</literal>.
Packit Service ca3877
 **/
Packit Service ca3877
void
Packit Service ca3877
soup_logger_set_printer (SoupLogger        *logger,
Packit Service ca3877
			 SoupLoggerPrinter  printer,
Packit Service ca3877
			 gpointer           printer_data,
Packit Service ca3877
			 GDestroyNotify     destroy)
Packit Service ca3877
{
Packit Service ca3877
	SoupLoggerPrivate *priv = soup_logger_get_instance_private (logger);
Packit Service ca3877
Packit Service ca3877
	priv->printer         = printer;
Packit Service ca3877
	priv->printer_data    = printer_data;
Packit Service ca3877
	priv->printer_dnotify = destroy;
Packit Service ca3877
}
Packit Service ca3877
Packit Service ca3877
static guint
Packit Service ca3877
soup_logger_get_id (SoupLogger *logger, gpointer object)
Packit Service ca3877
{
Packit Service ca3877
	SoupLoggerPrivate *priv = soup_logger_get_instance_private (logger);
Packit Service ca3877
Packit Service ca3877
	return GPOINTER_TO_UINT (g_object_get_qdata (object, priv->tag));
Packit Service ca3877
}
Packit Service ca3877
Packit Service ca3877
static guint
Packit Service ca3877
soup_logger_set_id (SoupLogger *logger, gpointer object)
Packit Service ca3877
{
Packit Service ca3877
	SoupLoggerPrivate *priv = soup_logger_get_instance_private (logger);
Packit Service ca3877
	gpointer klass = G_OBJECT_GET_CLASS (object);
Packit Service ca3877
	gpointer id;
Packit Service ca3877
Packit Service ca3877
	id = g_hash_table_lookup (priv->ids, klass);
Packit Service ca3877
	id = (char *)id + 1;
Packit Service ca3877
	g_hash_table_insert (priv->ids, klass, id);
Packit Service ca3877
Packit Service ca3877
	g_object_set_qdata (object, priv->tag, id);
Packit Service ca3877
	return GPOINTER_TO_UINT (id);
Packit Service ca3877
}
Packit Service ca3877
Packit Service ca3877
/**
Packit Service ca3877
 * soup_logger_attach:
Packit Service ca3877
 * @logger: a #SoupLogger
Packit Service ca3877
 * @session: a #SoupSession
Packit Service ca3877
 *
Packit Service ca3877
 * Sets @logger to watch @session and print debug information for
Packit Service ca3877
 * its messages.
Packit Service ca3877
 *
Packit Service ca3877
 * (The session will take a reference on @logger, which will be
Packit Service ca3877
 * removed when you call soup_logger_detach(), or when the session is
Packit Service ca3877
 * destroyed.)
Packit Service ca3877
 *
Packit Service ca3877
 * Deprecated: Use soup_session_add_feature() instead.
Packit Service ca3877
 **/
Packit Service ca3877
void
Packit Service ca3877
soup_logger_attach (SoupLogger  *logger,
Packit Service ca3877
		    SoupSession *session)
Packit Service ca3877
{
Packit Service ca3877
	soup_session_add_feature (session, SOUP_SESSION_FEATURE (logger));
Packit Service ca3877
}
Packit Service ca3877
Packit Service ca3877
/**
Packit Service ca3877
 * soup_logger_detach:
Packit Service ca3877
 * @logger: a #SoupLogger
Packit Service ca3877
 * @session: a #SoupSession
Packit Service ca3877
 *
Packit Service ca3877
 * Stops @logger from watching @session.
Packit Service ca3877
 *
Packit Service ca3877
 * Deprecated: Use soup_session_remove_feature() instead.
Packit Service ca3877
 **/
Packit Service ca3877
void
Packit Service ca3877
soup_logger_detach (SoupLogger  *logger,
Packit Service ca3877
		    SoupSession *session)
Packit Service ca3877
{
Packit Service ca3877
	soup_session_remove_feature (session, SOUP_SESSION_FEATURE (logger));
Packit Service ca3877
}
Packit Service ca3877
Packit Service ca3877
static void soup_logger_print (SoupLogger *logger, SoupLoggerLogLevel level,
Packit Service ca3877
			       char direction, const char *format, ...) G_GNUC_PRINTF (4, 5);
Packit Service ca3877
Packit Service ca3877
static void
Packit Service ca3877
soup_logger_print (SoupLogger *logger, SoupLoggerLogLevel level,
Packit Service ca3877
		   char direction, const char *format, ...)
Packit Service ca3877
{
Packit Service ca3877
	SoupLoggerPrivate *priv = soup_logger_get_instance_private (logger);
Packit Service ca3877
	va_list args;
Packit Service ca3877
	char *data, *line, *end;
Packit Service ca3877
Packit Service ca3877
	va_start (args, format);
Packit Service ca3877
	data = g_strdup_vprintf (format, args);
Packit Service ca3877
	va_end (args);
Packit Service ca3877
Packit Service ca3877
	if (level == SOUP_LOGGER_LOG_BODY && priv->max_body_size > 0) {
Packit Service ca3877
		if (strlen (data) > priv->max_body_size + 6)
Packit Service ca3877
			strcpy (data + priv->max_body_size, "\n[...]");
Packit Service ca3877
	}
Packit Service ca3877
Packit Service ca3877
	line = data;
Packit Service ca3877
	do {
Packit Service ca3877
		end = strchr (line, '\n');
Packit Service ca3877
		if (end)
Packit Service ca3877
			*end = '\0';
Packit Service ca3877
		if (priv->printer) {
Packit Service ca3877
			priv->printer (logger, level, direction,
Packit Service ca3877
				       line, priv->printer_data);
Packit Service ca3877
		} else
Packit Service ca3877
			printf ("%c %s\n", direction, line);
Packit Service ca3877
Packit Service ca3877
		line = end + 1;
Packit Service ca3877
	} while (end && *line);
Packit Service ca3877
Packit Service ca3877
	g_free (data);
Packit Service ca3877
}
Packit Service ca3877
Packit Service ca3877
static void
Packit Service ca3877
soup_logger_print_basic_auth (SoupLogger *logger, const char *value)
Packit Service ca3877
{
Packit Service ca3877
	char *decoded, *decoded_utf8, *p;
Packit Service ca3877
	gsize len;
Packit Service ca3877
Packit Service ca3877
	decoded = (char *)g_base64_decode (value + 6, &len;;
Packit Service ca3877
	if (decoded && !g_utf8_validate (decoded, -1, NULL)) {
Packit Service ca3877
		decoded_utf8 = g_convert_with_fallback (decoded, -1,
Packit Service ca3877
							"UTF-8", "ISO-8859-1",
Packit Service ca3877
							NULL, NULL, &len,
Packit Service ca3877
							NULL);
Packit Service ca3877
		if (decoded_utf8) {
Packit Service ca3877
			g_free (decoded);
Packit Service ca3877
			decoded = decoded_utf8;
Packit Service ca3877
		}
Packit Service ca3877
	}
Packit Service ca3877
Packit Service ca3877
	if (!decoded)
Packit Service ca3877
		decoded = g_strdup (value);
Packit Service ca3877
	p = strchr (decoded, ':');
Packit Service ca3877
	if (p) {
Packit Service ca3877
		while (++p < decoded + len)
Packit Service ca3877
			*p = '*';
Packit Service ca3877
	}
Packit Service ca3877
	soup_logger_print (logger, SOUP_LOGGER_LOG_HEADERS, '>',
Packit Service ca3877
			   "Authorization: Basic [%.*s]", (int)len, decoded);
Packit Service ca3877
	g_free (decoded);
Packit Service ca3877
}
Packit Service ca3877
Packit Service ca3877
static void
Packit Service ca3877
print_request (SoupLogger *logger, SoupMessage *msg,
Packit Service ca3877
	       SoupSocket *socket, gboolean restarted)
Packit Service ca3877
{
Packit Service ca3877
	SoupLoggerPrivate *priv = soup_logger_get_instance_private (logger);
Packit Service ca3877
	SoupLoggerLogLevel log_level;
Packit Service ca3877
	SoupMessageHeadersIter iter;
Packit Service ca3877
	const char *name, *value;
Packit Service ca3877
	char *socket_dbg;
Packit Service ca3877
	SoupURI *uri;
Packit Service ca3877
Packit Service ca3877
	if (priv->request_filter) {
Packit Service ca3877
		log_level = priv->request_filter (logger, msg,
Packit Service ca3877
						  priv->request_filter_data);
Packit Service ca3877
	} else
Packit Service ca3877
		log_level = priv->level;
Packit Service ca3877
Packit Service ca3877
	if (log_level == SOUP_LOGGER_LOG_NONE)
Packit Service ca3877
		return;
Packit Service ca3877
Packit Service ca3877
	uri = soup_message_get_uri (msg);
Packit Service ca3877
	if (msg->method == SOUP_METHOD_CONNECT) {
Packit Service ca3877
		soup_logger_print (logger, SOUP_LOGGER_LOG_MINIMAL, '>',
Packit Service ca3877
				   "CONNECT %s:%u HTTP/1.%d",
Packit Service ca3877
				   uri->host, uri->port,
Packit Service ca3877
				   soup_message_get_http_version (msg));
Packit Service ca3877
	} else {
Packit Service ca3877
		soup_logger_print (logger, SOUP_LOGGER_LOG_MINIMAL, '>',
Packit Service ca3877
				   "%s %s%s%s HTTP/1.%d",
Packit Service ca3877
				   msg->method, uri->path,
Packit Service ca3877
				   uri->query ? "?" : "",
Packit Service ca3877
				   uri->query ? uri->query : "",
Packit Service ca3877
				   soup_message_get_http_version (msg));
Packit Service ca3877
	}
Packit Service ca3877
Packit Service ca3877
	soup_logger_print (logger, SOUP_LOGGER_LOG_MINIMAL, '>',
Packit Service ca3877
			   "Soup-Debug-Timestamp: %lu",
Packit Service ca3877
			   (unsigned long)time (0));
Packit Service ca3877
Packit Service ca3877
	socket_dbg = socket ?
Packit Service ca3877
		g_strdup_printf ("%s %u (%p)",
Packit Service ca3877
				 g_type_name_from_instance ((GTypeInstance *)socket),
Packit Service ca3877
				 soup_logger_get_id (logger, socket), socket)
Packit Service ca3877
		: NULL;
Packit Service ca3877
Packit Service ca3877
	soup_logger_print (logger, SOUP_LOGGER_LOG_MINIMAL, '>',
Packit Service ca3877
			   "Soup-Debug: %s %u (%p), %s %u (%p), %s%s",
Packit Service ca3877
			   g_type_name_from_instance ((GTypeInstance *)priv->session),
Packit Service ca3877
			   soup_logger_get_id (logger, priv->session), priv->session,
Packit Service ca3877
			   g_type_name_from_instance ((GTypeInstance *)msg),
Packit Service ca3877
			   soup_logger_get_id (logger, msg), msg,
Packit Service ca3877
			   socket_dbg ? socket_dbg : "cached",
Packit Service ca3877
			   restarted ? ", restarted" : "");
Packit Service ca3877
	g_free (socket_dbg);
Packit Service ca3877
Packit Service ca3877
	if (log_level == SOUP_LOGGER_LOG_MINIMAL)
Packit Service ca3877
		return;
Packit Service ca3877
Packit Service ca3877
	if (!soup_message_headers_get_one (msg->request_headers, "Host")) {
Packit Service ca3877
		char *uri_host;
Packit Service ca3877
Packit Service ca3877
		if (strchr (uri->host, ':'))
Packit Service ca3877
			uri_host = g_strdup_printf ("[%s]", uri->host);
Packit Service ca3877
		else if (g_hostname_is_non_ascii (uri->host))
Packit Service ca3877
			uri_host = g_hostname_to_ascii (uri->host);
Packit Service ca3877
		else
Packit Service ca3877
			uri_host = uri->host;
Packit Service ca3877
Packit Service ca3877
		soup_logger_print (logger, SOUP_LOGGER_LOG_HEADERS, '>',
Packit Service ca3877
				   "Host: %s%c%u", uri_host,
Packit Service ca3877
				   soup_uri_uses_default_port (uri) ? '\0' : ':',
Packit Service ca3877
				   uri->port);
Packit Service ca3877
Packit Service ca3877
		if (uri_host != uri->host)
Packit Service ca3877
			g_free (uri_host);
Packit Service ca3877
	}
Packit Service ca3877
	soup_message_headers_iter_init (&iter, msg->request_headers);
Packit Service ca3877
	while (soup_message_headers_iter_next (&iter, &name, &value)) {
Packit Service ca3877
		if (!g_ascii_strcasecmp (name, "Authorization") &&
Packit Service ca3877
		    !g_ascii_strncasecmp (value, "Basic ", 6))
Packit Service ca3877
			soup_logger_print_basic_auth (logger, value);
Packit Service ca3877
		else {
Packit Service ca3877
			soup_logger_print (logger, SOUP_LOGGER_LOG_HEADERS, '>',
Packit Service ca3877
					   "%s: %s", name, value);
Packit Service ca3877
		}
Packit Service ca3877
	}
Packit Service ca3877
	if (log_level == SOUP_LOGGER_LOG_HEADERS)
Packit Service ca3877
		return;
Packit Service ca3877
Packit Service ca3877
	if (msg->request_body->length &&
Packit Service ca3877
	    soup_message_body_get_accumulate (msg->request_body)) {
Packit Service ca3877
		SoupBuffer *request;
Packit Service ca3877
Packit Service ca3877
		request = soup_message_body_flatten (msg->request_body);
Packit Service ca3877
		g_return_if_fail (request != NULL);
Packit Service ca3877
		soup_buffer_free (request);
Packit Service ca3877
Packit Service ca3877
		if (soup_message_headers_get_expectations (msg->request_headers) != SOUP_EXPECTATION_CONTINUE) {
Packit Service ca3877
			soup_logger_print (logger, SOUP_LOGGER_LOG_BODY, '>',
Packit Service ca3877
					   "\n%s", msg->request_body->data);
Packit Service ca3877
		}
Packit Service ca3877
	}
Packit Service ca3877
}
Packit Service ca3877
Packit Service ca3877
static void
Packit Service ca3877
print_response (SoupLogger *logger, SoupMessage *msg)
Packit Service ca3877
{
Packit Service ca3877
	SoupLoggerPrivate *priv = soup_logger_get_instance_private (logger);
Packit Service ca3877
	SoupLoggerLogLevel log_level;
Packit Service ca3877
	SoupMessageHeadersIter iter;
Packit Service ca3877
	const char *name, *value;
Packit Service ca3877
Packit Service ca3877
	if (priv->response_filter) {
Packit Service ca3877
		log_level = priv->response_filter (logger, msg,
Packit Service ca3877
						   priv->response_filter_data);
Packit Service ca3877
	} else
Packit Service ca3877
		log_level = priv->level;
Packit Service ca3877
Packit Service ca3877
	if (log_level == SOUP_LOGGER_LOG_NONE)
Packit Service ca3877
		return;
Packit Service ca3877
Packit Service ca3877
	soup_logger_print (logger, SOUP_LOGGER_LOG_MINIMAL, '<',
Packit Service ca3877
			   "HTTP/1.%d %u %s\n",
Packit Service ca3877
			   soup_message_get_http_version (msg),
Packit Service ca3877
			   msg->status_code, msg->reason_phrase);
Packit Service ca3877
Packit Service ca3877
	soup_logger_print (logger, SOUP_LOGGER_LOG_MINIMAL, '<',
Packit Service ca3877
			   "Soup-Debug-Timestamp: %lu",
Packit Service ca3877
			   (unsigned long)time (0));
Packit Service ca3877
	soup_logger_print (logger, SOUP_LOGGER_LOG_MINIMAL, '<',
Packit Service ca3877
			   "Soup-Debug: %s %u (%p)",
Packit Service ca3877
			   g_type_name_from_instance ((GTypeInstance *)msg),
Packit Service ca3877
			   soup_logger_get_id (logger, msg), msg);
Packit Service ca3877
Packit Service ca3877
	if (log_level == SOUP_LOGGER_LOG_MINIMAL)
Packit Service ca3877
		return;
Packit Service ca3877
Packit Service ca3877
	soup_message_headers_iter_init (&iter, msg->response_headers);
Packit Service ca3877
	while (soup_message_headers_iter_next (&iter, &name, &value)) {
Packit Service ca3877
		soup_logger_print (logger, SOUP_LOGGER_LOG_HEADERS, '<',
Packit Service ca3877
				   "%s: %s", name, value);
Packit Service ca3877
	}
Packit Service ca3877
	if (log_level == SOUP_LOGGER_LOG_HEADERS)
Packit Service ca3877
		return;
Packit Service ca3877
Packit Service ca3877
	if (msg->response_body->data) {
Packit Service ca3877
		soup_logger_print (logger, SOUP_LOGGER_LOG_BODY, '<',
Packit Service ca3877
				   "\n%s", msg->response_body->data);
Packit Service ca3877
	}
Packit Service ca3877
}
Packit Service ca3877
Packit Service ca3877
static void
Packit Service ca3877
finished (SoupMessage *msg, gpointer user_data)
Packit Service ca3877
{
Packit Service ca3877
	SoupLogger *logger = user_data;
Packit Service ca3877
	SoupLoggerPrivate *priv = soup_logger_get_instance_private (logger);
Packit Service ca3877
Packit Service ca3877
	g_mutex_lock (&priv->lock);
Packit Service ca3877
Packit Service ca3877
	print_response (logger, msg);
Packit Service ca3877
	soup_logger_print (logger, SOUP_LOGGER_LOG_MINIMAL, ' ', "\n");
Packit Service ca3877
Packit Service ca3877
	g_mutex_unlock (&priv->lock);
Packit Service ca3877
}
Packit Service ca3877
Packit Service ca3877
static void
Packit Service ca3877
got_informational (SoupMessage *msg, gpointer user_data)
Packit Service ca3877
{
Packit Service ca3877
	SoupLogger *logger = user_data;
Packit Service ca3877
	SoupLoggerPrivate *priv = soup_logger_get_instance_private (logger);
Packit Service ca3877
Packit Service ca3877
	g_mutex_lock (&priv->lock);
Packit Service ca3877
Packit Service ca3877
	g_signal_handlers_disconnect_by_func (msg, finished, logger);
Packit Service ca3877
	print_response (logger, msg);
Packit Service ca3877
	soup_logger_print (logger, SOUP_LOGGER_LOG_MINIMAL, ' ', "\n");
Packit Service ca3877
Packit Service ca3877
	if (msg->status_code == SOUP_STATUS_CONTINUE && msg->request_body->data) {
Packit Service ca3877
		SoupLoggerLogLevel log_level;
Packit Service ca3877
Packit Service ca3877
		soup_logger_print (logger, SOUP_LOGGER_LOG_MINIMAL, '>',
Packit Service ca3877
				   "[Now sending request body...]");
Packit Service ca3877
Packit Service ca3877
		if (priv->request_filter) {
Packit Service ca3877
			log_level = priv->request_filter (logger, msg,
Packit Service ca3877
							  priv->request_filter_data);
Packit Service ca3877
		} else
Packit Service ca3877
			log_level = priv->level;
Packit Service ca3877
Packit Service ca3877
		if (log_level == SOUP_LOGGER_LOG_BODY) {
Packit Service ca3877
			soup_logger_print (logger, SOUP_LOGGER_LOG_BODY, '>',
Packit Service ca3877
					   "%s", msg->request_body->data);
Packit Service ca3877
		}
Packit Service ca3877
Packit Service ca3877
		soup_logger_print (logger, SOUP_LOGGER_LOG_MINIMAL, ' ', "\n");
Packit Service ca3877
	}
Packit Service ca3877
Packit Service ca3877
	g_mutex_unlock (&priv->lock);
Packit Service ca3877
}
Packit Service ca3877
Packit Service ca3877
static void
Packit Service ca3877
got_body (SoupMessage *msg, gpointer user_data)
Packit Service ca3877
{
Packit Service ca3877
	SoupLogger *logger = user_data;
Packit Service ca3877
	SoupLoggerPrivate *priv = soup_logger_get_instance_private (logger);
Packit Service ca3877
Packit Service ca3877
	g_mutex_lock (&priv->lock);
Packit Service ca3877
Packit Service ca3877
	g_signal_handlers_disconnect_by_func (msg, finished, logger);
Packit Service ca3877
	print_response (logger, msg);
Packit Service ca3877
	soup_logger_print (logger, SOUP_LOGGER_LOG_MINIMAL, ' ', "\n");
Packit Service ca3877
Packit Service ca3877
	g_mutex_unlock (&priv->lock);
Packit Service ca3877
}
Packit Service ca3877
Packit Service ca3877
static void
Packit Service ca3877
starting (SoupMessage *msg, gpointer user_data)
Packit Service ca3877
{
Packit Service ca3877
	SoupLogger *logger = SOUP_LOGGER (user_data);
Packit Service ca3877
	SoupLoggerPrivate *priv = soup_logger_get_instance_private (logger);
Packit Service ca3877
	gboolean restarted;
Packit Service ca3877
	guint msg_id;
Packit Service ca3877
	SoupConnection *conn;
Packit Service ca3877
	SoupSocket *socket;
Packit Service ca3877
Packit Service ca3877
	msg_id = soup_logger_get_id (logger, msg);
Packit Service ca3877
	if (msg_id)
Packit Service ca3877
		restarted = TRUE;
Packit Service ca3877
	else {
Packit Service ca3877
		soup_logger_set_id (logger, msg);
Packit Service ca3877
		restarted = FALSE;
Packit Service ca3877
	}
Packit Service ca3877
Packit Service ca3877
	if (!soup_logger_get_id (logger, priv->session))
Packit Service ca3877
		soup_logger_set_id (logger, priv->session);
Packit Service ca3877
Packit Service ca3877
	conn = soup_message_get_connection (msg);
Packit Service ca3877
	socket = conn ? soup_connection_get_socket (conn) : NULL;
Packit Service ca3877
	if (socket && !soup_logger_get_id (logger, socket))
Packit Service ca3877
		soup_logger_set_id (logger, socket);
Packit Service ca3877
Packit Service ca3877
	print_request (logger, msg, socket, restarted);
Packit Service ca3877
	soup_logger_print (logger, SOUP_LOGGER_LOG_MINIMAL, ' ', "\n");
Packit Service ca3877
}
Packit Service ca3877
Packit Service ca3877
static void
Packit Service ca3877
soup_logger_request_queued (SoupSessionFeature *logger,
Packit Service ca3877
			    SoupSession *session,
Packit Service ca3877
			    SoupMessage *msg)
Packit Service ca3877
{
Packit Service ca3877
	g_return_if_fail (SOUP_IS_MESSAGE (msg));
Packit Service ca3877
Packit Service ca3877
	g_signal_connect (msg, "starting",
Packit Service ca3877
			  G_CALLBACK (starting),
Packit Service ca3877
			  logger);
Packit Service ca3877
	g_signal_connect (msg, "got-informational",
Packit Service ca3877
			  G_CALLBACK (got_informational),
Packit Service ca3877
			  logger);
Packit Service ca3877
	g_signal_connect (msg, "got-body",
Packit Service ca3877
			  G_CALLBACK (got_body),
Packit Service ca3877
			  logger);
Packit Service ca3877
	g_signal_connect (msg, "finished",
Packit Service ca3877
			  G_CALLBACK (finished),
Packit Service ca3877
			  logger);
Packit Service ca3877
}
Packit Service ca3877
Packit Service ca3877
static void
Packit Service ca3877
soup_logger_request_unqueued (SoupSessionFeature *logger,
Packit Service ca3877
			      SoupSession *session,
Packit Service ca3877
			      SoupMessage *msg)
Packit Service ca3877
{
Packit Service ca3877
	g_return_if_fail (SOUP_IS_MESSAGE (msg));
Packit Service ca3877
Packit Service ca3877
	g_signal_handlers_disconnect_by_func (msg, starting, logger);
Packit Service ca3877
	g_signal_handlers_disconnect_by_func (msg, got_informational, logger);
Packit Service ca3877
	g_signal_handlers_disconnect_by_func (msg, got_body, logger);
Packit Service ca3877
	g_signal_handlers_disconnect_by_func (msg, finished, logger);
Packit Service ca3877
}
Packit Service ca3877
Packit Service ca3877
static void
Packit Service ca3877
soup_logger_feature_attach (SoupSessionFeature *feature,
Packit Service ca3877
			    SoupSession *session)
Packit Service ca3877
{
Packit Service ca3877
	SoupLoggerPrivate *priv = soup_logger_get_instance_private (SOUP_LOGGER (feature));
Packit Service ca3877
Packit Service ca3877
	priv->session = session;
Packit Service ca3877
Packit Service ca3877
	soup_logger_default_feature_interface->attach (feature, session);
Packit Service ca3877
}
Packit Service ca3877
Packit Service ca3877
static void
Packit Service ca3877
soup_logger_session_feature_init (SoupSessionFeatureInterface *feature_interface,
Packit Service ca3877
				  gpointer interface_data)
Packit Service ca3877
{
Packit Service ca3877
	soup_logger_default_feature_interface =
Packit Service ca3877
		g_type_default_interface_peek (SOUP_TYPE_SESSION_FEATURE);
Packit Service ca3877
Packit Service ca3877
	feature_interface->attach = soup_logger_feature_attach;
Packit Service ca3877
	feature_interface->request_queued = soup_logger_request_queued;
Packit Service ca3877
	feature_interface->request_unqueued = soup_logger_request_unqueued;
Packit Service ca3877
}