[libsoup/wip/http2-a: 3/7] Add SoupHTTPOutputStream, implement SoupHTTP1OutputStream



commit 237a8e8b39382c307bc679bcd158aaf6aff88f9d
Author: Dan Winship <danw gnome org>
Date:   Sun Dec 8 14:48:52 2013 +0100

    Add SoupHTTPOutputStream, implement SoupHTTP1OutputStream
    
    Abstract out some HTTP/1.x-specific generating/writing bits from
    soup-message-io, etc, into a new HTTP/1.x-specific class.

 libsoup/Makefile.am                 |    6 +
 libsoup/soup-body-output-stream.c   |   20 +++-
 libsoup/soup-body-output-stream.h   |    8 +-
 libsoup/soup-filter-output-stream.c |   84 +++++++++++++
 libsoup/soup-filter-output-stream.h |   39 ++++++
 libsoup/soup-http-output-stream.c   |   71 +++++++++++
 libsoup/soup-http-output-stream.h   |   76 ++++++++++++
 libsoup/soup-http1-output-stream.c  |  223 +++++++++++++++++++++++++++++++++++
 libsoup/soup-http1-output-stream.h  |   37 ++++++
 libsoup/soup-message-client-io.c    |   71 ++---------
 libsoup/soup-message-io.c           |   59 +++------
 libsoup/soup-message-private.h      |    4 +-
 libsoup/soup-message-server-io.c    |   35 ++----
 13 files changed, 601 insertions(+), 132 deletions(-)
---
diff --git a/libsoup/Makefile.am b/libsoup/Makefile.am
index 032dbde..ddf5242 100644
--- a/libsoup/Makefile.am
+++ b/libsoup/Makefile.am
@@ -137,12 +137,18 @@ libsoup_2_4_la_SOURCES =          \
        soup-enum-types.c               \
        soup-filter-input-stream.c      \
        soup-filter-input-stream.h      \
+       soup-filter-output-stream.c     \
+       soup-filter-output-stream.h     \
        soup-form.c                     \
        soup-headers.c                  \
        soup-http-input-stream.c        \
        soup-http-input-stream.h        \
        soup-http1-input-stream.c       \
        soup-http1-input-stream.h       \
+       soup-http-output-stream.c       \
+       soup-http-output-stream.h       \
+       soup-http1-output-stream.c      \
+       soup-http1-output-stream.h      \
        soup-logger.c                   \
        soup-message.c                  \
        soup-message-body.c             \
diff --git a/libsoup/soup-body-output-stream.c b/libsoup/soup-body-output-stream.c
index 03353ef..a2c7e61 100644
--- a/libsoup/soup-body-output-stream.c
+++ b/libsoup/soup-body-output-stream.c
@@ -61,6 +61,11 @@ soup_body_output_stream_constructed (GObject *object)
        SoupBodyOutputStream *bostream = SOUP_BODY_OUTPUT_STREAM (object);
 
        bostream->priv->base_stream = g_filter_output_stream_get_base_stream (G_FILTER_OUTPUT_STREAM 
(bostream));
+
+       if (bostream->priv->encoding == SOUP_ENCODING_NONE ||
+           (bostream->priv->encoding == SOUP_ENCODING_CONTENT_LENGTH &&
+            bostream->priv->write_length == 0))
+               bostream->priv->eof = TRUE;
 }
 
 static void
@@ -116,10 +121,8 @@ soup_body_output_stream_write_raw (SoupBodyOutputStream  *bostream,
         */
        if (bostream->priv->write_length) {
                my_count = MIN (count, bostream->priv->write_length - bostream->priv->written);
-               if (my_count == 0) {
-                       bostream->priv->eof = TRUE;
+               if (my_count == 0)
                        return count;
-               }
        } else
                my_count = count;
 
@@ -127,8 +130,11 @@ soup_body_output_stream_write_raw (SoupBodyOutputStream  *bostream,
                                          buffer, my_count,
                                          blocking, cancellable, error);
 
-       if (nwrote > 0 && bostream->priv->write_length)
+       if (nwrote > 0 && bostream->priv->write_length) {
                bostream->priv->written += nwrote;
+               if (bostream->priv->written == bostream->priv->write_length)
+                       bostream->priv->eof = TRUE;
+       }
 
        if (nwrote == my_count && my_count != count)
                nwrote = count;
@@ -344,3 +350,9 @@ soup_body_output_stream_new (GOutputStream *base_stream,
                             "content-length", content_length,
                             NULL);
 }
