[libgdata] [core] Fix premature termination of request body on buffer underflow



commit ef78512a717eb47533ff45c0f6222cef0691fbe3
Author: Philip Withnall <philip tecnocode co uk>
Date:   Wed Aug 5 22:53:31 2009 +0100

    [core] Fix premature termination of request body on buffer underflow
    
    EOF and buffer underflow returns from gdata_buffer_pop_data() were getting
    confused and, if data wasn't pushed to a GDataUploadStream fast enough, would
    result in premature termination of the request body.

 gdata/gdata-buffer.c          |   23 ++++++++++++++++++-----
 gdata/gdata-buffer.h          |    4 ++--
 gdata/gdata-download-stream.c |    2 +-
 gdata/gdata-upload-stream.c   |    8 ++++----
 4 files changed, 25 insertions(+), 12 deletions(-)
---
diff --git a/gdata/gdata-buffer.c b/gdata/gdata-buffer.c
index 8bb1e2f..3da67b4 100644
--- a/gdata/gdata-buffer.c
+++ b/gdata/gdata-buffer.c
@@ -167,6 +167,7 @@ pop_cancelled_cb (GCancellable *cancellable, CancelledData *data)
  * @self: a #GDataBuffer
  * @data: return location for the popped data
  * @length_requested: the number of bytes of data requested
+ * @reached_eof: return location for a value which is %TRUE when we've reached EOF, %FALSE otherwise, or %NULL
  * @cancellable: a #GCancellable, or %NULL
  *
  * Pops up to @length_requested bytes off the head of the buffer and copies them to @data, which must be allocated by
@@ -188,7 +189,7 @@ pop_cancelled_cb (GCancellable *cancellable, CancelledData *data)
  * Since: 0.5.0
  **/
 gsize
