summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorCarlos Garcia Campos <cgarcia@igalia.com>2021-04-09 10:16:56 +0200
committerPatrick Griffis <tingping@tingping.se>2021-04-13 21:09:55 +0000
commit64d577ed888ab5d322a2c7093f8969d7dc337317 (patch)
treeac494cfe18340dfb2bf9f9d373498ac6e7e5b039
parent878809ba6aced2df29931fc461f905865cbcc26c (diff)
downloadlibsoup-carlosgc/metrics.tar.gz
metrics: add support for size metricscarlosgc/metrics
-rw-r--r--docs/reference/libsoup-3.0-sections.txt7
-rw-r--r--libsoup/cache/soup-cache.c7
-rw-r--r--libsoup/server/soup-server-io.c2
-rw-r--r--libsoup/soup-body-output-stream.c18
-rw-r--r--libsoup/soup-client-input-stream.c15
-rw-r--r--libsoup/soup-filter-input-stream.c65
-rw-r--r--libsoup/soup-logger.c17
-rw-r--r--libsoup/soup-message-io-data.c10
-rw-r--r--libsoup/soup-message-io-data.h1
-rw-r--r--libsoup/soup-message-io.c55
-rw-r--r--libsoup/soup-message-metrics-private.h9
-rw-r--r--libsoup/soup-message-metrics.c119
-rw-r--r--libsoup/soup-message-metrics.h18
-rw-r--r--tests/cache-test.c12
-rw-r--r--tests/coding-test.c18
-rw-r--r--tests/request-body-test.c29
-rw-r--r--tests/streaming-test.c55
17 files changed, 425 insertions, 32 deletions
diff --git a/docs/reference/libsoup-3.0-sections.txt b/docs/reference/libsoup-3.0-sections.txt
index cc5b5710..6d958f5b 100644
--- a/docs/reference/libsoup-3.0-sections.txt
+++ b/docs/reference/libsoup-3.0-sections.txt
@@ -1010,6 +1010,13 @@ soup_message_metrics_get_tls_start
soup_message_metrics_get_request_start
soup_message_metrics_get_response_start
soup_message_metrics_get_response_end
+<SUBSECTION>
+soup_message_metrics_get_request_header_bytes_sent
+soup_message_metrics_get_request_body_size
+soup_message_metrics_get_request_body_bytes_sent
+soup_message_metrics_get_response_header_bytes_received
+soup_message_metrics_get_response_body_size
+soup_message_metrics_get_response_body_bytes_received
<SUBSECTION Standard>
SOUP_TYPE_MESSAGE_METRICS
soup_message_metrics_get_type
diff --git a/libsoup/cache/soup-cache.c b/libsoup/cache/soup-cache.c
index 969b3a4d..a33be803 100644
--- a/libsoup/cache/soup-cache.c
+++ b/libsoup/cache/soup-cache.c
@@ -40,7 +40,7 @@
#include "soup-content-processor.h"
#include "soup-message-private.h"
#include "soup.h"
-#include "soup-message-private.h"
+#include "soup-message-metrics-private.h"
#include "soup-misc.h"
#include "soup-session-private.h"
#include "soup-session-feature-private.h"
@@ -688,6 +688,7 @@ soup_cache_send_response (SoupCache *cache, SoupMessage *msg)
SoupCacheEntry *entry;
GInputStream *file_stream, *body_stream, *cache_stream, *client_stream;
GFile *file;
+ SoupMessageMetrics *metrics;
g_return_val_if_fail (SOUP_IS_CACHE (cache), NULL);
g_return_val_if_fail (SOUP_IS_MESSAGE (msg), NULL);
@@ -711,6 +712,10 @@ soup_cache_send_response (SoupCache *cache, SoupMessage *msg)
if (!body_stream)
return NULL;
+ metrics = soup_message_get_metrics (msg);
+ if (metrics)
+ metrics->response_body_size = entry->length;
+
/* If we are told to send a response from cache any validation
in course is over by now */
entry->being_validated = FALSE;
diff --git a/libsoup/server/soup-server-io.c b/libsoup/server/soup-server-io.c
index 5cc240b1..a806d2f3 100644
--- a/libsoup/server/soup-server-io.c
+++ b/libsoup/server/soup-server-io.c
@@ -661,7 +661,7 @@ io_read (SoupServerMessage *msg,
switch (io->read_state) {
case SOUP_MESSAGE_IO_STATE_HEADERS:
- if (!soup_message_io_data_read_headers (io, FALSE, NULL, error)) {
+ if (!soup_message_io_data_read_headers (io, FALSE, NULL, NULL, error)) {
if (g_error_matches (*error, G_IO_ERROR, G_IO_ERROR_PARTIAL_INPUT))
soup_server_message_set_status (msg, SOUP_STATUS_BAD_REQUEST, NULL);
return FALSE;
diff --git a/libsoup/soup-body-output-stream.c b/libsoup/soup-body-output-stream.c
index 499e8e6f..c0aeb458 100644
--- a/libsoup/soup-body-output-stream.c
+++ b/libsoup/soup-body-output-stream.c
@@ -118,7 +118,15 @@ soup_body_output_stream_wrote_data (SoupBodyOutputStream *bostream,
const void *buffer,
gsize count)
{
- g_signal_emit (bostream, signals[WROTE_DATA], 0, buffer, count);
+ g_signal_emit (bostream, signals[WROTE_DATA], 0, buffer, count, FALSE);
+}
+
+static void
+soup_body_output_stream_wrote_metadata (SoupBodyOutputStream *bostream,
+ const void *buffer,
+ gsize count)
+{
+ g_signal_emit (bostream, signals[WROTE_DATA], 0, buffer, count, TRUE);
}
static gssize
@@ -178,6 +186,9 @@ again:
nwrote = g_pollable_stream_write (priv->base_stream,
buf, len, blocking,
cancellable, error);
+ if (nwrote > 0)
+ soup_body_output_stream_wrote_metadata (bostream, buf, nwrote);
+
if (nwrote < 0)
return nwrote;
memmove (buf, buf + nwrote, len + 1 - nwrote);
@@ -341,9 +352,10 @@ soup_body_output_stream_class_init (SoupBodyOutputStreamClass *stream_class)
0,
NULL, NULL,
NULL,
- G_TYPE_NONE, 2,
+ G_TYPE_NONE, 3,
G_TYPE_POINTER,
- G_TYPE_UINT);
+ G_TYPE_UINT,
+ G_TYPE_BOOLEAN);
g_object_class_install_property (
object_class, PROP_ENCODING,
diff --git a/libsoup/soup-client-input-stream.c b/libsoup/soup-client-input-stream.c
index bf4a866f..a95bfe41 100644
--- a/libsoup/soup-client-input-stream.c
+++ b/libsoup/soup-client-input-stream.c
@@ -12,6 +12,7 @@
#include "soup-client-input-stream.h"
#include "soup.h"
#include "soup-message-private.h"
+#include "soup-message-metrics-private.h"
#include "soup-misc.h"
struct _SoupClientInputStream {
@@ -20,6 +21,7 @@ struct _SoupClientInputStream {
typedef struct {
SoupMessage *msg;
+ SoupMessageMetrics *metrics;
} SoupClientInputStreamPrivate;
enum {
@@ -69,6 +71,7 @@ soup_client_input_stream_set_property (GObject *object, guint prop_id,
switch (prop_id) {
case PROP_MESSAGE:
priv->msg = g_value_dup_object (value);
+ priv->metrics = soup_message_get_metrics (priv->msg);
break;
default:
G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
@@ -100,11 +103,15 @@ soup_client_input_stream_read_fn (GInputStream *stream,
GCancellable *cancellable,
GError **error)
{
+ SoupClientInputStreamPrivate *priv = soup_client_input_stream_get_instance_private (SOUP_CLIENT_INPUT_STREAM (stream));
gssize nread;
nread = G_INPUT_STREAM_CLASS (soup_client_input_stream_parent_class)->
read_fn (stream, buffer, count, cancellable, error);
+ if (priv->metrics && nread > 0)
+ priv->metrics->response_body_size += nread;
+
if (nread == 0)
g_signal_emit (stream, signals[SIGNAL_EOF], 0);
@@ -117,11 +124,15 @@ soup_client_input_stream_skip (GInputStream *stream,
GCancellable *cancellable,
GError **error)
{
+ SoupClientInputStreamPrivate *priv = soup_client_input_stream_get_instance_private (SOUP_CLIENT_INPUT_STREAM (stream));
gssize nread;
nread = G_INPUT_STREAM_CLASS (soup_client_input_stream_parent_class)->
skip (stream, count, cancellable, error);
+ if (priv->metrics && nread > 0)
+ priv->metrics->response_body_size += nread;
+
if (nread == 0)
g_signal_emit (stream, signals[SIGNAL_EOF], 0);
@@ -134,11 +145,15 @@ soup_client_input_stream_read_nonblocking (GPollableInputStream *stream,
gsize count,
GError **error)
{
+ SoupClientInputStreamPrivate *priv = soup_client_input_stream_get_instance_private (SOUP_CLIENT_INPUT_STREAM (stream));
gssize nread;
nread = soup_client_input_stream_parent_pollable_interface->
read_nonblocking (stream, buffer, count, error);
+ if (priv->metrics && nread > 0)
+ priv->metrics->response_body_size += nread;
+
if (nread == 0)
g_signal_emit (stream, signals[SIGNAL_EOF], 0);
diff --git a/libsoup/soup-filter-input-stream.c b/libsoup/soup-filter-input-stream.c
index 448b7c9f..6ef11a05 100644
--- a/libsoup/soup-filter-input-stream.c
+++ b/libsoup/soup-filter-input-stream.c
@@ -26,6 +26,14 @@ typedef struct {
gboolean in_read_until;
} SoupFilterInputStreamPrivate;
+enum {
+ READ_DATA,
+
+ LAST_SIGNAL
+};
+
+static guint signals[LAST_SIGNAL] = { 0 };
+
static void soup_filter_input_stream_pollable_init (GPollableInputStreamInterface *pollable_interface, gpointer interface_data);
G_DEFINE_TYPE_WITH_CODE (SoupFilterInputStream, soup_filter_input_stream, G_TYPE_FILTER_INPUT_STREAM,
@@ -81,17 +89,21 @@ soup_filter_input_stream_read_fn (GInputStream *stream,
{
SoupFilterInputStream *fstream = SOUP_FILTER_INPUT_STREAM (stream);
SoupFilterInputStreamPrivate *priv = soup_filter_input_stream_get_instance_private (fstream);
+ gssize bytes_read;
if (!priv->in_read_until)
priv->need_more = FALSE;
- if (priv->buf && !priv->in_read_until) {
+ if (priv->buf && !priv->in_read_until)
return read_from_buf (fstream, buffer, count);
- } else {
- return g_pollable_stream_read (G_FILTER_INPUT_STREAM (fstream)->base_stream,
- buffer, count,
- TRUE, cancellable, error);
- }
+
+ bytes_read = g_pollable_stream_read (G_FILTER_INPUT_STREAM (fstream)->base_stream,
+ buffer, count,
+ TRUE, cancellable, error);
+ if (bytes_read > 0)
+ g_signal_emit (fstream, signals[READ_DATA], 0, bytes_read);
+
+ return bytes_read;
}
static gssize
@@ -102,16 +114,20 @@ soup_filter_input_stream_skip (GInputStream *stream,
{
SoupFilterInputStream *fstream = SOUP_FILTER_INPUT_STREAM (stream);
SoupFilterInputStreamPrivate *priv = soup_filter_input_stream_get_instance_private (fstream);
+ gssize bytes_skipped;
if (!priv->in_read_until)
priv->need_more = FALSE;
- if (priv->buf && !priv->in_read_until) {
+ if (priv->buf && !priv->in_read_until)
return read_from_buf (fstream, NULL, count);
- } else {
- return g_input_stream_skip (G_FILTER_INPUT_STREAM (fstream)->base_stream,
- count, cancellable, error);
- }
+
+ bytes_skipped = g_input_stream_skip (G_FILTER_INPUT_STREAM (fstream)->base_stream,
+ count, cancellable, error);
+ if (bytes_skipped > 0)
+ g_signal_emit (fstream, signals[READ_DATA], 0, bytes_skipped);
+
+ return bytes_skipped;
}
static gboolean
@@ -134,17 +150,21 @@ soup_filter_input_stream_read_nonblocking (GPollableInputStream *stream,
{
SoupFilterInputStream *fstream = SOUP_FILTER_INPUT_STREAM (stream);
SoupFilterInputStreamPrivate *priv = soup_filter_input_stream_get_instance_private (fstream);
+ gssize bytes_read;
if (!priv->in_read_until)
priv->need_more = FALSE;
- if (priv->buf && !priv->in_read_until) {
+ if (priv->buf && !priv->in_read_until)
return read_from_buf (fstream, buffer, count);
- } else {
- return g_pollable_stream_read (G_FILTER_INPUT_STREAM (fstream)->base_stream,
- buffer, count,
- FALSE, NULL, error);
- }
+
+ bytes_read = g_pollable_stream_read (G_FILTER_INPUT_STREAM (fstream)->base_stream,
+ buffer, count,
+ FALSE, NULL, error);
+ if (bytes_read > 0)
+ g_signal_emit (fstream, signals[READ_DATA], 0, bytes_read);
+
+ return bytes_read;
}
static GSource *
@@ -178,6 +198,17 @@ soup_filter_input_stream_class_init (SoupFilterInputStreamClass *stream_class)
input_stream_class->read_fn = soup_filter_input_stream_read_fn;
input_stream_class->skip = soup_filter_input_stream_skip;
+
+ signals[READ_DATA] =
+ g_signal_new ("read-data",
+ G_OBJECT_CLASS_TYPE (object_class),
+ G_SIGNAL_RUN_LAST,
+ 0,
+ NULL, NULL,
+ NULL,
+ G_TYPE_NONE, 1,
+ G_TYPE_UINT);
+
}
static void
diff --git a/libsoup/soup-logger.c b/libsoup/soup-logger.c
index 3bb7dda3..c9899e22 100644
--- a/libsoup/soup-logger.c
+++ b/libsoup/soup-logger.c
@@ -815,13 +815,20 @@ got_body (SoupMessage *msg, gpointer user_data)
}
static void
-body_stream_wrote_data_cb (GOutputStream *stream, const void *buffer,
- guint count, SoupLogger *logger)
+body_stream_wrote_data_cb (GOutputStream *stream,
+ const void *buffer,
+ guint count,
+ gboolean is_metadata,
+ SoupLogger *logger)
{
- SoupLoggerPrivate *priv = soup_logger_get_instance_private (logger);
- SoupMessage *msg = g_hash_table_lookup (priv->request_messages,
- stream);
+ SoupLoggerPrivate *priv;
+ SoupMessage *msg;
+
+ if (is_metadata)
+ return;
+ priv = soup_logger_get_instance_private (logger);
+ msg = g_hash_table_lookup (priv->request_messages, stream);
write_body (logger, buffer, count, msg, priv->request_bodies);
}
diff --git a/libsoup/soup-message-io-data.c b/libsoup/soup-message-io-data.c
index 61ad83f4..04a9c255 100644
--- a/libsoup/soup-message-io-data.c
+++ b/libsoup/soup-message-io-data.c
@@ -50,6 +50,7 @@ gboolean
soup_message_io_data_read_headers (SoupMessageIOData *io,
gboolean blocking,
GCancellable *cancellable,
+ gushort *extra_bytes,
GError **error)
{
gssize nread, old_len;
@@ -66,8 +67,11 @@ soup_message_io_data_read_headers (SoupMessageIOData *io,
cancellable, error);
io->read_header_buf->len = old_len + MAX (nread, 0);
if (nread == 0) {
- if (io->read_header_buf->len > 0)
+ if (io->read_header_buf->len > 0) {
+ if (extra_bytes)
+ *extra_bytes = 0;
break;
+ }
g_set_error_literal (error, G_IO_ERROR,
G_IO_ERROR_PARTIAL_INPUT,
@@ -82,12 +86,16 @@ soup_message_io_data_read_headers (SoupMessageIOData *io,
io->read_header_buf->len - 2,
"\n\n", 2)) {
io->read_header_buf->len--;
+ if (extra_bytes)
+ *extra_bytes = 1;
break;
} else if (nread == 2 && old_len >= 3 &&
!strncmp ((char *)io->read_header_buf->data +
io->read_header_buf->len - 3,
"\n\r\n", 3)) {
io->read_header_buf->len -= 2;
+ if (extra_bytes)
+ *extra_bytes = 2;
break;
}
}
diff --git a/libsoup/soup-message-io-data.h b/libsoup/soup-message-io-data.h
index 868ae8d5..e3feae7a 100644
--- a/libsoup/soup-message-io-data.h
+++ b/libsoup/soup-message-io-data.h
@@ -80,6 +80,7 @@ void soup_message_io_data_cleanup (SoupMessageIOData *io);
gboolean soup_message_io_data_read_headers (SoupMessageIOData *io,
gboolean blocking,
GCancellable *cancellable,
+ gushort *extra_bytes,
GError **error);
typedef gboolean (*SoupMessageIOSourceFunc) (GObject *msg,
diff --git a/libsoup/soup-message-io.c b/libsoup/soup-message-io.c
index 9eab3ef2..68b437e5 100644
--- a/libsoup/soup-message-io.c
+++ b/libsoup/soup-message-io.c
@@ -25,6 +25,7 @@
#include "soup-filter-input-stream.h"
#include "soup-logger-private.h"
#include "soup-message-private.h"
+#include "soup-message-metrics-private.h"
#include "soup-message-queue-item.h"
#include "soup-misc.h"
#include "soup-uri-utils-private.h"
@@ -34,6 +35,8 @@ struct _SoupClientMessageIOData {
SoupMessageQueueItem *item;
+ SoupMessageMetrics *metrics;
+
#ifdef HAVE_SYSPROF
gint64 begin_time_nsec;
#endif
@@ -164,9 +167,19 @@ soup_message_setup_body_istream (GInputStream *body_stream,
static void
request_body_stream_wrote_data_cb (SoupMessage *msg,
const void *buffer,
- guint count)
+ guint count,
+ gboolean is_metadata)
{
- soup_message_wrote_body_data (msg, count);
+ SoupClientMessageIOData *client_io = soup_message_get_io_data (msg);
+
+ if (client_io->metrics) {
+ client_io->metrics->request_body_bytes_sent += count;
+ if (!is_metadata)
+ client_io->metrics->request_body_size += count;
+ }
+
+ if (!is_metadata)
+ soup_message_wrote_body_data (msg, count);
}
static void
@@ -345,6 +358,8 @@ io_write (SoupMessage *msg, gboolean blocking,
if (nwrote == -1)
return FALSE;
io->written += nwrote;
+ if (client_io->metrics)
+ client_io->metrics->request_header_bytes_sent += nwrote;
}
io->written = 0;
@@ -497,6 +512,18 @@ parse_headers (SoupMessage *msg,
return TRUE;
}
+static void
+response_network_stream_read_data_cb (SoupMessage *msg,
+ guint count)
+{
+ SoupClientMessageIOData *client_io = soup_message_get_io_data (msg);
+
+ if (client_io->base.read_state < SOUP_MESSAGE_IO_STATE_BODY_START)
+ client_io->metrics->response_header_bytes_received += count;
+ else
+ client_io->metrics->response_body_bytes_received += count;
+}
+
/* Attempts to push forward the reading side of @msg's I/O. Returns
* %TRUE if it manages to make some progress, and it is likely that
* further progress can be made. Returns %FALSE if it has reached a
@@ -511,15 +538,27 @@ io_read (SoupMessage *msg, gboolean blocking,
SoupMessageIOData *io = &client_io->base;
gboolean succeeded;
gboolean is_first_read;
+ gushort extra_bytes;
switch (io->read_state) {
case SOUP_MESSAGE_IO_STATE_HEADERS:
is_first_read = io->read_header_buf->len == 0 &&
soup_message_get_status (msg) == SOUP_STATUS_NONE;
- if (!soup_message_io_data_read_headers (io, blocking, cancellable, error))
+ if (!soup_message_io_data_read_headers (io, blocking, cancellable, &extra_bytes, error))
return FALSE;
+ if (client_io->metrics) {
+ /* Adjust the header and body bytes received, since we might
+ * have read part of the body already that is queued by the stream.
+ */
+ if (client_io->metrics->response_header_bytes_received > io->read_header_buf->len + extra_bytes) {
+ client_io->metrics->response_body_bytes_received =
+ client_io->metrics->response_header_bytes_received - io->read_header_buf->len - extra_bytes;
+ client_io->metrics->response_header_bytes_received -= client_io->metrics->response_body_bytes_received;
+ }
+ }
+
if (is_first_read)
soup_message_set_metrics_timestamp (msg, SOUP_MESSAGE_METRICS_RESPONSE_START);
@@ -642,6 +681,9 @@ io_read (SoupMessage *msg, gboolean blocking,
if (nread == 0)
io->read_state = SOUP_MESSAGE_IO_STATE_BODY_DONE;
+ if (client_io->metrics)
+ client_io->metrics->response_body_size += nread;
+
break;
}
@@ -1000,6 +1042,13 @@ soup_message_send_request (SoupMessageQueueItem *item,
io->base.read_state = SOUP_MESSAGE_IO_STATE_NOT_STARTED;
io->base.write_state = SOUP_MESSAGE_IO_STATE_HEADERS;
+ io->metrics = soup_message_get_metrics (io->item->msg);
+ if (io->metrics) {
+ g_signal_connect_object (io->base.istream, "read-data",
+ G_CALLBACK (response_network_stream_read_data_cb),
+ io->item->msg, G_CONNECT_SWAPPED);
+ }
+
#ifdef HAVE_SYSPROF
io->begin_time_nsec = SYSPROF_CAPTURE_CURRENT_TIME;
#endif
diff --git a/libsoup/soup-message-metrics-private.h b/libsoup/soup-message-metrics-private.h
index 03d62ad1..6f0869e0 100644
--- a/libsoup/soup-message-metrics-private.h
+++ b/libsoup/soup-message-metrics-private.h
@@ -19,8 +19,15 @@ struct _SoupMessageMetrics {
guint64 request_start;
guint64 response_start;
guint64 response_end;
+
+ guint64 request_header_bytes_sent;
+ guint64 request_body_size;
+ guint64 request_body_bytes_sent;
+ guint64 response_header_bytes_received;
+ guint64 response_body_size;
+ guint64 response_body_bytes_received;
};
-SoupMessageMetrics *soup_message_metrics_new (void);
+SoupMessageMetrics *soup_message_metrics_new (void);
G_END_DECLS
diff --git a/libsoup/soup-message-metrics.c b/libsoup/soup-message-metrics.c
index e3b642c3..41a9ef49 100644
--- a/libsoup/soup-message-metrics.c
+++ b/libsoup/soup-message-metrics.c
@@ -32,6 +32,10 @@
* fetch start event and finish with response end. All other events are optional.
* An event can be 0 because it hasn't happened yet, because it's optional or
* because the load failed before the event reached.
+ *
+ * Size metrics are expressed in bytes and aree updated while the #SoupMessage is
+ * being loaded. You can connect to different #SoupMessage signals to get the
+ * final result of every value.
*/
G_DEFINE_BOXED_TYPE (SoupMessageMetrics, soup_message_metrics, soup_message_metrics_copy, soup_message_metrics_free)
@@ -243,3 +247,118 @@ soup_message_metrics_get_response_end (SoupMessageMetrics *metrics)
return metrics->response_end;
}
+
+/**
+ * soup_message_metrics_get_request_header_bytes_sent:
+ * @metrics: a #SoupMessageMetrics
+ *
+ * Get the number of bytes sent to the network for the request headers.
+ * This value is available right before #SoupMessage::wrote-headers signal
+ * is emitted, but you might get an intermediate value if called before.
+ *
+ * Returns: the request headers bytes sent
+ */
+guint64
+soup_message_metrics_get_request_header_bytes_sent (SoupMessageMetrics *metrics)
+{
+ g_return_val_if_fail (metrics != NULL, 0);
+
+ return metrics->request_header_bytes_sent;
+}
+
+/**
+ * soup_message_metrics_get_request_body_size:
+ * @metrics: a #SoupMessageMetrics
+ *
+ * Get the request body size in bytes. This is the size of the original body
+ * given to the request before any encoding is applied. This value is available
+ * right before #SoupMessage::wrote-body signal is emitted, but you might get
+ * an intermediate value if called before.
+ *
+ * Returns: the request body size
+ */
+guint64
+soup_message_metrics_get_request_body_size (SoupMessageMetrics *metrics)
+{
+ g_return_val_if_fail (metrics != NULL, 0);
+
+ return metrics->request_body_size;
+}
+
+/**
+ * soup_message_metrics_get_request_body_bytes_sent:
+ * @metrics: a #SoupMessageMetrics
+ *
+ * Get the number of bytes sent to the network for the request body. This is
+ * the size of the body sent, after encodings are applied, so it might be
+ * greater than the value returned by soup_message_metrics_get_request_body_size().
+ * This value is available right before #SoupMessage::wrote-body signal is
+ * emitted, but you might get an intermediate value if called before.
+ *
+ * Returns: the request body bytes sent
+ */
+guint64
+soup_message_metrics_get_request_body_bytes_sent (SoupMessageMetrics *metrics)
+{
+ g_return_val_if_fail (metrics != NULL, 0);
+
+ return metrics->request_body_bytes_sent;
+}
+
+/**
+ * soup_message_metrics_get_response_header_bytes_received:
+ * @metrics: a #SoupMessageMetrics
+ *
+ * Get the number of bytes received from the network for the response headers.
+ * This value is available right before #SoupMessage::got-headers signal
+ * is emitted, but you might get an intermediate value if called before.
+ * For resources loaded from the disk cache this value is always 0.
+ *
+ * Returns: the response headers bytes received
+ */
+guint64
+soup_message_metrics_get_response_header_bytes_received (SoupMessageMetrics *metrics)
+{
+ g_return_val_if_fail (metrics != NULL, 0);
+
+ return metrics->response_header_bytes_received;
+}
+
+/**
+ * soup_message_metrics_get_response_body_size:
+ * @metrics: a #SoupMessageMetrics
+ *
+ * Get the response body size in bytes. This is the size of the body as given to the
+ * user after all encodings are applied, so it might be greater than the value
+ * returned by soup_message_metrics_get_response_body_bytes_received(). This value is
+ * available right before #SoupMessage::got-body signal is emitted, but you might get
+ * an intermediate value if called before.
+ *
+ * Returns: the response body size
+ */
+guint64
+soup_message_metrics_get_response_body_size (SoupMessageMetrics *metrics)
+{
+ g_return_val_if_fail (metrics != NULL, 0);
+
+ return metrics->response_body_size;
+}
+
+/**
+ * soup_message_metrics_get_response_body_bytes_received:
+ * @metrics: a #SoupMessageMetrics
+ *
+ * Get the number of bytes received from the network for the response body. This value is
+ * available right before #SoupMessage::got-body signal is emitted, but you might get
+ * an intermediate value if called before.
+ * For resources loaded from the disk cache this value is always 0.
+ *
+ * Returns: the response body bytes received
+ */
+guint64
+soup_message_metrics_get_response_body_bytes_received (SoupMessageMetrics *metrics)
+{
+ g_return_val_if_fail (metrics != NULL, 0);
+
+ return metrics->response_body_bytes_received;
+}
diff --git a/libsoup/soup-message-metrics.h b/libsoup/soup-message-metrics.h
index b5d3f9fb..1c12a62d 100644
--- a/libsoup/soup-message-metrics.h
+++ b/libsoup/soup-message-metrics.h
@@ -48,6 +48,24 @@ guint64 soup_message_metrics_get_response_start (SoupMessageMetrics
SOUP_AVAILABLE_IN_ALL
guint64 soup_message_metrics_get_response_end (SoupMessageMetrics *metrics);
+SOUP_AVAILABLE_IN_ALL
+guint64 soup_message_metrics_get_request_header_bytes_sent (SoupMessageMetrics *metrics);
+
+SOUP_AVAILABLE_IN_ALL
+guint64 soup_message_metrics_get_request_body_size (SoupMessageMetrics *metrics);
+
+SOUP_AVAILABLE_IN_ALL
+guint64 soup_message_metrics_get_request_body_bytes_sent (SoupMessageMetrics *metrics);
+
+SOUP_AVAILABLE_IN_ALL
+guint64 soup_message_metrics_get_response_header_bytes_received (SoupMessageMetrics *metrics);
+
+SOUP_AVAILABLE_IN_ALL
+guint64 soup_message_metrics_get_response_body_size (SoupMessageMetrics *metrics);
+
+SOUP_AVAILABLE_IN_ALL
+guint64 soup_message_metrics_get_response_body_bytes_received (SoupMessageMetrics *metrics);
+
G_DEFINE_AUTOPTR_CLEANUP_FUNC(SoupMessageMetrics, soup_message_metrics_free)
G_END_DECLS
diff --git a/tests/cache-test.c b/tests/cache-test.c
index 517201ea..f7b7f6fc 100644
--- a/tests/cache-test.c
+++ b/tests/cache-test.c
@@ -796,6 +796,12 @@ do_metrics_test (gconstpointer data)
soup_test_request_close_stream (stream, NULL, NULL);
g_object_unref (stream);
g_assert_cmpuint (soup_message_metrics_get_response_end (metrics), >=, soup_message_metrics_get_response_start (metrics));
+ g_assert_cmpuint (soup_message_metrics_get_request_header_bytes_sent (metrics), ==, 0);
+ g_assert_cmpuint (soup_message_metrics_get_request_body_bytes_sent (metrics), ==, 0);
+ g_assert_cmpuint (soup_message_metrics_get_request_body_size (metrics), ==, 0);
+ g_assert_cmpuint (soup_message_metrics_get_response_header_bytes_received (metrics), ==, 0);
+ g_assert_cmpuint (soup_message_metrics_get_response_body_bytes_received (metrics), ==, 0);
+ g_assert_cmpuint (soup_message_metrics_get_response_body_size (metrics), >, 0);
g_object_unref (msg);
body = do_request (session, base_uri, "GET", "/2", NULL,
@@ -840,6 +846,12 @@ do_metrics_test (gconstpointer data)
soup_test_request_close_stream (stream, NULL, NULL);
g_object_unref (stream);
g_assert_cmpuint (soup_message_metrics_get_response_end (metrics), >=, soup_message_metrics_get_response_start (metrics));
+ g_assert_cmpuint (soup_message_metrics_get_request_header_bytes_sent (metrics), ==, 0);
+ g_assert_cmpuint (soup_message_metrics_get_request_body_bytes_sent (metrics), ==, 0);
+ g_assert_cmpuint (soup_message_metrics_get_request_body_size (metrics), ==, 0);
+ g_assert_cmpuint (soup_message_metrics_get_response_header_bytes_received (metrics), ==, 0);
+ g_assert_cmpuint (soup_message_metrics_get_response_body_bytes_received (metrics), ==, 0);
+ g_assert_cmpuint (soup_message_metrics_get_response_body_size (metrics), >, 0);
g_object_unref (msg);
soup_test_session_abort_unref (session);
diff --git a/tests/coding-test.c b/tests/coding-test.c
index 45e9452c..1897ae75 100644
--- a/tests/coding-test.c
+++ b/tests/coding-test.c
@@ -212,6 +212,21 @@ do_coding_test_gzip (CodingTestData *data, gconstpointer test_data)
}
static void
+do_coding_test_gzip_metrics (CodingTestData *data, gconstpointer test_data)
+{
+ GBytes *body;
+ SoupMessageMetrics *metrics;
+
+ soup_message_add_flags (data->msg, SOUP_MESSAGE_COLLECT_METRICS);
+ body = soup_session_send_and_read (data->session, data->msg, NULL, NULL);
+ metrics = soup_message_get_metrics (data->msg);
+ g_assert_nonnull (metrics);
+ g_assert_cmpuint (soup_message_metrics_get_response_body_size (metrics), ==, g_bytes_get_size (body));
+ g_assert_cmpuint (soup_message_metrics_get_response_body_bytes_received (metrics), <, soup_message_metrics_get_response_body_size (metrics));
+ g_bytes_unref (body);
+}
+
+static void
do_coding_test_gzip_with_junk (CodingTestData *data, gconstpointer test_data)
{
GBytes *body;
@@ -345,6 +360,9 @@ main (int argc, char **argv)
g_test_add ("/coding/message/gzip", CodingTestData,
GINT_TO_POINTER (CODING_TEST_DEFAULT),
setup_coding_test, do_coding_test_gzip, teardown_coding_test);
+ g_test_add ("/coding/message/gzip/metrics", CodingTestData,
+ GINT_TO_POINTER (CODING_TEST_DEFAULT),
+ setup_coding_test, do_coding_test_gzip_metrics, teardown_coding_test);
g_test_add ("/coding/message/gzip/with-junk", CodingTestData,
GINT_TO_POINTER (CODING_TEST_DEFAULT),
setup_coding_test, do_coding_test_gzip_with_junk, teardown_coding_test);
diff --git a/tests/request-body-test.c b/tests/request-body-test.c
index 3ba52e1b..1b9938d3 100644
--- a/tests/request-body-test.c
+++ b/tests/request-body-test.c
@@ -33,8 +33,23 @@ wrote_body_data (SoupMessage *msg,
guint count,
PutTestData *ptd)
{
+ SoupMessageMetrics *metrics = soup_message_get_metrics (msg);
+
debug_printf (2, " wrote_body_data, %u bytes\n", count);
ptd->nwrote += count;
+
+ g_assert_cmpuint (soup_message_metrics_get_request_body_bytes_sent (metrics), >=, ptd->nwrote);
+ g_assert_cmpuint (soup_message_metrics_get_request_body_size (metrics), ==, ptd->nwrote);
+}
+
+static void
+wrote_body (SoupMessage *msg,
+ PutTestData *ptd)
+{
+ SoupMessageMetrics *metrics = soup_message_get_metrics (msg);
+
+ g_assert_cmpuint (soup_message_metrics_get_request_body_bytes_sent (metrics), >=, ptd->nwrote);
+ g_assert_cmpuint (soup_message_metrics_get_request_body_size (metrics), ==, ptd->nwrote);
}
static GChecksum *
@@ -107,6 +122,7 @@ do_request_test (gconstpointer data)
SoupMessageHeaders *request_headers;
const char *client_md5, *server_md5;
GChecksum *check;
+ SoupMessageMetrics *metrics;
if (flags & RESTART)
uri = g_uri_parse_relative (base_uri, "/redirect", SOUP_HTTP_URI_FLAGS, NULL);
@@ -118,6 +134,7 @@ do_request_test (gconstpointer data)
client_md5 = g_checksum_get_string (check);
msg = soup_message_new_from_uri ("PUT", uri);
+ soup_message_add_flags (msg, SOUP_MESSAGE_COLLECT_METRICS);
request_headers = soup_message_get_request_headers (msg);
if (flags & BYTES) {
soup_message_set_request_body_from_bytes (msg, ptd.content_type, ptd.bytes);
@@ -137,17 +154,29 @@ do_request_test (gconstpointer data)
g_signal_connect (msg, "wrote-body-data",
G_CALLBACK (wrote_body_data), &ptd);
+ g_signal_connect (msg, "wrote-body",
+ G_CALLBACK (wrote_body), &ptd);
if (flags & ASYNC)
soup_test_session_async_send (session, msg, NULL, NULL);
else
soup_test_session_send_message (session, msg);
soup_test_assert_message_status (msg, SOUP_STATUS_CREATED);
+
+ metrics = soup_message_get_metrics (msg);
if (flags & NULL_STREAM) {
g_assert_cmpint (ptd.nwrote, ==, 0);
g_assert_cmpstr (soup_message_headers_get_one (request_headers, "Content-Length"), ==, "0");
+ g_assert_cmpuint (soup_message_metrics_get_request_body_bytes_sent (metrics), ==, 0);
+ g_assert_cmpuint (soup_message_metrics_get_request_body_size (metrics), ==, 0);
} else {
g_assert_cmpint (g_bytes_get_size (ptd.bytes), ==, ptd.nwrote);
+ if (flags & BYTES) {
+ g_assert_cmpuint (soup_message_metrics_get_request_body_bytes_sent (metrics), ==, ptd.nwrote);
+ } else {
+ g_assert_cmpuint (soup_message_metrics_get_request_body_bytes_sent (metrics), >, ptd.nwrote);
+ }
+ g_assert_cmpuint (soup_message_metrics_get_request_body_size (metrics), ==, ptd.nwrote);
}
server_md5 = soup_message_headers_get_one (soup_message_get_response_headers (msg),
diff --git a/tests/streaming-test.c b/tests/streaming-test.c
index 318d8c4a..7b776d4c 100644
--- a/tests/streaming-test.c
+++ b/tests/streaming-test.c
@@ -82,21 +82,76 @@ server_callback (SoupServer *server,
}
static void
+msg_wrote_headers_cb (SoupMessage *msg,
+ SoupMessageMetrics *metrics)
+{
+ g_assert_cmpuint (soup_message_metrics_get_request_header_bytes_sent (metrics), >, 0);
+ g_assert_cmpuint (soup_message_metrics_get_request_body_bytes_sent (metrics), ==, 0);
+ g_assert_cmpuint (soup_message_metrics_get_request_body_size (metrics), ==, 0);
+}
+
+static void
+msg_got_headers_cb (SoupMessage *msg,
+ SoupMessageMetrics *metrics)
+{
+ g_assert_cmpuint (soup_message_metrics_get_response_header_bytes_received (metrics), >, 0);
+}
+
+static void
+msg_got_body_cb (SoupMessage *msg,
+ SoupMessageMetrics *metrics)
+{
+ g_assert_cmpuint (soup_message_metrics_get_response_body_bytes_received (metrics), >, 0);
+ g_assert_cmpuint (soup_message_metrics_get_response_body_size (metrics), >, 0);
+}
+
+static void
do_request (SoupSession *session, GUri *base_uri, char *path)
{
GUri *uri;
SoupMessage *msg;
GBytes *body;
char *md5;
+ SoupMessageMetrics *metrics;
uri = g_uri_parse_relative (base_uri, path, SOUP_HTTP_URI_FLAGS, NULL);
msg = soup_message_new_from_uri ("GET", uri);
g_uri_unref (uri);
+ soup_message_add_flags (msg, SOUP_MESSAGE_COLLECT_METRICS);
+ metrics = soup_message_get_metrics (msg);
+ g_assert_nonnull (metrics);
+ g_assert_cmpuint (soup_message_metrics_get_request_header_bytes_sent (metrics), ==, 0);
+ g_assert_cmpuint (soup_message_metrics_get_request_body_bytes_sent (metrics), ==, 0);
+ g_assert_cmpuint (soup_message_metrics_get_request_body_size (metrics), ==, 0);
+ g_assert_cmpuint (soup_message_metrics_get_response_header_bytes_received (metrics), ==, 0);
+ g_assert_cmpuint (soup_message_metrics_get_response_body_bytes_received (metrics), ==, 0);
+ g_assert_cmpuint (soup_message_metrics_get_response_body_size (metrics), ==, 0);
+
+ g_signal_connect (msg, "wrote-headers",
+ G_CALLBACK (msg_wrote_headers_cb),
+ metrics);
+ g_signal_connect (msg, "got-headers",
+ G_CALLBACK (msg_got_headers_cb),
+ metrics);
+ g_signal_connect (msg, "got-body",
+ G_CALLBACK (msg_got_body_cb),
+ metrics);
+
body = soup_test_session_async_send (session, msg, NULL, NULL);
soup_test_assert_message_status (msg, SOUP_STATUS_OK);
g_assert_cmpint (g_bytes_get_size (body), ==, g_bytes_get_size (full_response));
+ g_assert_cmpint (soup_message_metrics_get_response_body_size (metrics), ==, g_bytes_get_size (body));
+ g_assert_cmpuint (soup_message_metrics_get_request_header_bytes_sent (metrics), >, 0);
+ g_assert_cmpuint (soup_message_metrics_get_request_body_bytes_sent (metrics), ==, 0);
+ g_assert_cmpuint (soup_message_metrics_get_request_body_size (metrics), ==, 0);
+ g_assert_cmpuint (soup_message_metrics_get_response_header_bytes_received (metrics), >, 0);
+ if (g_str_equal (path, "chunked")) {
+ g_assert_cmpuint (soup_message_metrics_get_response_body_bytes_received (metrics), >, soup_message_metrics_get_response_body_size (metrics));
+ } else {
+ g_assert_cmpuint (soup_message_metrics_get_response_body_bytes_received (metrics), ==, soup_message_metrics_get_response_body_size (metrics));
+ }
md5 = g_compute_checksum_for_data (G_CHECKSUM_MD5,
(guchar *)g_bytes_get_data (body, NULL),