+
+gboolean
+soup_body_output_stream_get_eof (SoupBodyOutputStream *bostream)
+{
+       return bostream->priv->eof;
+}
diff --git a/libsoup/soup-body-output-stream.h b/libsoup/soup-body-output-stream.h
index 8bd8970..2617f99 100644
--- a/libsoup/soup-body-output-stream.h
+++ b/libsoup/soup-body-output-stream.h
@@ -38,9 +38,11 @@ typedef struct {
 
 GType soup_body_output_stream_get_type (void);
 
-GOutputStream *soup_body_output_stream_new (GOutputStream *base_stream,
-                                           SoupEncoding   encoding,
-                                           goffset        content_length);
+GOutputStream *soup_body_output_stream_new     (GOutputStream        *base_stream,
+                                               SoupEncoding          encoding,
+                                               goffset               content_length);
+
+gboolean       soup_body_output_stream_get_eof (SoupBodyOutputStream *bostream);
 
 G_END_DECLS
 
diff --git a/libsoup/soup-filter-output-stream.c b/libsoup/soup-filter-output-stream.c
new file mode 100644
index 0000000..538022a
--- /dev/null
+++ b/libsoup/soup-filter-output-stream.c
@@ -0,0 +1,84 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * soup-filter-output-stream.c
+ *
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+
+#include "soup-filter-output-stream.h"
+#include "soup.h"
+
+/* This is just GFilterOutputStream + GPollableOutputStream */
+
+static void soup_filter_output_stream_pollable_init (GPollableOutputStreamInterface *pollable_interface, 
gpointer interface_data);
+
+G_DEFINE_TYPE_WITH_CODE (SoupFilterOutputStream, soup_filter_output_stream, G_TYPE_FILTER_OUTPUT_STREAM,
+                        G_IMPLEMENT_INTERFACE (G_TYPE_POLLABLE_OUTPUT_STREAM,
+                                               soup_filter_output_stream_pollable_init))
+
+static void
+soup_filter_output_stream_init (SoupFilterOutputStream *stream)
+{
+}
+
+static gboolean
+soup_filter_output_stream_is_writable (GPollableOutputStream *stream)
+{
+       return g_pollable_output_stream_is_writable (G_POLLABLE_OUTPUT_STREAM (G_FILTER_OUTPUT_STREAM 
(stream)->base_stream));
+}
+
+static gssize
+soup_filter_output_stream_write_nonblocking (GPollableOutputStream  *stream,
+                                            const void             *buffer,
+                                            gsize                   count,
+                                            GError                **error)
+{
+       return g_pollable_stream_write (G_FILTER_OUTPUT_STREAM (stream)->base_stream,
+                                       buffer, count,
+                                       FALSE, NULL, error);
+}
+
+static GSource *
+soup_filter_output_stream_create_source (GPollableOutputStream *stream,
+                                        GCancellable          *cancellable)
+{
+       GSource *base_source, *pollable_source;
+
+       base_source = g_pollable_output_stream_create_source (G_POLLABLE_OUTPUT_STREAM 
(G_FILTER_OUTPUT_STREAM (stream)->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 void
+soup_filter_output_stream_class_init (SoupFilterOutputStreamClass *stream_class)
+{
+}
+
+static void
+soup_filter_output_stream_pollable_init (GPollableOutputStreamInterface *pollable_interface,
+                                       gpointer                       interface_data)
+{
+       pollable_interface->is_writable = soup_filter_output_stream_is_writable;
+       pollable_interface->write_nonblocking = soup_filter_output_stream_write_nonblocking;
+       pollable_interface->create_source = soup_filter_output_stream_create_source;
+}
+
+GOutputStream *
+soup_filter_output_stream_new (GOutputStream *base_stream)
+{
+       return g_object_new (SOUP_TYPE_FILTER_OUTPUT_STREAM,
+                            "base-stream", base_stream,
+                            "close-base-stream", FALSE,
+                            NULL);
+}
diff --git a/libsoup/soup-filter-output-stream.h b/libsoup/soup-filter-output-stream.h
new file mode 100644
index 0000000..fba9702
--- /dev/null
+++ b/libsoup/soup-filter-output-stream.h
@@ -0,0 +1,39 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+#ifndef SOUP_FILTER_OUTPUT_STREAM_H
+#define SOUP_FILTER_OUTPUT_STREAM_H 1
+
+#include <libsoup/soup-types.h>
+
+G_BEGIN_DECLS
+
+#define SOUP_TYPE_FILTER_OUTPUT_STREAM            (soup_filter_output_stream_get_type ())
+#define SOUP_FILTER_OUTPUT_STREAM(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), 
SOUP_TYPE_FILTER_OUTPUT_STREAM, SoupFilterOutputStream))
+#define SOUP_FILTER_OUTPUT_STREAM_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), 
SOUP_TYPE_FILTER_OUTPUT_STREAM, SoupFilterOutputStreamClass))
+#define SOUP_IS_FILTER_OUTPUT_STREAM(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), 
SOUP_TYPE_FILTER_OUTPUT_STREAM))
+#define SOUP_IS_FILTER_OUTPUT_STREAM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), 
SOUP_TYPE_FILTER_OUTPUT_STREAM))
+#define SOUP_FILTER_OUTPUT_STREAM_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), 
SOUP_TYPE_FILTER_OUTPUT_STREAM, SoupFilterOutputStreamClass))
+
+typedef struct _SoupFilterOutputStreamPrivate SoupFilterOutputStreamPrivate;
+
+typedef struct {
+       GFilterOutputStream parent;
+
+       SoupFilterOutputStreamPrivate *priv;
+} SoupFilterOutputStream;
+
+typedef struct {
+       GFilterOutputStreamClass parent_class;
+
+} SoupFilterOutputStreamClass;
+
+GType soup_filter_output_stream_get_type (void);
+
+GOutputStream *soup_filter_output_stream_new (GOutputStream *base_stream);
+
+G_END_DECLS
+
+#endif /* SOUP_FILTER_OUTPUT_STREAM_H */
diff --git a/libsoup/soup-http-output-stream.c b/libsoup/soup-http-output-stream.c
new file mode 100644
index 0000000..e345083
--- /dev/null
+++ b/libsoup/soup-http-output-stream.c
@@ -0,0 +1,71 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * soup-http-output-stream.c
+ *
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "soup-http-output-stream.h"
+#include "soup.h"
+#include "soup-filter-output-stream.h"
+
+G_DEFINE_ABSTRACT_TYPE (SoupHTTPOutputStream, soup_http_output_stream, SOUP_TYPE_FILTER_OUTPUT_STREAM)
+
+static void
+soup_http_output_stream_init (SoupHTTPOutputStream *http)
+{
+}
+
+static void
+soup_http_output_stream_class_init (SoupHTTPOutputStreamClass *http_class)
+{
+}
+
+void
+soup_http_output_stream_build_request_headers (SoupHTTPOutputStream  *http,
+                                              gboolean               via_proxy,
+                                              const char            *method,
+                                              SoupURI               *request_uri,
+                                              SoupHTTPVersion        version,
+                                              SoupMessageHeaders    *headers)
+{
+       return SOUP_HTTP_OUTPUT_STREAM_GET_CLASS (http)->
+               build_request_headers (http, via_proxy,
+                                      method, request_uri, version,
+                                      headers);
+}
+
+void
+soup_http_output_stream_build_response_headers (SoupHTTPOutputStream  *http,
+                                               const char            *request_method,
+                                               SoupHTTPVersion        version,
+                                               guint                  status_code,
+                                               const char            *reason_phrase,
+                                               SoupMessageHeaders    *headers)
+{
+       return SOUP_HTTP_OUTPUT_STREAM_GET_CLASS (http)->
+               build_response_headers (http, request_method,
+                                       version, status_code, reason_phrase,
+                                       headers);
+}
+
+gboolean
+soup_http_output_stream_write_headers (SoupHTTPOutputStream  *http,
+                                      gboolean               blocking,
+                                      GCancellable          *cancellable,
+                                      GError               **error)
+{
+       return SOUP_HTTP_OUTPUT_STREAM_GET_CLASS (http)->
+               write_headers (http, blocking, cancellable, error);
+}
+
+GOutputStream *
+soup_http_output_stream_get_body_stream (SoupHTTPOutputStream *http)
+{
+       return SOUP_HTTP_OUTPUT_STREAM_GET_CLASS (http)->
+               get_body_stream (http);
+}
diff --git a/libsoup/soup-http-output-stream.h b/libsoup/soup-http-output-stream.h
new file mode 100644
index 0000000..0179242
--- /dev/null
+++ b/libsoup/soup-http-output-stream.h
@@ -0,0 +1,76 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+#ifndef SOUP_HTTP_OUTPUT_STREAM_H
+#define SOUP_HTTP_OUTPUT_STREAM_H 1
+
+#include "soup-types.h"
+#include "soup-filter-output-stream.h"
+#include "soup-message.h"
+
+G_BEGIN_DECLS
+
+#define SOUP_TYPE_HTTP_OUTPUT_STREAM            (soup_http_output_stream_get_type ())
+#define SOUP_HTTP_OUTPUT_STREAM(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), 
SOUP_TYPE_HTTP_OUTPUT_STREAM, SoupHTTPOutputStream))
+#define SOUP_HTTP_OUTPUT_STREAM_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), 
SOUP_TYPE_HTTP_OUTPUT_STREAM, SoupHTTPOutputStreamClass))
+#define SOUP_IS_HTTP_OUTPUT_STREAM(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), 
SOUP_TYPE_HTTP_OUTPUT_STREAM))
+#define SOUP_IS_HTTP_OUTPUT_STREAM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), 
SOUP_TYPE_HTTP_OUTPUT_STREAM))
+#define SOUP_HTTP_OUTPUT_STREAM_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), 
SOUP_TYPE_HTTP_OUTPUT_STREAM, SoupHTTPOutputStreamClass))
+
+typedef struct {
+       SoupFilterOutputStream parent;
+
+} SoupHTTPOutputStream;
+
+typedef struct {
+       SoupFilterOutputStreamClass parent_class;
+
+       void           (*build_request_headers)  (SoupHTTPOutputStream  *http,
+                                                 gboolean               via_proxy,
+                                                 const char            *method,
+                                                 SoupURI               *request_uri,
+                                                 SoupHTTPVersion        version,
+                                                 SoupMessageHeaders    *headers);
+       void           (*build_response_headers) (SoupHTTPOutputStream  *http,
+                                                 const char            *request_method,
+                                                 SoupHTTPVersion        version,
+                                                 guint                  status_code,
+                                                 const char            *reason_phrase,
+                                                 SoupMessageHeaders    *headers);
+
+       gboolean       (*write_headers)          (SoupHTTPOutputStream  *http,
+                                                 gboolean               blocking,
+                                                 GCancellable          *cancellable,
+                                                 GError               **error);
+
+       GOutputStream *(*get_body_stream)        (SoupHTTPOutputStream  *http);
+
+} SoupHTTPOutputStreamClass;
+
+GType soup_http_output_stream_get_type (void);
+
+void           soup_http_output_stream_build_request_headers  (SoupHTTPOutputStream  *http,
+                                                              gboolean               via_proxy,
+                                                              const char            *method,
+                                                              SoupURI               *request_uri,
+                                                              SoupHTTPVersion        version,
+                                                              SoupMessageHeaders    *headers);
+void           soup_http_output_stream_build_response_headers (SoupHTTPOutputStream  *http,
+                                                              const char            *request_method,
+                                                              SoupHTTPVersion        version,
+                                                              guint                  status_code,
+                                                              const char            *reason_phrase,
+                                                              SoupMessageHeaders    *headers);
+
+gboolean       soup_http_output_stream_write_headers          (SoupHTTPOutputStream  *http,
+                                                              gboolean               blocking,
+                                                              GCancellable          *cancellable,
+                                                              GError               **error);
+
+GOutputStream *soup_http_output_stream_get_body_stream        (SoupHTTPOutputStream  *http);
+
+G_END_DECLS
+
+#endif /* SOUP_HTTP_OUTPUT_STREAM_H */
diff --git a/libsoup/soup-http1-output-stream.c b/libsoup/soup-http1-output-stream.c
new file mode 100644
index 0000000..5b01e1c
--- /dev/null
+++ b/libsoup/soup-http1-output-stream.c
@@ -0,0 +1,223 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * soup-http1-output-stream.c
+ *
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+
+#include <glib/gi18n-lib.h>
+
+#include "soup-http1-output-stream.h"
+#include "soup.h"
+#include "soup-body-output-stream.h"
+
+G_DEFINE_TYPE (SoupHTTP1OutputStream, soup_http1_output_stream, SOUP_TYPE_HTTP_OUTPUT_STREAM)
+
+typedef struct {
+       GString *header_buf;
+       gsize nwritten;
+
+       SoupEncoding encoding;
+       goffset content_length;
+} SoupHTTP1OutputStreamPrivate;
+#define SOUP_HTTP1_OUTPUT_STREAM_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), 
SOUP_TYPE_HTTP1_OUTPUT_STREAM, SoupHTTP1OutputStreamPrivate))
+
+static void
+soup_http1_output_stream_init (SoupHTTP1OutputStream *http1)
+{
+       SoupHTTP1OutputStreamPrivate *priv = SOUP_HTTP1_OUTPUT_STREAM_GET_PRIVATE (http1);
+
+       priv->header_buf = g_string_new (NULL);
+       priv->encoding = SOUP_ENCODING_NONE;
+       priv->content_length = -1;
+}
+
+static void
+soup_http1_output_stream_finalize (GObject *object)
+{
+       SoupHTTP1OutputStreamPrivate *priv = SOUP_HTTP1_OUTPUT_STREAM_GET_PRIVATE (object);
+
+       g_string_free (priv->header_buf, TRUE);
+
+       G_OBJECT_CLASS (soup_http1_output_stream_parent_class)->finalize (object);
+}
+
+static void
+start_build_headers (SoupHTTPOutputStream *http)
+{
+       SoupHTTP1OutputStreamPrivate *priv = SOUP_HTTP1_OUTPUT_STREAM_GET_PRIVATE (http);
+
+       g_string_truncate (priv->header_buf, 0);
+       priv->nwritten = 0;
+}
+
+static void
+finish_build_headers (SoupHTTPOutputStream *http,
+                     SoupMessageHeaders   *headers)
+{
+       SoupHTTP1OutputStreamPrivate *priv = SOUP_HTTP1_OUTPUT_STREAM_GET_PRIVATE (http);
+       SoupMessageHeadersIter iter;
+       const char *name, *value;
+
+       if (priv->encoding == SOUP_ENCODING_CONTENT_LENGTH)
+               priv->content_length = soup_message_headers_get_content_length (headers);
+
+       soup_message_headers_iter_init (&iter, headers);
+       while (soup_message_headers_iter_next (&iter, &name, &value))
+               g_string_append_printf (priv->header_buf, "%s: %s\r\n", name, value);
+       g_string_append (priv->header_buf, "\r\n");
+}
+
+static void
+soup_http1_output_stream_build_request_headers (SoupHTTPOutputStream  *http,
+                                               gboolean               via_proxy,
+                                               const char            *method,
+                                               SoupURI               *request_uri,
+                                               SoupHTTPVersion        version,
+                                               SoupMessageHeaders    *headers)
+{
+       SoupHTTP1OutputStreamPrivate *priv = SOUP_HTTP1_OUTPUT_STREAM_GET_PRIVATE (http);
+       char *uri_host;
+       char *uri_string;
+
+       start_build_headers (http);
+
+       if (strchr (request_uri->host, ':'))
+               uri_host = g_strdup_printf ("[%.*s]", (int) strcspn (request_uri->host, "%"), 
request_uri->host);
+       else if (g_hostname_is_non_ascii (request_uri->host))
+               uri_host = g_hostname_to_ascii (request_uri->host);
+       else
+               uri_host = request_uri->host;
+
+       if (method == SOUP_METHOD_CONNECT) {
+               /* CONNECT URI is hostname:port for tunnel destination */
+               uri_string = g_strdup_printf ("%s:%d", uri_host, request_uri->port);
+       } else {
+               /* Proxy expects full URI to destination. Otherwise
+                * just the path.
+                */
+               uri_string = soup_uri_to_string (request_uri, !via_proxy);
+
+               if (via_proxy && request_uri->fragment) {
+                       /* Strip fragment */
+                       char *fragment = strchr (uri_string, '#');
+                       if (fragment)
+                               *fragment = '\0';
+               }
+       }
+
+       g_string_append_printf (priv->header_buf, "%s %s HTTP/1.%d\r\n",
+                               method, uri_string,
+                               (version == SOUP_HTTP_1_0) ? 0 : 1);
+
+       if (!soup_message_headers_get_one (headers, "Host")) {
+               if (soup_uri_uses_default_port (request_uri)) {
+                       g_string_append_printf (priv->header_buf, "Host: %s\r\n",
+                                               uri_host);
+               } else {
+                       g_string_append_printf (priv->header_buf, "Host: %s:%d\r\n",
+                                               uri_host, request_uri->port);
+               }
+       }
+       g_free (uri_string);
+       if (uri_host != request_uri->host)
+               g_free (uri_host);
+
+       priv->encoding = soup_message_headers_get_encoding (headers);
+       finish_build_headers (http, headers);
+}
+
+static void
+soup_http1_output_stream_build_response_headers (SoupHTTPOutputStream  *http,
+                                                const char            *request_method,
+                                                SoupHTTPVersion        version,
+                                                guint                  status_code,
+                                                const char            *reason_phrase,
+                                                SoupMessageHeaders    *headers)
+{
+       SoupHTTP1OutputStreamPrivate *priv = SOUP_HTTP1_OUTPUT_STREAM_GET_PRIVATE (http);
+       SoupEncoding claimed_encoding;
+
+       start_build_headers (http);
+
+       g_string_append_printf (priv->header_buf, "HTTP/1.%c %d %s\r\n",
+                               (version == SOUP_HTTP_1_0) ? '0' : '1',
+                               status_code, reason_phrase);
+
+       claimed_encoding = soup_message_headers_get_encoding (headers);
+       if ((request_method == SOUP_METHOD_HEAD ||
+            status_code  == SOUP_STATUS_NO_CONTENT ||
+            status_code  == SOUP_STATUS_NOT_MODIFIED ||
+            SOUP_STATUS_IS_INFORMATIONAL (status_code)) ||
+           (request_method == SOUP_METHOD_CONNECT &&
+            SOUP_STATUS_IS_SUCCESSFUL (status_code)))
+               priv->encoding = SOUP_ENCODING_NONE;
+       else
+               priv->encoding = claimed_encoding;
+
+       finish_build_headers (http, headers);
+}
+
+static gboolean
+soup_http1_output_stream_write_headers (SoupHTTPOutputStream  *http,
+                                       gboolean               blocking,
+                                       GCancellable          *cancellable,
+                                       GError               **error)
+{
+       SoupHTTP1OutputStreamPrivate *priv = SOUP_HTTP1_OUTPUT_STREAM_GET_PRIVATE (http);
+       GOutputStream *ostream = G_FILTER_OUTPUT_STREAM (http)->base_stream;
+       gssize nwrote;
+
+       while (priv->nwritten < priv->header_buf->len) {
+               nwrote = g_pollable_stream_write (ostream,
+                                                 priv->header_buf->str + priv->nwritten,
+                                                 priv->header_buf->len - priv->nwritten,
+                                                 blocking, cancellable, error);
+               if (nwrote == -1)
+                       return FALSE;
+               priv->nwritten += nwrote;
+       }
+
+       return TRUE;
+}
+
+static GOutputStream *
+soup_http1_output_stream_get_body_stream (SoupHTTPOutputStream  *http)
+{
+       SoupHTTP1OutputStreamPrivate *priv = SOUP_HTTP1_OUTPUT_STREAM_GET_PRIVATE (http);
+
+       return soup_body_output_stream_new (G_FILTER_OUTPUT_STREAM (http)->base_stream,
+                                           priv->encoding,
+                                           priv->content_length);
+}
+
+static void
+soup_http1_output_stream_class_init (SoupHTTP1OutputStreamClass *http1_class)
+{
+       GObjectClass *object_class = G_OBJECT_CLASS (http1_class);
+       SoupHTTPOutputStreamClass *http_class = SOUP_HTTP_OUTPUT_STREAM_CLASS (http1_class);
+
+       g_type_class_add_private (http1_class, sizeof (SoupHTTP1OutputStreamPrivate));
+
+       object_class->finalize = soup_http1_output_stream_finalize;
+
+       http_class->build_request_headers = soup_http1_output_stream_build_request_headers;
+       http_class->build_response_headers = soup_http1_output_stream_build_response_headers;
+       http_class->write_headers = soup_http1_output_stream_write_headers;
+       http_class->get_body_stream = soup_http1_output_stream_get_body_stream;
+}
+
+GOutputStream *
+soup_http1_output_stream_new (GOutputStream *base_stream)
+{
+       return g_object_new (SOUP_TYPE_HTTP1_OUTPUT_STREAM,
+                            "base-stream", base_stream,
+                            "close-base-stream", FALSE,
+                            NULL);
+}
diff --git a/libsoup/soup-http1-output-stream.h b/libsoup/soup-http1-output-stream.h
new file mode 100644
index 0000000..ab6fee3
--- /dev/null
+++ b/libsoup/soup-http1-output-stream.h
@@ -0,0 +1,37 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+#ifndef SOUP_HTTP1_OUTPUT_STREAM_H
+#define SOUP_HTTP1_OUTPUT_STREAM_H 1
+
+#include "soup-types.h"
+#include "soup-http-output-stream.h"
+
+G_BEGIN_DECLS
+
+#define SOUP_TYPE_HTTP1_OUTPUT_STREAM            (soup_http1_output_stream_get_type ())
+#define SOUP_HTTP1_OUTPUT_STREAM(obj)            (G_TYPE_CHECK_INSTANCE_CAST ((obj), 
SOUP_TYPE_HTTP1_OUTPUT_STREAM, SoupHTTP1OutputStream))
+#define SOUP_HTTP1_OUTPUT_STREAM_CLASS(klass)    (G_TYPE_CHECK_CLASS_CAST ((klass), 
SOUP_TYPE_HTTP1_OUTPUT_STREAM, SoupHTTP1OutputStreamClass))
+#define SOUP_IS_HTTP1_OUTPUT_STREAM(obj)         (G_TYPE_CHECK_INSTANCE_TYPE ((obj), 
SOUP_TYPE_HTTP1_OUTPUT_STREAM))
+#define SOUP_IS_HTTP1_OUTPUT_STREAM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), 
SOUP_TYPE_HTTP1_OUTPUT_STREAM))
+#define SOUP_HTTP1_OUTPUT_STREAM_GET_CLASS(obj)  (G_TYPE_INSTANCE_GET_CLASS ((obj), 
SOUP_TYPE_HTTP1_OUTPUT_STREAM, SoupHTTP1OutputStreamClass))
+
+typedef struct {
+       SoupHTTPOutputStream parent;
+
+} SoupHTTP1OutputStream;
+
+typedef struct {
+       SoupHTTPOutputStreamClass parent_class;
+
+} SoupHTTP1OutputStreamClass;
+
+GType soup_http1_output_stream_get_type (void);
+
+GOutputStream *soup_http1_output_stream_new (GOutputStream *base_stream);
+
+G_END_DECLS
+
+#endif /* SOUP_HTTP1_OUTPUT_STREAM_H */
diff --git a/libsoup/soup-message-client-io.c b/libsoup/soup-message-client-io.c
index 13cb104..fd30afd 100644
--- a/libsoup/soup-message-client-io.c
+++ b/libsoup/soup-message-client-io.c
@@ -51,75 +51,30 @@ parse_response_headers (SoupMessage *msg,
 }
 
 static void