-gdata_buffer_pop_data (GDataBuffer *self, guint8 *data, gsize length_requested, GCancellable *cancellable)
+gdata_buffer_pop_data (GDataBuffer *self, guint8 *data, gsize length_requested, gboolean *reached_eof, GCancellable *cancellable)
 {
 	GDataBufferChunk *chunk;
 	gsize return_length = 0, length_remaining;
@@ -204,6 +205,10 @@ gdata_buffer_pop_data (GDataBuffer *self, guint8 *data, gsize length_requested,
 
 	g_static_mutex_lock (&(self->mutex));
 
+	/* Set reached_eof */
+	if (reached_eof != NULL)
+		*reached_eof = self->reached_eof;
+
 	if (self->reached_eof == TRUE && length_requested > self->total_length) {
 		/* Return data up to the EOF */
 		return_length = self->total_length;
@@ -294,18 +299,26 @@ gdata_buffer_pop_data (GDataBuffer *self, guint8 *data, gsize length_requested,
  * gdata_buffer_pop_all_data:
  * @self: a #GDataBuffer
  * @data: return location for the popped data
+ * @maximum_length: the maximum number of bytes to return
+ * @reached_eof: return location for a value which is %TRUE when we've reached EOF, %FALSE otherwise, or %NULL
  *
  * Pops as much data as possible off the #GDataBuffer, up to a limit of @maxium_length bytes. If fewer bytes exist
  * in the buffer, fewer bytes will be returned. If more bytes exist in the buffer, @maximum_length bytes will be returned.
  *
- * This function will never block.
+ * If %0 bytes exist in the buffer, this function will block until data is available. Otherwise, it will never block.
  *
- * Return value: the number of bytes returned in @data
+ * Return value: the number of bytes returned in @data (guaranteed to be more than %0 and at most @maximum_length)
  *
  * Since: 0.5.0
  **/
 gsize
-gdata_buffer_pop_data_limited (GDataBuffer *self, guint8 *data, gsize maximum_length)
+gdata_buffer_pop_data_limited (GDataBuffer *self, guint8 *data, gsize maximum_length, gboolean *reached_eof)
 {
-	return gdata_buffer_pop_data (self, data, MIN (maximum_length, self->total_length), NULL);
+	/* If there's no data in the buffer, block until some is available */
+	g_static_mutex_lock (&(self->mutex));
+	if (self->total_length == 0 && self->reached_eof == FALSE)
+		g_cond_wait (self->cond, g_static_mutex_get_mutex (&(self->mutex)));
+	g_static_mutex_unlock (&(self->mutex));
+
+	return gdata_buffer_pop_data (self, data, MIN (maximum_length, self->total_length), reached_eof, NULL);
 }
diff --git a/gdata/gdata-buffer.h b/gdata/gdata-buffer.h
index be0c622..9591dc1 100644
--- a/gdata/gdata-buffer.h
+++ b/gdata/gdata-buffer.h
@@ -51,8 +51,8 @@ GDataBuffer *gdata_buffer_new (void) G_GNUC_WARN_UNUSED_RESULT;
 void gdata_buffer_free (GDataBuffer *self);
 
 gboolean gdata_buffer_push_data (GDataBuffer *self, const guint8 *data, gsize length);
-gsize gdata_buffer_pop_data (GDataBuffer *self, guint8 *data, gsize length_requested, GCancellable *cancellable);
-gsize gdata_buffer_pop_data_limited (GDataBuffer *self, guint8 *data, gsize maximum_length);
+gsize gdata_buffer_pop_data (GDataBuffer *self, guint8 *data, gsize length_requested, gboolean *reached_eof, GCancellable *cancellable);
+gsize gdata_buffer_pop_data_limited (GDataBuffer *self, guint8 *data, gsize maximum_length, gboolean *reached_eof);
 
 G_END_DECLS
 
diff --git a/gdata/gdata-download-stream.c b/gdata/gdata-download-stream.c
index bfd3b58..4891f2c 100644
--- a/gdata/gdata-download-stream.c
+++ b/gdata/gdata-download-stream.c
@@ -272,7 +272,7 @@ gdata_download_stream_read (GInputStream *stream, void *buffer, gsize count, GCa
 	}
 
 	/* Read the data off the buffer */
-	length_read = (gssize) gdata_buffer_pop_data (priv->buffer, buffer, count, cancellable);
+	length_read = (gssize) gdata_buffer_pop_data (priv->buffer, buffer, count, NULL, cancellable);
 
 	if (g_cancellable_set_error_if_cancelled (cancellable, error) == TRUE) {
 		/* Handle cancellation */
diff --git a/gdata/gdata-upload-stream.c b/gdata/gdata-upload-stream.c
index bbc0a7d..c83a86f 100644
--- a/gdata/gdata-upload-stream.c
+++ b/gdata/gdata-upload-stream.c
@@ -454,8 +454,7 @@ gdata_upload_stream_close (GOutputStream *stream, GCancellable *cancellable, GEr
  * If we don't return from this signal handler, the message is never paused, and thus
  * Bad Things don't happen (due to the bug, messages can be paused, but not unpaused).
  * Obviously this means that our memory usage will increase, and we'll eventually end
- * up storing the entire request body in memory, but that's unavoidable at this point.
- * The additions to work around the bug are commented as "bgo#522147". */
+ * up storing the entire request body in memory, but that's unavoidable at this point. */
 static void
 wrote_chunk_cb (SoupMessage *message, GDataUploadStream *self)
 {
@@ -463,6 +462,7 @@ wrote_chunk_cb (SoupMessage *message, GDataUploadStream *self)
 
 	GDataUploadStreamPrivate *priv = self->priv;
 	gsize length;
+	gboolean reached_eof = FALSE;
 	guint8 buffer[CHUNK_SIZE];
 
 	/* Signal the main thread that the chunk has been written */
@@ -476,8 +476,8 @@ wrote_chunk_cb (SoupMessage *message, GDataUploadStream *self)
 	 * we could deadlock if we block on getting CHUNK_SIZE bytes at the end of the stream. write() could
 	 * easily be called with fewer bytes, but has no way to notify us that we've reached the end of the
 	 * stream, so we'd happily block on receiving more bytes which weren't forthcoming. */
-	length = gdata_buffer_pop_data_limited (priv->buffer, buffer, CHUNK_SIZE);
-	if (length == 0) {
+	length = gdata_buffer_pop_data_limited (priv->buffer, buffer, CHUNK_SIZE, &reached_eof);
+	if (reached_eof == TRUE) {
 		/* We've reached the end of the stream, so append the footer (if appropriate) and stop */
 		if (priv->entry != NULL) {
 			const gchar *footer = "\n--" BOUNDARY_STRING "--";



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