[libsoup/wip/http2: 3/7] Add SoupHTTPOutputStream, implement SoupHTTP1OutputStream
- From: Dan Winship <danw src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [libsoup/wip/http2: 3/7] Add SoupHTTPOutputStream, implement SoupHTTP1OutputStream
- Date: Tue, 18 Mar 2014 21:18:39 +0000 (UTC)
commit b113f70ef961417acd62203dd0463dfa089784d4
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 | 20 ++--
libsoup/soup-message-server-io.c | 35 ++----
13 files changed, 609 insertions(+), 140 deletions(-)
---
diff --git a/libsoup/Makefile.am b/libsoup/Makefile.am
index 90e0d83..b81dee3 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 2382225..6729df4 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 fbbac0d..087c096 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;
@@ -108,6 +109,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)
@@ -262,25 +265,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)) {
@@ -304,13 +299,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 */
@@ -333,17 +321,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;
}
@@ -372,9 +356,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;
@@ -987,7 +968,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 b4b86a9..1c8214e 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 {
@@ -50,16 +51,15 @@ typedef struct {
void soup_message_cleanup_response (SoupMessage *msg);
-typedef void (*SoupMessageGetHeadersFn) (SoupMessage *msg,
- GString *headers,
- SoupEncoding *encoding,
- gpointer user_data);
-typedef guint (*SoupMessageParseHeadersFn) (SoupMessage *msg,
- SoupHTTPInputStream *http,
- gpointer user_data,
- GError **error);
-typedef void (*SoupMessageCompletionFn) (SoupMessage *msg,
- gpointer user_data);
+typedef void (*SoupMessageGetHeadersFn) (SoupMessage *msg,
+ SoupHTTPOutputStream *http,
+ gpointer user_data);
+typedef guint (*SoupMessageParseHeadersFn) (SoupMessage *msg,
+ SoupHTTPInputStream *http,
+ gpointer user_data,
+ GError **error);
+typedef void (*SoupMessageCompletionFn) (SoupMessage *msg,
+ gpointer user_data);
void soup_message_send_request (SoupMessageQueueItem *item,
diff --git a/libsoup/soup-message-server-io.c b/libsoup/soup-message-server-io.c
index 01a4fd2..e21c000 100644
--- a/libsoup/soup-message-server-io.c
+++ b/libsoup/soup-message-server-io.c
@@ -152,40 +152,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]