libsoup r1126 - in trunk: . libsoup tests



Author: danw
Date: Sat Apr  5 20:35:35 2008
New Revision: 1126
URL: http://svn.gnome.org/viewvc/libsoup?rev=1126&view=rev

Log:
	* libsoup/soup-message-body.c (soup_message_body_set_accumulate)
	(soup_message_body_get_accumulate): New, replaces
	SOUP_MESSAGE_OVERWRITE_CHUNKS, but can be set on either the
	incoming or outgoing message body.
	(soup_message_body_get_chunk): update to still dtrt if !accumulate
	(soup_message_body_got_chunk, soup_message_body_wrote_chunk): New
	methods to handle accumulating/discarding chunks.

	* libsoup/soup-message-io.c (read_body_chunk): Use
	soup_message_body_got_chunk.
	(io_write): Use soup_message_body_wrote_chunk, to discard unneeded
	chunks after writing them. Fixes most of #522146.

	* libsoup/soup-message.c (soup_message_class_init): add a new
	flag, "server-side", to indicate whether the message is
	client-side or server-side, and update several methods to use it.
	(got_body): Update for accumulate
	(soup_message_set_flags): If the caller changes OVERWRITE_CHUNKS,
	update the corresponding accumulate flag.

	* libsoup/soup-message.h (SOUP_MESSAGE_OVERWRITE_CHUNKS):
	deprecated now

	* tests/chunk-test.c (do_request_test): Use
	soup_message_body_set_accumulate() now, and verify that the chunks
	are being discarded appropriately.
	(do_response_test): Use
	soup_message_body_set_accumulate() instead of OVERWRITE_CHUNKS.

	* tests/pull-api.c (do_fully_async_test)
	(do_synchronously_async_test): Use
	soup_message_body_set_accumulate().


Modified:
   trunk/ChangeLog
   trunk/libsoup/soup-message-body.c
   trunk/libsoup/soup-message-body.h
   trunk/libsoup/soup-message-io.c
   trunk/libsoup/soup-message-private.h
   trunk/libsoup/soup-message.c
   trunk/libsoup/soup-message.h
   trunk/libsoup/soup-server.c
   trunk/tests/chunk-test.c
   trunk/tests/pull-api.c

Modified: trunk/libsoup/soup-message-body.c
==============================================================================
--- trunk/libsoup/soup-message-body.c	(original)
+++ trunk/libsoup/soup-message-body.c	Sat Apr  5 20:35:35 2008
@@ -304,6 +304,8 @@
 	SoupMessageBody body;
 	GSList *chunks, *last;
 	SoupBuffer *flattened;
+	gboolean accumulate;
+	goffset base_offset;
 	int ref_count;
 } SoupMessageBodyPrivate;
 
@@ -321,11 +323,63 @@
 	SoupMessageBodyPrivate *priv;
 
 	priv = g_slice_new0 (SoupMessageBodyPrivate);
+	priv->accumulate = TRUE;
 	priv->ref_count = 1;
 
 	return (SoupMessageBody *)priv;
 }
 