-get_request_headers (SoupMessage *msg, GString *header,
-                    SoupEncoding *encoding, gpointer user_data)
+get_request_headers (SoupMessage          *msg,
+                    SoupHTTPOutputStream *http,
+                    gpointer              user_data)
 {
        SoupMessagePrivate *priv = SOUP_MESSAGE_GET_PRIVATE (msg);
        SoupMessageQueueItem *item = user_data;
-       SoupURI *uri = soup_message_get_uri (msg);
-       char *uri_host;
-       char *uri_string;
-       SoupMessageHeadersIter iter;
-       const char *name, *value;
+       SoupEncoding encoding;
 
-       if (strchr (uri->host, ':'))
-               uri_host = g_strdup_printf ("[%.*s]", (int) strcspn (uri->host, "%"), uri->host);
-       else if (g_hostname_is_non_ascii (uri->host))
-               uri_host = g_hostname_to_ascii (uri->host);
-       else
-               uri_host = uri->host;
-
-       if (msg->method == SOUP_METHOD_CONNECT) {
-               /* CONNECT URI is hostname:port for tunnel destination */
-               uri_string = g_strdup_printf ("%s:%d", uri_host, uri->port);
-       } else {
-               gboolean proxy = soup_connection_is_via_proxy (item->conn);
-
-               /* Proxy expects full URI to destination. Otherwise
-                * just the path.
-                */
-               uri_string = soup_uri_to_string (uri, !proxy);
-
-               if (proxy && uri->fragment) {
-                       /* Strip fragment */
-                       char *fragment = strchr (uri_string, '#');
-                       if (fragment)
-                               *fragment = '\0';
-               }
-       }
-
-       g_string_append_printf (header, "%s %s HTTP/1.%d\r\n",
-                               msg->method, uri_string,
-                               (priv->http_version == SOUP_HTTP_1_0) ? 0 : 1);
-
-       if (!soup_message_headers_get_one (msg->request_headers, "Host")) {
-               if (soup_uri_uses_default_port (uri)) {
-                       g_string_append_printf (header, "Host: %s\r\n",
-                                               uri_host);
-               } else {
-                       g_string_append_printf (header, "Host: %s:%d\r\n",
-                                               uri_host, uri->port);
-               }
-       }
-       g_free (uri_string);
-       if (uri_host != uri->host)
-               g_free (uri_host);
-
-       *encoding = soup_message_headers_get_encoding (msg->request_headers);
-       if ((*encoding == SOUP_ENCODING_CONTENT_LENGTH ||
-            *encoding == SOUP_ENCODING_NONE) &&
+       encoding = soup_message_headers_get_encoding (msg->request_headers);
+       if ((encoding == SOUP_ENCODING_CONTENT_LENGTH ||
+            encoding == SOUP_ENCODING_NONE) &&
            (msg->request_body->length > 0 ||
             soup_message_headers_get_one (msg->request_headers, "Content-Type")) &&
            !soup_message_headers_get_content_length (msg->request_headers)) {
-               *encoding = SOUP_ENCODING_CONTENT_LENGTH;
                soup_message_headers_set_content_length (msg->request_headers,
                                                         msg->request_body->length);
        }
 
-       soup_message_headers_iter_init (&iter, msg->request_headers);
-       while (soup_message_headers_iter_next (&iter, &name, &value))
-               g_string_append_printf (header, "%s: %s\r\n", name, value);
-       g_string_append (header, "\r\n");
+       soup_http_output_stream_build_request_headers (http,
+                                                      soup_connection_is_via_proxy (item->conn),
+                                                      msg->method,
+                                                      soup_message_get_uri (msg),
+                                                      priv->http_version,
+                                                      msg->request_headers);
 }
 
 void
diff --git a/libsoup/soup-message-io.c b/libsoup/soup-message-io.c
index bc00eff..8a895e8 100644
--- a/libsoup/soup-message-io.c
+++ b/libsoup/soup-message-io.c
@@ -20,6 +20,7 @@
 #include "soup-content-sniffer-stream.h"
 #include "soup-filter-input-stream.h"
 #include "soup-http1-input-stream.h"
+#include "soup-http1-output-stream.h"
 #include "soup-message-private.h"
 #include "soup-message-queue.h"
 #include "soup-misc-private.h"
@@ -55,24 +56,24 @@ typedef struct {
        SoupMessageIOMode     mode;
        GCancellable         *cancellable;
 
-       GIOStream              *iostream;
-       SoupHTTPInputStream    *istream;
-       GInputStream           *body_istream;
-       GOutputStream          *ostream;
-       GOutputStream          *body_ostream;
-       GMainContext           *async_context;
+       GIOStream            *iostream;
+       SoupHTTPInputStream  *istream;
+       GInputStream         *body_istream;
+       SoupHTTPOutputStream *ostream;
+       GOutputStream        *body_ostream;
+       GMainContext         *async_context;
 
        SoupMessageIOState    read_state;
        SoupEncoding          read_encoding;
        SoupMessageBody      *read_body;
 
        SoupMessageIOState    write_state;
+       gboolean              write_got_headers;
        SoupEncoding          write_encoding;
        GString              *write_buf;
        SoupMessageBody      *write_body;
        SoupBuffer           *write_chunk;
        goffset               write_body_offset;
-       goffset               write_length;
        goffset               written;
 
        GSource *io_source;
@@ -112,6 +113,8 @@ soup_message_io_cleanup (SoupMessage *msg)
                g_object_unref (io->istream);
        if (io->body_istream)
                g_object_unref (io->body_istream);
+       if (io->ostream)
+               g_object_unref (io->ostream);
        if (io->body_ostream)
                g_object_unref (io->body_ostream);
        if (io->async_context)
@@ -303,25 +306,17 @@ io_write (SoupMessage *msg, gboolean blocking,
 
        switch (io->write_state) {
        case SOUP_MESSAGE_IO_STATE_HEADERS:
-               if (!io->write_buf->len) {
-                       io->get_headers_cb (msg, io->write_buf,
-                                           &io->write_encoding,
-                                           io->header_data);
+               if (!io->write_got_headers) {
+                       io->get_headers_cb (msg, io->ostream, io->header_data);
+                       io->write_got_headers = TRUE;
                }
 
-               while (io->written < io->write_buf->len) {
-                       nwrote = g_pollable_stream_write (io->ostream,
-                                                         io->write_buf->str + io->written,
-                                                         io->write_buf->len - io->written,
-                                                         blocking,
-                                                         cancellable, error);
-                       if (nwrote == -1)
-                               return FALSE;
-                       io->written += nwrote;
-               }
+               if (!soup_http_output_stream_write_headers (io->ostream, blocking,
+                                                           cancellable, error))
+                       return FALSE;
 
                io->written = 0;
-               g_string_truncate (io->write_buf, 0);
+               io->write_got_headers = FALSE;
 
                if (io->mode == SOUP_MESSAGE_IO_SERVER &&
                    SOUP_STATUS_IS_INFORMATIONAL (msg->status_code)) {
@@ -345,13 +340,6 @@ io_write (SoupMessage *msg, gboolean blocking,
                        break;
                }
 
-               if (io->write_encoding == SOUP_ENCODING_CONTENT_LENGTH) {
-                       SoupMessageHeaders *hdrs =
-                               (io->mode == SOUP_MESSAGE_IO_CLIENT) ?
-                               msg->request_headers : msg->response_headers;
-                       io->write_length = soup_message_headers_get_content_length (hdrs);
-               }
-
                if (io->mode == SOUP_MESSAGE_IO_CLIENT &&
                    soup_message_headers_get_expectations (msg->request_headers) & SOUP_EXPECTATION_CONTINUE) 
{
                        /* Need to wait for the Continue response */
@@ -374,17 +362,13 @@ io_write (SoupMessage *msg, gboolean blocking,
 
 
        case SOUP_MESSAGE_IO_STATE_BODY_START:
-               io->body_ostream = soup_body_output_stream_new (io->ostream,
-                                                               io->write_encoding,
-                                                               io->write_length);
+               io->body_ostream = soup_http_output_stream_get_body_stream (io->ostream);
                io->write_state = SOUP_MESSAGE_IO_STATE_BODY;
                break;
 
 
        case SOUP_MESSAGE_IO_STATE_BODY:
-               if (!io->write_length &&
-                   io->write_encoding != SOUP_ENCODING_EOF &&
-                   io->write_encoding != SOUP_ENCODING_CHUNKED) {
+               if (soup_body_output_stream_get_eof (SOUP_BODY_OUTPUT_STREAM (io->body_ostream))) {
                        io->write_state = SOUP_MESSAGE_IO_STATE_BODY_DONE;
                        break;
                }
@@ -413,9 +397,6 @@ io_write (SoupMessage *msg, gboolean blocking,
                chunk = soup_buffer_new_subbuffer (io->write_chunk,
                                                   io->written, nwrote);
                io->written += nwrote;
-               if (io->write_length)
-                       io->write_length -= nwrote;
-
                if (io->written == io->write_chunk->length)
                        io->write_state = SOUP_MESSAGE_IO_STATE_BODY_DATA;
 
@@ -1043,7 +1024,7 @@ new_iostate (SoupMessage *msg, GIOStream *iostream,
 
        io->iostream = g_object_ref (iostream);
        io->istream = SOUP_HTTP_INPUT_STREAM (soup_http1_input_stream_new (g_io_stream_get_input_stream 
(iostream)));
-       io->ostream = g_io_stream_get_output_stream (iostream);
+       io->ostream = SOUP_HTTP_OUTPUT_STREAM (soup_http1_output_stream_new (g_io_stream_get_output_stream 
(iostream)));
 
        if (async_context)
                io->async_context = g_main_context_ref (async_context);
diff --git a/libsoup/soup-message-private.h b/libsoup/soup-message-private.h
index 190d453..b10a412 100644
--- a/libsoup/soup-message-private.h
+++ b/libsoup/soup-message-private.h
@@ -11,6 +11,7 @@
 #include "soup-content-processor.h"
 #include "soup-content-sniffer.h"
 #include "soup-http-input-stream.h"
+#include "soup-http-output-stream.h"
 #include "soup-session.h"
 
 typedef struct {
@@ -51,8 +52,7 @@ void             soup_message_cleanup_response (SoupMessage      *msg);
 
 
 typedef void     (*SoupMessageGetHeadersFn)   (SoupMessage          *msg,
-                                              GString              *headers,
-                                              SoupEncoding         *encoding,
+                                              SoupHTTPOutputStream  *http,
                                               gpointer              user_data);
 typedef guint    (*SoupMessageParseHeadersFn) (SoupMessage          *msg,
                                               SoupHTTPInputStream  *http,
diff --git a/libsoup/soup-message-server-io.c b/libsoup/soup-message-server-io.c
index c3d3904..7fea8f3 100644
--- a/libsoup/soup-message-server-io.c
+++ b/libsoup/soup-message-server-io.c
@@ -153,40 +153,23 @@ handle_partial_get (SoupMessage *msg)
 }
 
 static void
-get_response_headers (SoupMessage *msg, GString *headers,
-                     SoupEncoding *encoding, gpointer user_data)
+get_response_headers (SoupMessage          *msg,
+                     SoupHTTPOutputStream *http,
+                     gpointer              user_data)
 {
-       SoupEncoding claimed_encoding;
-       SoupMessageHeadersIter iter;
-       const char *name, *value;
-
        handle_partial_get (msg);
 
-       g_string_append_printf (headers, "HTTP/1.%c %d %s\r\n",
-                               soup_message_get_http_version (msg) == SOUP_HTTP_1_0 ? '0' : '1',
-                               msg->status_code, msg->reason_phrase);
-
-       claimed_encoding = soup_message_headers_get_encoding (msg->response_headers);
-       if ((msg->method == SOUP_METHOD_HEAD ||
-            msg->status_code  == SOUP_STATUS_NO_CONTENT ||
-            msg->status_code  == SOUP_STATUS_NOT_MODIFIED ||
-            SOUP_STATUS_IS_INFORMATIONAL (msg->status_code)) ||
-           (msg->method == SOUP_METHOD_CONNECT &&
-            SOUP_STATUS_IS_SUCCESSFUL (msg->status_code)))
-               *encoding = SOUP_ENCODING_NONE;
-       else
-               *encoding = claimed_encoding;
-
-       if (claimed_encoding == SOUP_ENCODING_CONTENT_LENGTH &&
+       if (soup_message_headers_get_encoding (msg->response_headers) == SOUP_ENCODING_CONTENT_LENGTH &&
            !soup_message_headers_get_content_length (msg->response_headers)) {
                soup_message_headers_set_content_length (msg->response_headers,
                                                         msg->response_body->length);
        }
 
-       soup_message_headers_iter_init (&iter, msg->response_headers);
-       while (soup_message_headers_iter_next (&iter, &name, &value))
-               g_string_append_printf (headers, "%s: %s\r\n", name, value);
-       g_string_append (headers, "\r\n");
+       soup_http_output_stream_build_response_headers (http, msg->method,
+                                                       soup_message_get_http_version (msg),
+                                                       msg->status_code,
+                                                       msg->reason_phrase,
+                                                       msg->response_headers);
 }
 
 void


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