[libsoup/carlosgc/metrics: 2/2] metrics: add support for size metrics




commit f3afe96b865aa0ec1d9634cbcaff7eb1bc2dc4fc
Author: Carlos Garcia Campos <cgarcia igalia com>
Date:   Fri Apr 9 10:16:56 2021 +0200

    metrics: add support for size metrics

 docs/reference/libsoup-3.0-sections.txt |   7 ++
 libsoup/cache/soup-cache.c              |   7 +-
 libsoup/server/soup-server-io.c         |   2 +-
 libsoup/soup-body-output-stream.c       |  18 ++++-
 libsoup/soup-client-input-stream.c      |  15 +++++
 libsoup/soup-filter-input-stream.c      |  65 +++++++++++++-----
 libsoup/soup-logger.c                   |  17 +++--
 libsoup/soup-message-io-data.c          |  10 ++-
 libsoup/soup-message-io-data.h          |   1 +
 libsoup/soup-message-io.c               |  55 ++++++++++++++-
 libsoup/soup-message-metrics-private.h  |   9 ++-
 libsoup/soup-message-metrics.c          | 115 ++++++++++++++++++++++++++++++++
 libsoup/soup-message-metrics.h          |  18 +++++
 tests/cache-test.c                      |  12 ++++
 tests/coding-test.c                     |  18 +++++
 tests/request-body-test.c               |  29 ++++++++
 tests/streaming-test.c                  |  55 +++++++++++++++
 17 files changed, 421 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 70bc72ab..e19a890b 100644
--- a/libsoup/soup-message-metrics.c
+++ b/libsoup/soup-message-metrics.c
@@ -237,3 +237,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
@@ -211,6 +211,21 @@ do_coding_test_gzip (CodingTestData *data, gconstpointer test_data)
        g_bytes_unref (body);
 }
 
+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)
 {
@@ -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
@@ -81,6 +81,30 @@ server_callback (SoupServer        *server,
                          G_CALLBACK (free_offset), offset);
 }
 
+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)
 {
@@ -88,15 +112,46 @@ do_request (SoupSession *session, GUri *base_uri, char *path)
        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),


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