+/**
+ * soup_message_body_set_accumulate:
+ * @body: a #SoupMessageBody
+ * @accumulate: whether or not to accumulate body chunks in @body
+ *
+ * Sets or clears the accumulate flag on @body. (The default value
+ * is %TRUE.)
+ *
+ * If you set this flag to %FALSE on an "incoming" message body (that
+ * is, the %response_body of a client-side message, or %request_body
+ * of a server-side message), this will cause each chunk of the body
+ * to be discarded after its corresponding #SoupMessage::got_chunk
+ * signal is emitted. (This is equivalent to setting the deprecated
+ * %SOUP_MESSAGE_OVERWRITE_CHUNKS flag on the message.)
+ *
+ * If you set this flag to %FALSE on an "outgoing" message body (the
+ * %request_body of a client-side message, or %response_body of a
+ * server-side message), it will cause each chunk of the body to be
+ * discarded after its corresponding #SoupMessage::wrote_chunk signal
+ * is emitted.
+ *
+ * In either case, @body's %data field will not be filled in after the
+ * body is fully sent/received, since the body data will no longer be
+ * available
+ **/
+void
+soup_message_body_set_accumulate (SoupMessageBody *body,
+				  gboolean         accumulate)
+{
+	SoupMessageBodyPrivate *priv = (SoupMessageBodyPrivate *)body;
+
+	priv->accumulate = accumulate;
+}
+
+/**
+ * soup_message_body_get_accumulate:
+ * @body: a #SoupMessageBody
+ *
+ * Gets the accumulate flag on @body; see
+ * soup_message_body_set_accumulate() for details.
+ *
+ * Return value: the accumulate flag for @body.
+ **/
+gboolean
+soup_message_body_get_accumulate (SoupMessageBody *body)
+{
+	SoupMessageBodyPrivate *priv = (SoupMessageBodyPrivate *)body;
+
+	return priv->accumulate;
+}
+
 static void
 append_buffer (SoupMessageBody *body, SoupBuffer *buffer)
 {
@@ -438,6 +492,8 @@
 	GSList *iter;
 	SoupBuffer *chunk;
 
+	g_return_val_if_fail (priv->accumulate == TRUE, NULL);
+
 	if (!priv->flattened) {
 #if GLIB_SIZEOF_SIZE_T < 8
 		g_return_val_if_fail (body->length < G_MAXSIZE, NULL);
@@ -489,6 +545,7 @@
 	GSList *iter;
 	SoupBuffer *chunk = NULL;
 
+	offset -= priv->base_offset;
 	for (iter = priv->chunks; iter; iter = iter->next) {
 		chunk = iter->data;
 
@@ -509,6 +566,63 @@
 	}
 }
 
+/**
+ * soup_message_body_got_chunk:
+ * @body: a #SoupMessageBody
+ * @chunk: a #SoupBuffer received from the network
+ *
+ * Handles the #SoupMessageBody part of receiving a chunk of data from
+ * the network. Normally this means appending @chunk to @body, exactly
+ * as with soup_message_body_append_buffer(), but if you have set
+ * @body's accumulate flag to %FALSE, then that will not happen.
+ *
+ * This is a low-level method which you should not normally need to
+ * use.
+ **/
+void
+soup_message_body_got_chunk (SoupMessageBody *body, SoupBuffer *chunk)
+{
+	SoupMessageBodyPrivate *priv = (SoupMessageBodyPrivate *)body;
+
+	if (!priv->accumulate)
+		return;
+
+	soup_message_body_append_buffer (body, chunk);
+}
+
+/**
+ * soup_message_body_wrote_chunk:
+ * @body: a #SoupMessageBody
+ * @chunk: a #SoupBuffer received from the network
+ *
+ * Handles the #SoupMessageBody part of writing a chunk of data to the
+ * network. Normally this is a no-op, but if you have set @body's
+ * accumulate flag to %FALSE, then this will cause @chunk (and any
+ * chunks preceding it in @body) to be discarded to free up memory.
+ *
+ * This is a low-level method which you should not normally need to
+ * use.
+ **/
+void
+soup_message_body_wrote_chunk (SoupMessageBody *body, SoupBuffer *chunk)
+{
+	SoupMessageBodyPrivate *priv = (SoupMessageBodyPrivate *)body;
+	SoupBuffer *chunk2;
+
+	if (priv->accumulate)
+		return;
+
+	do {
+		chunk2 = priv->chunks->data;
+		priv->chunks = g_slist_remove (priv->chunks, chunk2);
+		priv->base_offset += chunk2->length;
+		soup_buffer_free (chunk2);
+	} while (priv->chunks && chunk2 != chunk);
+
+	if (!priv->chunks)
+		priv->last = NULL;
+}
+
 static SoupMessageBody *
 soup_message_body_copy (SoupMessageBody *body)
 {

Modified: trunk/libsoup/soup-message-body.h
==============================================================================
--- trunk/libsoup/soup-message-body.h	(original)
+++ trunk/libsoup/soup-message-body.h	Sat Apr  5 20:35:35 2008
@@ -51,6 +51,10 @@
 
 SoupMessageBody *soup_message_body_new           (void);
 
+void             soup_message_body_set_accumulate(SoupMessageBody *body,
+						  gboolean         accumulate);
+gboolean         soup_message_body_get_accumulate(SoupMessageBody *body);
+
 void             soup_message_body_append        (SoupMessageBody *body,
 						  SoupMemoryUse    use,
 						  gconstpointer    data,
@@ -65,6 +69,11 @@
 SoupBuffer      *soup_message_body_get_chunk     (SoupMessageBody *body,
 						  goffset          offset);
 
+void             soup_message_body_got_chunk     (SoupMessageBody *body,
+						  SoupBuffer      *chunk);
+void             soup_message_body_wrote_chunk   (SoupMessageBody *body,
+						  SoupBuffer      *chunk);
+
 void             soup_message_body_free          (SoupMessageBody *body);
 
 G_END_DECLS

Modified: trunk/libsoup/soup-message-io.c
==============================================================================
--- trunk/libsoup/soup-message-io.c	(original)
+++ trunk/libsoup/soup-message-io.c	Sat Apr  5 20:35:35 2008
@@ -261,8 +261,7 @@
 /* Reads as much message body data as is available on io->sock (but no
  * further than the end of the current message body or chunk). On a
  * successful read, emits "got_chunk" (possibly multiple times), and
- * if %SOUP_MESSAGE_OVERWRITE_CHUNKS wasn't set, appends the chunk
- * to io->read_body.
+ * (unless told not to) appends the chunk to io->read_body.
  *
  * See the note at read_metadata() for an explanation of the return
  * value.
@@ -306,8 +305,7 @@
 
 		if (status == SOUP_SOCKET_OK && nread) {
 			buffer->length = nread;
-			if (!(priv->msg_flags & SOUP_MESSAGE_OVERWRITE_CHUNKS))
-				soup_message_body_append_buffer (io->read_body, buffer);
+			soup_message_body_got_chunk (io->read_body, buffer);
 
 			io->read_length -= nread;
 
@@ -549,6 +547,7 @@
 				 io->write_chunk->length, TRUE))
 			return;
 
+		soup_message_body_wrote_chunk (io->write_body, io->write_chunk);
 		soup_buffer_free (io->write_chunk);
 		io->write_body_offset += io->write_chunk->length;
 		io->write_chunk = NULL;
@@ -591,6 +590,7 @@
 				 io->write_chunk->length, TRUE))
 			return;
 
+		soup_message_body_wrote_chunk (io->write_body, io->write_chunk);
 		soup_buffer_free (io->write_chunk);
 		io->write_chunk = NULL;
 

Modified: trunk/libsoup/soup-message-private.h
==============================================================================
--- trunk/libsoup/soup-message-private.h	(original)
+++ trunk/libsoup/soup-message-private.h	Sat Apr  5 20:35:35 2008
@@ -27,6 +27,7 @@
 	GDestroyNotify     chunk_allocator_dnotify;
 
 	guint              msg_flags;
+	gboolean           server_side;
 
 	SoupHTTPVersion    http_version, orig_http_version;
 

Modified: trunk/libsoup/soup-message.c
==============================================================================
--- trunk/libsoup/soup-message.c	(original)
+++ trunk/libsoup/soup-message.c	Sat Apr  5 20:35:35 2008
@@ -62,11 +62,16 @@
  * filled in right before libsoup writes the request to the network,
  * but you should not count on this; use soup_message_body_flatten()
  * if you want to ensure that %data is filled in. @response_body's
- * %data will be filled in before #SoupMessage::finished is emitted,
- * unless you set the %SOUP_MESSAGE_OVERWRITE_CHUNKS flag.
+ * %data will be filled in before #SoupMessage::finished is emitted.
  *
  * For a server-side #SoupMessage, @request_body's %data will be
  * filled in before #SoupMessage::got_body is emitted.
+ *
+ * To prevent the %data field from being filled in at all (eg, if you
+ * are handling the data from a #SoupMessage::got_chunk, and so don't
+ * need to see it all at the end), call
+ * soup_message_body_set_accumulate() on @response_body or
+ * @request_body as appropriate, passing %FALSE.
  **/
 
 G_DEFINE_TYPE (SoupMessage, soup_message, G_TYPE_OBJECT)
@@ -98,6 +103,7 @@
 	PROP_URI,
 	PROP_HTTP_VERSION,
 	PROP_FLAGS,
+	PROP_SERVER_SIDE,
 	PROP_STATUS_CODE,
 	PROP_REASON_PHRASE,
 
@@ -446,6 +452,13 @@
 				    0,
 				    G_PARAM_READWRITE));
 	g_object_class_install_property (
+		object_class, PROP_SERVER_SIDE,
+		g_param_spec_boolean (SOUP_MESSAGE_SERVER_SIDE,
+				      "Server-side",
+				      "Whether or not the message is server-side rather than client-side",
+				      FALSE,
+				      G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY));
+	g_object_class_install_property (
 		object_class, PROP_STATUS_CODE,
 		g_param_spec_uint (SOUP_MESSAGE_STATUS_CODE,
 				   "Status code",
@@ -466,6 +479,7 @@
 	      const GValue *value, GParamSpec *pspec)
 {
 	SoupMessage *msg = SOUP_MESSAGE (object);
+	SoupMessagePrivate *priv = SOUP_MESSAGE_GET_PRIVATE (msg);
 
 	switch (prop_id) {
 	case PROP_METHOD:
@@ -480,6 +494,9 @@
 	case PROP_FLAGS:
 		soup_message_set_flags (msg, g_value_get_flags (value));
 		break;
+	case PROP_SERVER_SIDE:
+		priv->server_side = g_value_get_boolean (value);
+		break;
 	case PROP_STATUS_CODE:
 		soup_message_set_status (msg, g_value_get_uint (value));
 		break;
@@ -513,6 +530,9 @@
 	case PROP_FLAGS:
 		g_value_set_flags (value, priv->msg_flags);
 		break;
+	case PROP_SERVER_SIDE:
+		g_value_set_boolean (value, priv->server_side);
+		break;
 	case PROP_STATUS_CODE:
 		g_value_set_uint (value, msg->status_code);
 		break;
@@ -752,15 +772,13 @@
 got_body (SoupMessage *req)
 {
 	SoupMessagePrivate *priv = SOUP_MESSAGE_GET_PRIVATE (req);
+	SoupMessageBody *body;
 
-	if (!(priv->msg_flags & SOUP_MESSAGE_OVERWRITE_CHUNKS)) {
+	body = priv->server_side ? req->request_body : req->response_body;
+	if (soup_message_body_get_accumulate (body)) {
 		SoupBuffer *buffer;
 
-		/* Figure out *which* body we read, and flatten it. */
-		if (req->status_code == 0)
-			buffer = soup_message_body_flatten (req->request_body);
-		else
-			buffer = soup_message_body_flatten (req->response_body);
+		buffer = soup_message_body_flatten (body);
 		soup_buffer_free (buffer);
 	}
 }
@@ -838,13 +856,7 @@
 	if (priv->io_status != SOUP_MESSAGE_IO_STATUS_RUNNING)
 		return;
 
-	/* If status_code is SOUP_STATUS_NONE, we're still processing
-	 * the request side; if it's not, we're processing the
-	 * response side.
-	 */
-	hdrs = (msg->status_code == SOUP_STATUS_NONE) ?
-		msg->request_headers : msg->response_headers;
-
+	hdrs = priv->server_side ? msg->request_headers : msg->response_headers;
 	if (soup_message_headers_get (hdrs, header_name)) {
 		closure->marshal (closure, return_value, n_param_values,
 				  param_values, invocation_hint,
@@ -1103,11 +1115,9 @@
  * SoupMessageFlags:
  * @SOUP_MESSAGE_NO_REDIRECT: The session should not follow redirect
  * (3xx) responses received by this message.
- * @SOUP_MESSAGE_OVERWRITE_CHUNKS: Each chunk of the response will be
- * freed after its corresponding %got_chunk signal is emitted, meaning
- * %response will still be empty after the message is complete. You
- * can use this to save memory if you expect the response to be large
- * and you are able to process it a chunk at a time.
+ * @SOUP_MESSAGE_OVERWRITE_CHUNKS: Deprecated: equivalent to calling
+ * soup_message_body_set_accumulate() on the incoming message body
+ * (ie, %response_body for a client-side request), passing %FALSE.
  *
  * Various flags that can be set on a #SoupMessage to alter its
  * behavior.
@@ -1123,9 +1133,18 @@
 void
 soup_message_set_flags (SoupMessage *msg, SoupMessageFlags flags)
 {
+	SoupMessagePrivate *priv;
+
 	g_return_if_fail (SOUP_IS_MESSAGE (msg));
+	priv = SOUP_MESSAGE_GET_PRIVATE (msg);
+
+	if ((priv->msg_flags ^ flags) & SOUP_MESSAGE_OVERWRITE_CHUNKS) {
+		soup_message_body_set_accumulate (
+			priv->server_side ? msg->request_body : msg->response_body,
+			!(flags & SOUP_MESSAGE_OVERWRITE_CHUNKS));
+	}
 
-	SOUP_MESSAGE_GET_PRIVATE (msg)->msg_flags = flags;
+	priv->msg_flags = flags;
 	g_object_notify (G_OBJECT (msg), SOUP_MESSAGE_FLAGS);
 }
 
@@ -1387,23 +1406,22 @@
  * %length to indicate how much data it read.
  *
  * Generally, a custom chunk allocator would be used in conjunction
- * with %SOUP_MESSAGE_OVERWRITE_CHUNKS and #SoupMessage::got_chunk, as
- * part of a strategy to avoid unnecessary copying of data. However,
- * you cannot assume that every call to the allocator will be followed
- * by a call to your %got_chunk handler; if an I/O error occurs, then
- * the buffer will be unreffed without ever having been used. If your
- * buffer-allocation strategy requires special cleanup, use
- * soup_buffer_new_with_owner() rather than doing the cleanup from the
- * %got_chunk handler.
- *
- * The other thing to remember when using
- * %SOUP_MESSAGE_OVERWRITE_CHUNKS is that the buffer passed to the
- * %got_chunk handler will be unreffed after the handler returns, just
- * as it would be in the non-custom-allocated case. If you want to
- * hand the chunk data off to some other part of your program to use
- * later, you'll need to ref the #SoupBuffer (or its owner, in the
- * soup_buffer_new_with_owner() case) to ensure that the data remains
- * valid.
+ * with soup_message_body_set_accumulate() %FALSE and
+ * #SoupMessage::got_chunk, as part of a strategy to avoid unnecessary
+ * copying of data. However, you cannot assume that every call to the
+ * allocator will be followed by a call to your %got_chunk handler; if
+ * an I/O error occurs, then the buffer will be unreffed without ever
+ * having been used. If your buffer-allocation strategy requires
+ * special cleanup, use soup_buffer_new_with_owner() rather than doing
+ * the cleanup from the %got_chunk handler.
+ *
+ * The other thing to remember when using non-accumulating message
+ * bodies is that the buffer passed to the %got_chunk handler will be
+ * unreffed after the handler returns, just as it would be in the
+ * non-custom-allocated case. If you want to hand the chunk data off
+ * to some other part of your program to use later, you'll need to ref
+ * the #SoupBuffer (or its owner, in the soup_buffer_new_with_owner()
+ * case) to ensure that the data remains valid.
  **/
 void
 soup_message_set_chunk_allocator (SoupMessage *msg,

Modified: trunk/libsoup/soup-message.h
==============================================================================
--- trunk/libsoup/soup-message.h	(original)
+++ trunk/libsoup/soup-message.h	Sat Apr  5 20:35:35 2008
@@ -64,6 +64,7 @@
 #define SOUP_MESSAGE_URI           "uri"
 #define SOUP_MESSAGE_HTTP_VERSION  "http-version"
 #define SOUP_MESSAGE_FLAGS         "flags"
+#define SOUP_MESSAGE_SERVER_SIDE   "server-side"
 #define SOUP_MESSAGE_STATUS_CODE   "status-code"
 #define SOUP_MESSAGE_REASON_PHRASE "reason-phrase"
 
@@ -100,7 +101,9 @@
 
 typedef enum {
 	SOUP_MESSAGE_NO_REDIRECT      = (1 << 1),
+#ifndef LIBSOUP_DISABLE_DEPRECATED
 	SOUP_MESSAGE_OVERWRITE_CHUNKS = (1 << 3),
+#endif
 } SoupMessageFlags;
 
 void           soup_message_set_flags           (SoupMessage        *msg,

Modified: trunk/libsoup/soup-server.c
==============================================================================
--- trunk/libsoup/soup-server.c	(original)
+++ trunk/libsoup/soup-server.c	Sat Apr  5 20:35:35 2008
@@ -807,7 +807,9 @@
 	soup_client_context_cleanup (client);
 
 	/* Listen for another request on this connection */
-	msg = g_object_new (SOUP_TYPE_MESSAGE, NULL);
+	msg = g_object_new (SOUP_TYPE_MESSAGE,
+			    SOUP_MESSAGE_SERVER_SIDE, TRUE,
+			    NULL);
         soup_message_headers_set_encoding (msg->response_headers,
                                            SOUP_ENCODING_CONTENT_LENGTH);
 	if (priv->server_header) {

Modified: trunk/tests/chunk-test.c
==============================================================================
--- trunk/tests/chunk-test.c	(original)
+++ trunk/tests/chunk-test.c	Sat Apr  5 20:35:35 2008
@@ -36,12 +36,10 @@
 
 	debug_printf (2, "  writing chunk\n");
 
-#ifdef IMPLEMENTED_OVERWRITE_CHUNKS_FOR_REQUESTS
 	if (ptd->next > 0 && ptd->chunks[ptd->next - 1]) {
 		debug_printf (1, "  error: next chunk requested before last one freed!\n");
 		errors++;
 	}
-#endif
 
 	if (ptd->next < G_N_ELEMENTS (ptd->chunks)) {
 		soup_message_body_append_buffer (msg->request_body,
@@ -117,6 +115,7 @@
 
 	msg = soup_message_new_from_uri ("PUT", base_uri);
 	soup_message_headers_set_encoding (msg->request_headers, SOUP_ENCODING_CHUNKED);
+	soup_message_body_set_accumulate (msg->request_body, FALSE);
 	soup_message_set_chunk_allocator (msg, error_chunk_allocator, NULL, NULL);
 	g_signal_connect (msg, "wrote_headers",
 			  G_CALLBACK (write_next_chunk), &ptd);
@@ -206,7 +205,7 @@
 	gtd.check = g_checksum_new (G_CHECKSUM_MD5);
 
 	msg = soup_message_new_from_uri ("GET", base_uri);
-	soup_message_set_flags (msg, SOUP_MESSAGE_OVERWRITE_CHUNKS);
+	soup_message_body_set_accumulate (msg->response_body, FALSE);
 	soup_message_set_chunk_allocator (msg, chunk_allocator, &gtd, NULL);
 	g_signal_connect (msg, "got_chunk",
 			  G_CALLBACK (got_chunk), &gtd);

Modified: trunk/tests/pull-api.c
==============================================================================
--- trunk/tests/pull-api.c	(original)
+++ trunk/tests/pull-api.c	Sat Apr  5 20:35:35 2008
@@ -98,10 +98,10 @@
 	ad.expected_status = expected_status;
 
 	/* Since we aren't going to look at the final value of
-	 * msg->response, we set OVERWRITE_CHUNKS, to tell libsoup to
-	 * not even bother generating it.
+	 * msg->response_body, we tell libsoup to not even bother
+	 * generating it.
 	 */
-	soup_message_set_flags (msg, SOUP_MESSAGE_OVERWRITE_CHUNKS);
+	soup_message_body_set_accumulate (msg->response_body, FALSE);
 
 	/* Connect to "got_headers", from which we'll decide where to
 	 * go next.
@@ -289,10 +289,10 @@
 	msg = soup_message_new (SOUP_METHOD_GET, uri);
 	g_free (uri);
 
-	/* As in the fully-async case, we set OVERWRITE_CHUNKS as an
+	/* As in the fully-async case, we turn off accumulate, as an
 	 * optimization.
 	 */
-	soup_message_set_flags (msg, SOUP_MESSAGE_OVERWRITE_CHUNKS);
+	soup_message_body_set_accumulate (msg->response_body, FALSE);
 
 	/* Send the message, get back headers */
 	sync_async_send (session, msg);



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