[libsoup/giobased: 7/11] a bit messy, but push chunked encoding into sos
- From: Dan Winship <danw src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [libsoup/giobased: 7/11] a bit messy, but push chunked encoding into sos
- Date: Tue, 22 Mar 2011 13:07:14 +0000 (UTC)
commit 0f32f7537aa0440e0a826d1f3b6647f1b9eb7fc0
Author: Dan Winship <danw gnome org>
Date: Thu Dec 9 22:27:09 2010 +0100
a bit messy, but push chunked encoding into sos
libsoup/Makefile.am | 2 +
libsoup/soup-message-io.c | 138 +++++-----------------
libsoup/soup-output-stream.c | 265 ++++++++++++++++++++++++++++++++++++++++++
libsoup/soup-output-stream.h | 55 +++++++++
4 files changed, 353 insertions(+), 107 deletions(-)
---
diff --git a/libsoup/Makefile.am b/libsoup/Makefile.am
index aec7948..951a6b9 100644
--- a/libsoup/Makefile.am
+++ b/libsoup/Makefile.am
@@ -154,6 +154,8 @@ libsoup_2_4_la_SOURCES = \
soup-method.c \
soup-misc.c \
soup-multipart.c \
+ soup-output-stream.h \
+ soup-output-stream.c \
soup-password-manager.c \
soup-path-map.h \
soup-path-map.c \
diff --git a/libsoup/soup-message-io.c b/libsoup/soup-message-io.c
index 4d66329..444ec2e 100644
--- a/libsoup/soup-message-io.c
+++ b/libsoup/soup-message-io.c
@@ -18,6 +18,7 @@
#include "soup-message-private.h"
#include "soup-message-queue.h"
#include "soup-misc.h"
+#include "soup-output-stream.h"
#include "soup-socket.h"
#include "soup-ssl.h"
@@ -31,9 +32,6 @@ typedef enum {
SOUP_MESSAGE_IO_STATE_HEADERS,
SOUP_MESSAGE_IO_STATE_BLOCKING,
SOUP_MESSAGE_IO_STATE_BODY,
- SOUP_MESSAGE_IO_STATE_CHUNK_SIZE,
- SOUP_MESSAGE_IO_STATE_CHUNK,
- SOUP_MESSAGE_IO_STATE_CHUNK_END,
SOUP_MESSAGE_IO_STATE_TRAILERS,
SOUP_MESSAGE_IO_STATE_FINISHING,
SOUP_MESSAGE_IO_STATE_DONE
@@ -51,7 +49,7 @@ typedef struct {
SoupSocket *sock;
SoupInputStream *istream;
- GOutputStream *ostream;
+ SoupOutputStream *ostream;
GMainContext *async_context;
gboolean blocking;
@@ -181,7 +179,7 @@ soup_message_io_finished (SoupMessage *msg)
}
static gboolean io_read (SoupInputStream *stream, SoupMessage *msg);
-static gboolean io_write (GOutputStream *stream, SoupMessage *msg);
+static gboolean io_write (SoupOutputStream *stream, SoupMessage *msg);
static gboolean
request_is_idempotent (SoupMessage *msg)
@@ -551,7 +549,7 @@ write_data (SoupMessage *msg, const char *data, guint len, gboolean body)
{
SoupMessagePrivate *priv = SOUP_MESSAGE_GET_PRIVATE (msg);
SoupMessageIOData *io = priv->io_data;
- gsize nwrote;
+ gssize nwrote;
GError *error = NULL;
SoupBuffer *chunk;
const char *start;
@@ -561,18 +559,12 @@ write_data (SoupMessage *msg, const char *data, guint len, gboolean body)
return FALSE;
}
- while (len > io->written) {
- if (io->blocking) {
- nwrote = g_output_stream_write (io->ostream,
- data + io->written,
- len - io->written,
- io->cancellable, &error);
- } else {
- nwrote = g_pollable_output_stream_write_nonblocking (
- G_POLLABLE_OUTPUT_STREAM (io->ostream),
- data + io->written, len - io->written,
- io->cancellable, &error);
- }
+ do {
+ nwrote = soup_output_stream_write (io->ostream,
+ data + io->written,
+ len - io->written,
+ io->blocking,
+ io->cancellable, &error);
if (g_error_matches (error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK)) {
g_error_free (error);
@@ -598,21 +590,12 @@ write_data (SoupMessage *msg, const char *data, guint len, gboolean body)
soup_buffer_free (chunk);
SOUP_MESSAGE_IO_RETURN_VAL_IF_CANCELLED_OR_PAUSED (FALSE);
}
- }
+ } while (len > io->written);
io->written = 0;
return TRUE;
}
-static inline SoupMessageIOState
-io_body_state (SoupEncoding encoding)
-{
- if (encoding == SOUP_ENCODING_CHUNKED)
- return SOUP_MESSAGE_IO_STATE_CHUNK_SIZE;
- else
- return SOUP_MESSAGE_IO_STATE_BODY;
-}
-
/*
* There are two request/response formats: the basic request/response,
* possibly with one or more unsolicited informational responses (such
@@ -640,7 +623,7 @@ io_body_state (SoupEncoding encoding)
*/
static gboolean
-io_write (GOutputStream *stream, SoupMessage *msg)
+io_write (SoupOutputStream *stream, SoupMessage *msg)
{
SoupMessagePrivate *priv = SOUP_MESSAGE_GET_PRIVATE (msg);
SoupMessageIOData *io = priv->io_data;
@@ -680,6 +663,9 @@ io_write (GOutputStream *stream, SoupMessage *msg)
io->write_length = soup_message_headers_get_content_length (hdrs);
}
+ soup_output_stream_set_encoding (io->ostream,
+ io->write_encoding);
+
if (io->mode == SOUP_MESSAGE_IO_SERVER &&
SOUP_STATUS_IS_INFORMATIONAL (msg->status_code)) {
if (msg->status_code == SOUP_STATUS_CONTINUE) {
@@ -702,7 +688,7 @@ io_write (GOutputStream *stream, SoupMessage *msg)
io->write_state = SOUP_MESSAGE_IO_STATE_BLOCKING;
io->read_state = SOUP_MESSAGE_IO_STATE_HEADERS;
} else {
- io->write_state = io_body_state (io->write_encoding);
+ io->write_state = SOUP_MESSAGE_IO_STATE_BODY;
/* If the client was waiting for a Continue
* but we sent something else, then they're
@@ -736,7 +722,9 @@ io_write (GOutputStream *stream, SoupMessage *msg)
case SOUP_MESSAGE_IO_STATE_BODY:
- if (!io->write_length && io->write_encoding != SOUP_ENCODING_EOF) {
+ if (!io->write_length &&
+ io->write_encoding != SOUP_ENCODING_EOF &&
+ io->write_encoding != SOUP_ENCODING_CHUNKED) {
wrote_body:
io->write_state = SOUP_MESSAGE_IO_STATE_FINISHING;
@@ -752,8 +740,12 @@ io_write (GOutputStream *stream, SoupMessage *msg)
soup_message_io_pause (msg);
return FALSE;
}
- if (io->write_chunk->length > io->write_length &&
- io->write_encoding != SOUP_ENCODING_EOF) {
+ if (io->write_encoding == SOUP_ENCODING_EOF) {
+ if (!io->write_chunk->length)
+ goto wrote_body;
+ } else if (io->write_encoding == SOUP_ENCODING_CHUNKED) {
+ io->write_length = io->write_chunk->length;
+ } else if (io->write_chunk->length > io->write_length) {
/* App is trying to write more than it
* claimed it would; we have to truncate.
*/
@@ -762,95 +754,27 @@ io_write (GOutputStream *stream, SoupMessage *msg)
0, io->write_length);
soup_buffer_free (io->write_chunk);
io->write_chunk = truncated;
- } else if (io->write_encoding == SOUP_ENCODING_EOF &&
- !io->write_chunk->length)
- goto wrote_body;
- }
-
- if (!write_data (msg, io->write_chunk->data,
- io->write_chunk->length, TRUE))
- return FALSE;
-
- if (io->mode == SOUP_MESSAGE_IO_SERVER)
- soup_message_body_wrote_chunk (io->write_body, io->write_chunk);
- io->write_body_offset += io->write_chunk->length;
- soup_buffer_free (io->write_chunk);
- io->write_chunk = NULL;
-
- SOUP_MESSAGE_IO_PREPARE_FOR_CALLBACK;
- soup_message_wrote_chunk (msg);
- SOUP_MESSAGE_IO_RETURN_VAL_IF_CANCELLED_OR_PAUSED (FALSE);
- break;
-
- case SOUP_MESSAGE_IO_STATE_CHUNK_SIZE:
- if (!io->write_chunk) {
- io->write_chunk = soup_message_body_get_chunk (io->write_body, io->write_body_offset);
- if (!io->write_chunk) {
- soup_message_io_pause (msg);
- return FALSE;
}
- g_string_append_printf (io->write_buf, "%lx\r\n",
- (unsigned long) io->write_chunk->length);
- io->write_body_offset += io->write_chunk->length;
- }
-
- if (!write_data (msg, io->write_buf->str,
- io->write_buf->len, FALSE))
- return FALSE;
-
- g_string_truncate (io->write_buf, 0);
-
- if (io->write_chunk->length == 0) {
- /* The last chunk has no CHUNK_END... */
- io->write_state = SOUP_MESSAGE_IO_STATE_TRAILERS;
- break;
}
- io->write_state = SOUP_MESSAGE_IO_STATE_CHUNK;
- /* fall through */
-
-
- case SOUP_MESSAGE_IO_STATE_CHUNK:
if (!write_data (msg, io->write_chunk->data,
io->write_chunk->length, TRUE))
return FALSE;
+ if (io->write_chunk->length == 0)
+ goto wrote_body;
+
if (io->mode == SOUP_MESSAGE_IO_SERVER)
soup_message_body_wrote_chunk (io->write_body, io->write_chunk);
+ io->write_body_offset += io->write_chunk->length;
soup_buffer_free (io->write_chunk);
io->write_chunk = NULL;
- io->write_state = SOUP_MESSAGE_IO_STATE_CHUNK_END;
-
SOUP_MESSAGE_IO_PREPARE_FOR_CALLBACK;
soup_message_wrote_chunk (msg);
SOUP_MESSAGE_IO_RETURN_VAL_IF_CANCELLED_OR_PAUSED (FALSE);
-
- /* fall through */
-
-
- case SOUP_MESSAGE_IO_STATE_CHUNK_END:
- if (!write_data (msg, SOUP_MESSAGE_IO_EOL,
- SOUP_MESSAGE_IO_EOL_LEN, FALSE))
- return FALSE;
-
- io->write_state = SOUP_MESSAGE_IO_STATE_CHUNK_SIZE;
break;
-
- case SOUP_MESSAGE_IO_STATE_TRAILERS:
- if (!write_data (msg, SOUP_MESSAGE_IO_EOL,
- SOUP_MESSAGE_IO_EOL_LEN, FALSE))
- return FALSE;
-
- io->write_state = SOUP_MESSAGE_IO_STATE_FINISHING;
-
- SOUP_MESSAGE_IO_PREPARE_FOR_CALLBACK;
- soup_message_wrote_body (msg);
- SOUP_MESSAGE_IO_RETURN_VAL_IF_CANCELLED_OR_PAUSED (FALSE);
- /* fall through */
-
-
case SOUP_MESSAGE_IO_STATE_FINISHING:
if (io->write_source) {
g_source_destroy (io->write_source);
@@ -959,7 +883,7 @@ io_read (SoupInputStream *stream, SoupMessage *msg)
io->read_state =
SOUP_MESSAGE_IO_STATE_BLOCKING;
io->write_state =
- io_body_state (io->write_encoding);
+ SOUP_MESSAGE_IO_STATE_BODY;
} else {
/* Just stay in HEADERS */
io->read_state = SOUP_MESSAGE_IO_STATE_HEADERS;
@@ -1100,7 +1024,7 @@ new_iostate (SoupMessage *msg, SoupSocket *sock, SoupMessageIOMode mode,
iostream = soup_socket_get_iostream (sock);
if (iostream) {
io->istream = soup_input_stream_new (g_io_stream_get_input_stream (iostream));
- io->ostream = g_io_stream_get_output_stream (iostream);
+ io->ostream = soup_output_stream_new (g_io_stream_get_output_stream (iostream));
}
g_object_get (io->sock,
SOUP_SOCKET_FLAG_NONBLOCKING, &non_blocking,
diff --git a/libsoup/soup-output-stream.c b/libsoup/soup-output-stream.c
new file mode 100644
index 0000000..fee3227
--- /dev/null
+++ b/libsoup/soup-output-stream.c
@@ -0,0 +1,265 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * soup-output-stream.c
+ *
+ * Copyright (C) 2010 Red Hat, Inc.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <gio/gio.h>
+
+#include "soup-output-stream.h"
+#include "soup-message-headers.h"
+
+typedef enum {
+ SOUP_OUTPUT_STREAM_STATE_CHUNK_SIZE,
+ SOUP_OUTPUT_STREAM_STATE_CHUNK_END,
+ SOUP_OUTPUT_STREAM_STATE_CHUNK,
+ SOUP_OUTPUT_STREAM_STATE_TRAILERS,
+ SOUP_OUTPUT_STREAM_STATE_DONE
+} SoupOutputStreamState;
+
+struct _SoupOutputStreamPrivate {
+ GOutputStream *base_stream;
+ char buf[20];
+
+ SoupEncoding encoding;
+ goffset write_length;
+ SoupOutputStreamState chunked_state;
+};
+
+static void soup_output_stream_pollable_init (GPollableOutputStreamInterface *pollable_interface, gpointer interface_data);
+
+G_DEFINE_TYPE_WITH_CODE (SoupOutputStream, soup_output_stream, G_TYPE_FILTER_OUTPUT_STREAM,
+ G_IMPLEMENT_INTERFACE (G_TYPE_POLLABLE_OUTPUT_STREAM,
+ soup_output_stream_pollable_init))
+
+
+static void
+soup_output_stream_init (SoupOutputStream *stream)
+{
+ stream->priv = G_TYPE_INSTANCE_GET_PRIVATE (stream,
+ SOUP_TYPE_OUTPUT_STREAM,
+ SoupOutputStreamPrivate);
+}
+
+static void
+constructed (GObject *object)
+{
+ SoupOutputStream *sstream = SOUP_OUTPUT_STREAM (object);
+
+ sstream->priv->base_stream = g_filter_output_stream_get_base_stream (G_FILTER_OUTPUT_STREAM (sstream));
+}
+
+static gssize
+soup_output_stream_write_raw (SoupOutputStream *sstream,
+ const void *buffer,
+ gsize count,
+ gboolean blocking,
+ GCancellable *cancellable,
+ GError **error)
+{
+ if (blocking) {
+ return g_output_stream_write (sstream->priv->base_stream,
+ buffer, count,
+ cancellable, error);
+ } else {
+ return g_pollable_output_stream_write_nonblocking (
+ G_POLLABLE_OUTPUT_STREAM (sstream->priv->base_stream),
+ buffer, count, cancellable, error);
+ }
+}
+
+static gssize
+soup_output_stream_write_chunked (SoupOutputStream *sstream,
+ const void *buffer,
+ gsize count,
+ gboolean blocking,
+ GCancellable *cancellable,
+ GError **error)
+{
+ char *buf = sstream->priv->buf;
+ gssize nwrote, len;
+
+again:
+ len = strlen (buf);
+ if (len) {
+ nwrote = soup_output_stream_write_raw (sstream, buf, len,
+ blocking, cancellable,
+ error);
+ if (nwrote < 0)
+ return nwrote;
+ memmove (buf, buf + nwrote, len + 1 - nwrote);
+ goto again;
+ }
+
+ switch (sstream->priv->chunked_state) {
+ case SOUP_OUTPUT_STREAM_STATE_CHUNK_SIZE:
+ snprintf (buf, sizeof (sstream->priv->buf),
+ "%lx\r\n", (gulong)count);
+ len = strlen (buf);
+
+ if (count > 0)
+ sstream->priv->chunked_state = SOUP_OUTPUT_STREAM_STATE_CHUNK;
+ else
+ sstream->priv->chunked_state = SOUP_OUTPUT_STREAM_STATE_TRAILERS;
+ break;
+
+ case SOUP_OUTPUT_STREAM_STATE_CHUNK:
+ nwrote = soup_output_stream_write_raw (sstream, buffer, count,
+ blocking, cancellable,
+ error);
+ if (nwrote < (gssize)count)
+ return nwrote;
+
+ sstream->priv->chunked_state = SOUP_OUTPUT_STREAM_STATE_CHUNK_END;
+ break;
+
+ case SOUP_OUTPUT_STREAM_STATE_CHUNK_END:
+ strncpy (buf, "\r\n", sizeof (sstream->priv->buf));
+ len = 2;
+ sstream->priv->chunked_state = SOUP_OUTPUT_STREAM_STATE_DONE;
+ break;
+
+ case SOUP_OUTPUT_STREAM_STATE_TRAILERS:
+ strncpy (buf, "\r\n", sizeof (sstream->priv->buf));
+ len = 2;
+ sstream->priv->chunked_state = SOUP_OUTPUT_STREAM_STATE_DONE;
+ break;
+
+ case SOUP_OUTPUT_STREAM_STATE_DONE:
+ sstream->priv->chunked_state = SOUP_OUTPUT_STREAM_STATE_CHUNK_SIZE;
+ return count;
+ }
+
+ goto again;
+}
+
+static gssize
+soup_output_stream_write_fn (GOutputStream *stream,
+ const void *buffer,
+ gsize count,
+ GCancellable *cancellable,
+ GError **error)
+{
+ SoupOutputStream *sstream = SOUP_OUTPUT_STREAM (stream);
+
+ switch (sstream->priv->encoding) {
+ case SOUP_ENCODING_CHUNKED:
+ return soup_output_stream_write_chunked (sstream, buffer, count,
+ TRUE, cancellable,
+ error);
+
+ default:
+ return soup_output_stream_write_raw (sstream, buffer, count,
+ TRUE, cancellable, error);
+ }
+}
+
+static gboolean
+soup_output_stream_is_writable (GPollableOutputStream *stream)
+{
+ SoupOutputStream *sstream = SOUP_OUTPUT_STREAM (stream);
+
+ return g_pollable_output_stream_is_writable (G_POLLABLE_OUTPUT_STREAM (sstream->priv->base_stream));
+}
+
+static GSource *
+soup_output_stream_create_source (GPollableOutputStream *stream,
+ GCancellable *cancellable)
+{
+ SoupOutputStream *sstream = SOUP_OUTPUT_STREAM (stream);
+ GSource *base_source, *pollable_source;
+
+ base_source = g_pollable_output_stream_create_source (G_POLLABLE_OUTPUT_STREAM (sstream->priv->base_stream), cancellable);
+ g_source_set_dummy_callback (base_source);
+ pollable_source = g_pollable_source_new (G_OBJECT (stream));
+ g_source_add_child_source (pollable_source, base_source);
+ g_source_unref (base_source);
+
+ return pollable_source;
+}
+
+static gssize
+soup_output_stream_write_nonblocking (GPollableOutputStream *stream,
+ const void *buffer,
+ gsize count,
+ GError **error)
+{
+ SoupOutputStream *sstream = SOUP_OUTPUT_STREAM (stream);
+
+ switch (sstream->priv->encoding) {
+ case SOUP_ENCODING_CHUNKED:
+ return soup_output_stream_write_chunked (sstream, buffer, count,
+ FALSE, NULL, error);
+
+ default:
+ return soup_output_stream_write_raw (sstream, buffer, count,
+ FALSE, NULL, error);
+ }
+}
+
+static void
+soup_output_stream_class_init (SoupOutputStreamClass *stream_class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (stream_class);
+ GOutputStreamClass *output_stream_class = G_OUTPUT_STREAM_CLASS (stream_class);
+
+ g_type_class_add_private (stream_class, sizeof (SoupOutputStreamPrivate));
+
+ object_class->constructed = constructed;
+
+ output_stream_class->write_fn = soup_output_stream_write_fn;
+}
+
+static void
+soup_output_stream_pollable_init (GPollableOutputStreamInterface *pollable_interface,
+ gpointer interface_data)
+{
+ pollable_interface->is_writable = soup_output_stream_is_writable;
+ pollable_interface->create_source = soup_output_stream_create_source;
+ pollable_interface->write_nonblocking = soup_output_stream_write_nonblocking;
+}
+
+SoupOutputStream *
+soup_output_stream_new (GOutputStream *base_stream)
+{
+ return g_object_new (SOUP_TYPE_OUTPUT_STREAM,
+ "base-stream", base_stream,
+ "close-base-stream", FALSE,
+ NULL);
+}
+
+void
+soup_output_stream_set_encoding (SoupOutputStream *sstream,
+ SoupEncoding encoding)
+{
+ sstream->priv->encoding = encoding;
+ if (encoding == SOUP_ENCODING_CHUNKED)
+ sstream->priv->chunked_state = SOUP_OUTPUT_STREAM_STATE_CHUNK_SIZE;
+}
+
+gssize
+soup_output_stream_write (SoupOutputStream *sstream,
+ const void *buffer,
+ gsize count,
+ gboolean blocking,
+ GCancellable *cancellable,
+ GError **error)
+{
+ if (blocking) {
+ return g_output_stream_write (G_OUTPUT_STREAM (sstream),
+ buffer, count,
+ cancellable, error);
+ } else {
+ return g_pollable_output_stream_write_nonblocking (
+ G_POLLABLE_OUTPUT_STREAM (sstream),
+ buffer, count, cancellable, error);
+ }
+}
diff --git a/libsoup/soup-output-stream.h b/libsoup/soup-output-stream.h
new file mode 100644
index 0000000..554aef1
--- /dev/null
+++ b/libsoup/soup-output-stream.h
@@ -0,0 +1,55 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright (C) 2010 Red Hat, Inc.
+ */
+
+#ifndef SOUP_OUTPUT_STREAM_H
+#define SOUP_OUTPUT_STREAM_H 1
+
+#include <libsoup/soup-types.h>
+#include <libsoup/soup-message-headers.h>
+
+G_BEGIN_DECLS
+
+#define SOUP_TYPE_OUTPUT_STREAM (soup_output_stream_get_type ())
+#define SOUP_OUTPUT_STREAM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SOUP_TYPE_OUTPUT_STREAM, SoupOutputStream))
+#define SOUP_OUTPUT_STREAM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), SOUP_TYPE_OUTPUT_STREAM, SoupOutputStreamClass))
+#define SOUP_IS_OUTPUT_STREAM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SOUP_TYPE_OUTPUT_STREAM))
+#define SOUP_IS_OUTPUT_STREAM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), SOUP_TYPE_OUTPUT_STREAM))
+#define SOUP_OUTPUT_STREAM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), SOUP_TYPE_OUTPUT_STREAM, SoupOutputStreamClass))
+
+typedef struct _SoupOutputStreamPrivate SoupOutputStreamPrivate;
+
+typedef struct {
+ GFilterOutputStream parent;
+
+ SoupOutputStreamPrivate *priv;
+} SoupOutputStream;
+
+typedef struct {
+ GFilterOutputStreamClass parent_class;
+
+ /* Padding for future expansion */
+ void (*_libsoup_reserved1) (void);
+ void (*_libsoup_reserved2) (void);
+ void (*_libsoup_reserved3) (void);
+ void (*_libsoup_reserved4) (void);
+} SoupOutputStreamClass;
+
+GType soup_output_stream_get_type (void);
+
+SoupOutputStream *soup_output_stream_new (GOutputStream *base_stream);
+
+void soup_output_stream_set_encoding (SoupOutputStream *sstream,
+ SoupEncoding encoding);
+
+gssize soup_output_stream_write (SoupOutputStream *sstream,
+ const void *buffer,
+ gsize count,
+ gboolean blocking,
+ GCancellable *cancellable,
+ GError **error);
+
+G_END_DECLS
+
+#endif /* SOUP_OUTPUT_STREAM_H */
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]