[libsoup/wip/http2-b: 2/3] Add SoupHTTPChannel to abstract HTTP I/O
- From: Dan Winship <danw src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [libsoup/wip/http2-b: 2/3] Add SoupHTTPChannel to abstract HTTP I/O
- Date: Thu, 26 Feb 2015 15:50:57 +0000 (UTC)
commit d12416cdc816d579a3eb3b6a57199848b7c0ec48
Author: Dan Winship <danw gnome org>
Date: Sun Dec 8 14:48:52 2013 +0100
Add SoupHTTPChannel to abstract HTTP I/O
Add an abstract SoupHTTPChannel class, and a SoupHTTP1Channel
implementation for HTTP/1.x. This incorporates all of the code that
was formerly in soup-message-client-io.c and soup-message-server-io.c,
along with various other bits.
libsoup/Makefile.am | 6 +-
libsoup/soup-body-output-stream.c | 20 +-
libsoup/soup-body-output-stream.h | 8 +-
libsoup/soup-connection.c | 10 +-
libsoup/soup-http-channel.c | 313 ++++++++++++++++++++
libsoup/soup-http-channel.h | 85 ++++++
libsoup/soup-http1-channel.c | 587 +++++++++++++++++++++++++++++++++++++
libsoup/soup-http1-channel.h | 35 +++
libsoup/soup-message-client-io.c | 166 -----------
libsoup/soup-message-io.c | 243 +++------------
libsoup/soup-message-private.h | 36 +--
libsoup/soup-message-server-io.c | 281 ------------------
libsoup/soup-server.c | 13 +-
po/POTFILES.in | 3 +-
14 files changed, 1119 insertions(+), 687 deletions(-)
---
diff --git a/libsoup/Makefile.am b/libsoup/Makefile.am
index 19dff18..da96e63 100644
--- a/libsoup/Makefile.am
+++ b/libsoup/Makefile.am
@@ -139,16 +139,18 @@ libsoup_2_4_la_SOURCES = \
soup-filter-input-stream.h \
soup-form.c \
soup-headers.c \
+ soup-http-channel.c \
+ soup-http-channel.h \
+ soup-http1-channel.c \
+ soup-http1-channel.h \
soup-logger.c \
soup-message.c \
soup-message-body.c \
- soup-message-client-io.c \
soup-message-headers.c \
soup-message-io.c \
soup-message-private.h \
soup-message-queue.h \
soup-message-queue.c \
- soup-message-server-io.c \
soup-method.c \
soup-misc.c \
soup-misc-private.h \
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-connection.c b/libsoup/soup-connection.c
index 436b8cb..652d9ce 100644
--- a/libsoup/soup-connection.c
+++ b/libsoup/soup-connection.c
@@ -702,6 +702,7 @@ soup_connection_send_request (SoupConnection *conn,
gpointer user_data)
{
SoupConnectionPrivate *priv;
+ GMainContext *async_context;
g_return_if_fail (SOUP_IS_CONNECTION (conn));
g_return_if_fail (item != NULL);
@@ -714,5 +715,12 @@ soup_connection_send_request (SoupConnection *conn,
else
priv->reusable = FALSE;
- soup_message_send_request (item, completion_cb, user_data);
+ if (!SOUP_IS_SESSION_SYNC (item->session)) {
+ async_context = soup_session_get_async_context (item->session);
+ if (!async_context)
+ async_context = g_main_context_default ();
+ } else
+ async_context = NULL;
+
+ soup_message_io_client (item, async_context, completion_cb, user_data);
}
diff --git a/libsoup/soup-http-channel.c b/libsoup/soup-http-channel.c
new file mode 100644
index 0000000..867bb0b
--- /dev/null
+++ b/libsoup/soup-http-channel.c
@@ -0,0 +1,313 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * soup-http-channel.c
+ *
+ * Copyright 2014 Red Hat, Inc.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "soup-http-channel.h"
+#include "soup.h"
+#include "soup-misc-private.h"
+
+/**
+ * SECTION:soup-http-channel
+ * @short_description: Channel for a single HTTP request/response
+ *
+ * #SoupHTTPChannel is an abstract type representing a communication
+ * channel for handling a single HTTP request and response.
+ */
+
+G_DEFINE_ABSTRACT_TYPE (SoupHTTPChannel, soup_http_channel, G_TYPE_OBJECT)
+
+enum {
+ PROP_0,
+
+ PROP_MESSAGE,
+ PROP_MODE
+};
+
+typedef struct {
+ SoupMessage *msg;
+ SoupHTTPChannelMode mode;
+
+} SoupHTTPChannelPrivate;
+#define SOUP_HTTP_CHANNEL_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), SOUP_TYPE_HTTP_CHANNEL,
SoupHTTPChannelPrivate))
+
+static void
+soup_http_channel_init (SoupHTTPChannel *channel)
+{
+}
+
+static void
+soup_http_channel_set_property (GObject *object, guint prop_id,
+ const GValue *value, GParamSpec *pspec)
+{
+ SoupHTTPChannelPrivate *priv = SOUP_HTTP_CHANNEL_GET_PRIVATE (object);
+
+ switch (prop_id) {
+ case PROP_MESSAGE:
+ priv->msg = g_value_dup_object (value);
+ break;
+ case PROP_MODE:
+ priv->mode = g_value_get_int (value);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+soup_http_channel_get_property (GObject *object, guint prop_id,
+ GValue *value, GParamSpec *pspec)
+{
+ SoupHTTPChannelPrivate *priv = SOUP_HTTP_CHANNEL_GET_PRIVATE (object);
+
+ switch (prop_id) {
+ case PROP_MESSAGE:
+ g_value_set_object (value, priv->msg);
+ break;
+ case PROP_MODE:
+ g_value_set_int (value, priv->mode);
+ break;
+ default:
+ G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+ break;
+ }
+}
+
+static void
+soup_http_channel_dispose (GObject *object)
+{
+ SoupHTTPChannelPrivate *priv = SOUP_HTTP_CHANNEL_GET_PRIVATE (object);
+
+ g_clear_object (&priv->msg);
+
+ G_OBJECT_CLASS (soup_http_channel_parent_class)->dispose (object);
+}
+
+static void
+soup_http_channel_class_init (SoupHTTPChannelClass *channel_class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (channel_class);
+
+ g_type_class_add_private (channel_class, sizeof (SoupHTTPChannelPrivate));
+
+ object_class->set_property = soup_http_channel_set_property;
+ object_class->get_property = soup_http_channel_get_property;
+ object_class->dispose = soup_http_channel_dispose;
+
+ g_object_class_install_property (
+ object_class, PROP_MESSAGE,
+ g_param_spec_object (SOUP_HTTP_CHANNEL_MESSAGE,
+ "SoupMessage",
+ "The channel's SoupMessage",
+ SOUP_TYPE_MESSAGE,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS |
+ G_PARAM_CONSTRUCT_ONLY));
+ g_object_class_install_property (
+ object_class, PROP_MODE,
+ /* We don't parse private headers for enum types, so use int */
+ g_param_spec_int (SOUP_HTTP_CHANNEL_MODE,
+ "Mode",
+ "The channel's SoupHTTPChannelMode",
+ -1, G_MAXINT, -1,
+ G_PARAM_READWRITE |
+ G_PARAM_STATIC_STRINGS |
+ G_PARAM_CONSTRUCT_ONLY));
+}
+
+gboolean
+soup_http_channel_read_headers (SoupHTTPChannel *channel,
+ gboolean blocking,
+ GCancellable *cancellable,
+ GError **error)
+{
+ if (SOUP_HTTP_CHANNEL_GET_PRIVATE (channel)->mode == SOUP_HTTP_CHANNEL_CLIENT) {
+ return SOUP_HTTP_CHANNEL_GET_CLASS (channel)->
+ read_response_headers (channel, blocking, cancellable, error);
+ } else {
+ return SOUP_HTTP_CHANNEL_GET_CLASS (channel)->
+ read_request_headers (channel, blocking, cancellable, error);
+ }
+}
+
+static void
+handle_partial_get (SoupMessage *msg)
+{
+ SoupRange *ranges;
+ int nranges;
+ SoupBuffer *full_response;
+ guint status;
+
+ /* Make sure the message is set up right for us to return a
+ * partial response; it has to be a GET, the status must be
+ * 200 OK (and in particular, NOT already 206 Partial
+ * Content), and the SoupServer must have already filled in
+ * the response body
+ */
+ if (msg->method != SOUP_METHOD_GET ||
+ msg->status_code != SOUP_STATUS_OK ||
+ soup_message_headers_get_encoding (msg->response_headers) !=
+ SOUP_ENCODING_CONTENT_LENGTH ||
+ msg->response_body->length == 0 ||
+ !soup_message_body_get_accumulate (msg->response_body))
+ return;
+
+ /* Oh, and there has to have been a valid Range header on the
+ * request, of course.
+ */
+ status = soup_message_headers_get_ranges_internal (msg->request_headers,
+ msg->response_body->length,
+ TRUE,
+ &ranges, &nranges);
+ if (status == SOUP_STATUS_REQUESTED_RANGE_NOT_SATISFIABLE) {
+ soup_message_set_status (msg, status);
+ soup_message_body_truncate (msg->response_body);
+ return;
+ } else if (status != SOUP_STATUS_PARTIAL_CONTENT)
+ return;
+
+ full_response = soup_message_body_flatten (msg->response_body);
+ if (!full_response) {
+ soup_message_headers_free_ranges (msg->request_headers, ranges);
+ return;
+ }
+
+ soup_message_set_status (msg, SOUP_STATUS_PARTIAL_CONTENT);
+ soup_message_body_truncate (msg->response_body);
+
+ if (nranges == 1) {
+ SoupBuffer *range_buf;
+
+ /* Single range, so just set Content-Range and fix the body. */
+
+ soup_message_headers_set_content_range (msg->response_headers,
+ ranges[0].start,
+ ranges[0].end,
+ full_response->length);
+ range_buf = soup_buffer_new_subbuffer (full_response,
+ ranges[0].start,
+ ranges[0].end - ranges[0].start + 1);
+ soup_message_body_append_buffer (msg->response_body, range_buf);
+ soup_buffer_free (range_buf);
+ } else {
+ SoupMultipart *multipart;
+ SoupMessageHeaders *part_headers;
+ SoupBuffer *part_body;
+ const char *content_type;
+ int i;
+
+ /* Multiple ranges, so build a multipart/byteranges response
+ * to replace msg->response_body with.
+ */
+
+ multipart = soup_multipart_new ("multipart/byteranges");
+ content_type = soup_message_headers_get_one (msg->response_headers,
+ "Content-Type");
+ for (i = 0; i < nranges; i++) {
+ part_headers = soup_message_headers_new (SOUP_MESSAGE_HEADERS_MULTIPART);
+ if (content_type) {
+ soup_message_headers_append (part_headers,
+ "Content-Type",
+ content_type);
+ }
+ soup_message_headers_set_content_range (part_headers,
+ ranges[i].start,
+ ranges[i].end,
+ full_response->length);
+ part_body = soup_buffer_new_subbuffer (full_response,
+ ranges[i].start,
+ ranges[i].end - ranges[i].start + 1);
+ soup_multipart_append_part (multipart, part_headers,
+ part_body);
+ soup_message_headers_free (part_headers);
+ soup_buffer_free (part_body);
+ }
+
+ soup_multipart_to_message (multipart, msg->response_headers,
+ msg->response_body);
+ soup_multipart_free (multipart);
+ }
+
+ soup_buffer_free (full_response);
+ soup_message_headers_free_ranges (msg->request_headers, ranges);
+}
+
+gboolean
+soup_http_channel_write_headers (SoupHTTPChannel *channel,
+ gboolean blocking,
+ GCancellable *cancellable,
+ GError **error)
+{
+ SoupHTTPChannelPrivate *priv = SOUP_HTTP_CHANNEL_GET_PRIVATE (channel);
+
+ if (SOUP_HTTP_CHANNEL_GET_PRIVATE (channel)->mode == SOUP_HTTP_CHANNEL_CLIENT) {
+ SoupEncoding encoding;
+
+ /* Fix up unspecified encoding */
+ encoding = soup_message_headers_get_encoding (priv->msg->request_headers);
+ if ((encoding == SOUP_ENCODING_CONTENT_LENGTH ||
+ encoding == SOUP_ENCODING_NONE) &&
+ (priv->msg->request_body->length > 0 ||
+ soup_message_headers_get_one (priv->msg->request_headers, "Content-Type")) &&
+ !soup_message_headers_get_content_length (priv->msg->request_headers)) {
+ soup_message_headers_set_content_length (priv->msg->request_headers,
+ priv->msg->request_body->length);
+ }
+
+ return SOUP_HTTP_CHANNEL_GET_CLASS (channel)->
+ write_request_headers (channel, blocking, cancellable, error);
+ } else {
+ handle_partial_get (priv->msg);
+
+ /* Fix up unspecified encoding */
+ if (soup_message_headers_get_encoding (priv->msg->response_headers) ==
SOUP_ENCODING_CONTENT_LENGTH &&
+ !soup_message_headers_get_content_length (priv->msg->response_headers)) {
+ soup_message_headers_set_content_length (priv->msg->response_headers,
+ priv->msg->response_body->length);
+ }
+
+ return SOUP_HTTP_CHANNEL_GET_CLASS (channel)->
+ write_response_headers (channel, blocking, cancellable, error);
+ }
+}
+
+GInputStream *
+soup_http_channel_get_body_input_stream (SoupHTTPChannel *channel)
+{
+ return SOUP_HTTP_CHANNEL_GET_CLASS (channel)->get_body_input_stream (channel);
+}
+
+GOutputStream *
+soup_http_channel_get_body_output_stream (SoupHTTPChannel *channel)
+{
+ return SOUP_HTTP_CHANNEL_GET_CLASS (channel)->get_body_output_stream (channel);
+}
+
+GSource *
+soup_http_channel_create_oneshot_source (SoupHTTPChannel *channel,
+ GIOCondition cond,
+ GCancellable *cancellable)
+{
+ g_return_val_if_fail (cond == G_IO_IN || cond == G_IO_OUT, NULL);
+
+ return SOUP_HTTP_CHANNEL_GET_CLASS (channel)->create_oneshot_source (channel, cond, cancellable);
+}
+
+SoupMessage *
+soup_http_channel_get_message (SoupHTTPChannel *channel)
+{
+ return SOUP_HTTP_CHANNEL_GET_PRIVATE (channel)->msg;
+}
+
+SoupHTTPChannelMode
+soup_http_channel_get_mode (SoupHTTPChannel *channel)
+{
+ return SOUP_HTTP_CHANNEL_GET_PRIVATE (channel)->mode;
+}
diff --git a/libsoup/soup-http-channel.h b/libsoup/soup-http-channel.h
new file mode 100644
index 0000000..4926a90
--- /dev/null
+++ b/libsoup/soup-http-channel.h
@@ -0,0 +1,85 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright 2014 Red Hat, Inc.
+ */
+
+#ifndef SOUP_HTTP_CHANNEL_H
+#define SOUP_HTTP_CHANNEL_H 1
+
+#include "soup-types.h"
+
+#define SOUP_TYPE_HTTP_CHANNEL (soup_http_channel_get_type ())
+#define SOUP_HTTP_CHANNEL(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SOUP_TYPE_HTTP_CHANNEL,
SoupHTTPChannel))
+#define SOUP_HTTP_CHANNEL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), SOUP_TYPE_HTTP_CHANNEL,
SoupHTTPChannelClass))
+#define SOUP_IS_HTTP_CHANNEL(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SOUP_TYPE_HTTP_CHANNEL))
+#define SOUP_IS_HTTP_CHANNEL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), SOUP_TYPE_HTTP_CHANNEL))
+#define SOUP_HTTP_CHANNEL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), SOUP_TYPE_HTTP_CHANNEL,
SoupHTTPChannelClass))
+
+#define SOUP_HTTP_CHANNEL_MESSAGE "message"
+#define SOUP_HTTP_CHANNEL_MODE "mode"
+
+typedef struct {
+ GObject parent;
+
+} SoupHTTPChannel;
+
+typedef struct {
+ GObjectClass parent_class;
+
+ gboolean (*read_request_headers) (SoupHTTPChannel *channel,
+ gboolean blocking,
+ GCancellable *cancellable,
+ GError **error);
+
+ gboolean (*read_response_headers) (SoupHTTPChannel *channel,
+ gboolean blocking,
+ GCancellable *cancellable,
+ GError **error);
+
+ gboolean (*write_request_headers) (SoupHTTPChannel *channel,
+ gboolean blocking,
+ GCancellable *cancellable,
+ GError **error);
+
+ gboolean (*write_response_headers) (SoupHTTPChannel *channel,
+ gboolean blocking,
+ GCancellable *cancellable,
+ GError **error);
+
+ GInputStream * (*get_body_input_stream) (SoupHTTPChannel *channel);
+ GOutputStream * (*get_body_output_stream) (SoupHTTPChannel *channel);
+
+ GSource * (*create_oneshot_source) (SoupHTTPChannel *channel,
+ GIOCondition cond,
+ GCancellable *cancellable);
+
+} SoupHTTPChannelClass;
+
+typedef enum {
+ SOUP_HTTP_CHANNEL_CLIENT,
+ SOUP_HTTP_CHANNEL_SERVER
+} SoupHTTPChannelMode;
+
+GType soup_http_channel_get_type (void);
+
+gboolean soup_http_channel_read_headers (SoupHTTPChannel *channel,
+ gboolean blocking,
+ GCancellable *cancellable,
+ GError **error);
+
+gboolean soup_http_channel_write_headers (SoupHTTPChannel *channel,
+ gboolean blocking,
+ GCancellable *cancellable,
+ GError **error);
+
+GInputStream * soup_http_channel_get_body_input_stream (SoupHTTPChannel *channel);
+GOutputStream * soup_http_channel_get_body_output_stream (SoupHTTPChannel *channel);
+
+GSource * soup_http_channel_create_oneshot_source (SoupHTTPChannel *channel,
+ GIOCondition cond,
+ GCancellable *cancellable);
+
+SoupMessage * soup_http_channel_get_message (SoupHTTPChannel *channel);
+SoupHTTPChannelMode soup_http_channel_get_mode (SoupHTTPChannel *channel);
+
+#endif /* SOUP_HTTP_CHANNEL_H */
diff --git a/libsoup/soup-http1-channel.c b/libsoup/soup-http1-channel.c
new file mode 100644
index 0000000..332d49f
--- /dev/null
+++ b/libsoup/soup-http1-channel.c
@@ -0,0 +1,587 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * soup-http1-channel.c
+ *
+ * Copyright 2014 Red Hat, Inc.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include <string.h>
+
+#include <glib/gi18n-lib.h>
+
+#include "soup-http1-channel.h"
+#include "soup.h"
+#include "soup-body-input-stream.h"
+#include "soup-body-output-stream.h"
+#include "soup-filter-input-stream.h"
+#include "soup-socket-private.h"
+
+G_DEFINE_TYPE (SoupHTTP1Channel, soup_http1_channel, SOUP_TYPE_HTTP_CHANNEL)
+
+typedef struct {
+ SoupSocket *server_sock;
+
+ SoupFilterInputStream *istream;
+ GPollableInputStream *poll_istream;
+ GOutputStream *ostream;
+ GPollableOutputStream *poll_ostream;
+
+ GString *input_headers, *output_headers;
+ gboolean headers_read;
+ gsize headers_nwritten;
+
+ SoupEncoding input_encoding, output_encoding;
+ goffset input_length, output_length;
+
+} SoupHTTP1ChannelPrivate;
+#define SOUP_HTTP1_CHANNEL_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o), SOUP_TYPE_HTTP1_CHANNEL,
SoupHTTP1ChannelPrivate))
+
+static void
+soup_http1_channel_init (SoupHTTP1Channel *channel)
+{
+ SoupHTTP1ChannelPrivate *priv = SOUP_HTTP1_CHANNEL_GET_PRIVATE (channel);
+
+ priv->input_headers = g_string_new (NULL);
+ priv->output_headers = g_string_new (NULL);
+}
+
+#define READ_BUFFER_SIZE 8192
+
+static gboolean
+read_headers (SoupHTTPChannel *channel,
+ gboolean blocking,
+ GCancellable *cancellable,
+ GError **error)
+{
+ SoupHTTP1ChannelPrivate *priv = SOUP_HTTP1_CHANNEL_GET_PRIVATE (channel);
+ gssize nread, old_len;
+ gboolean got_lf;
+
+ if (priv->headers_read) {
+ /* restart */
+ g_string_truncate (priv->input_headers, 0);
+ priv->headers_read = FALSE;
+ }
+
+ while (1) {
+ old_len = priv->input_headers->len;
+ g_string_set_size (priv->input_headers, old_len + READ_BUFFER_SIZE);
+ nread = soup_filter_input_stream_read_line (priv->istream,
+ priv->input_headers->str + old_len,
+ READ_BUFFER_SIZE,
+ blocking,
+ &got_lf,
+ cancellable, error);
+ priv->input_headers->len = old_len + MAX (nread, 0);
+
+ if (nread < 0)
+ return FALSE;
+ else if (nread == 0) {
+ g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_PARTIAL_INPUT,
+ _("Connection terminated unexpectedly"));
+ return FALSE;
+ }
+
+ if (got_lf) {
+ if (nread == 1 && old_len >= 2 &&
+ !strncmp (priv->input_headers->str +
+ priv->input_headers->len - 2,
+ "\n\n", 2))
+ break;
+ else if (nread == 2 && old_len >= 3 &&
+ !strncmp (priv->input_headers->str +
+ priv->input_headers->len - 3,
+ "\n\r\n", 3))
+ break;
+ }
+ }
+
+ /* We need to "rewind" priv->input_headers back one line.
+ * That SHOULD be two characters (CR LF), but if the
+ * web server was stupid, it might only be one.
+ */
+ if (priv->input_headers->len < 3 ||
+ priv->input_headers->str[priv->input_headers->len - 2] == '\n')
+ priv->input_headers->len--;
+ else
+ priv->input_headers->len -= 2;
+ priv->input_headers->str[priv->input_headers->len] = '\0';
+
+ priv->headers_read = TRUE;
+ return TRUE;
+}
+
+static gboolean
+soup_http1_channel_read_request_headers (SoupHTTPChannel *channel,
+ gboolean blocking,
+ GCancellable *cancellable,
+ GError **error)
+{
+ SoupHTTP1ChannelPrivate *priv = SOUP_HTTP1_CHANNEL_GET_PRIVATE (channel);
+ SoupMessage *msg = soup_http_channel_get_message (channel);
+ guint status;
+ char *req_method, *req_path, *uri_string;
+ const char *req_host;
+ SoupHTTPVersion version;
+ SoupURI *uri;
+
+ if (!read_headers (channel, blocking, cancellable, error))
+ return FALSE;
+
+ status = soup_headers_parse_request (priv->input_headers->str,
+ priv->input_headers->len,
+ msg->request_headers,
+ &req_method, &req_path, &version);
+
+ if (status != SOUP_STATUS_OK) {
+ failed:
+ g_set_error_literal (error, SOUP_HTTP_ERROR, status,
+ _("Could not parse HTTP request"));
+ return FALSE;
+ }
+
+ g_object_set (msg,
+ SOUP_MESSAGE_METHOD, req_method,
+ SOUP_MESSAGE_HTTP_VERSION, version,
+ NULL);
+ g_free (req_method);
+
+ /* Handle request body encoding */
+ priv->input_encoding = soup_message_headers_get_encoding (msg->request_headers);
+ if (priv->input_encoding == SOUP_ENCODING_UNRECOGNIZED) {
+ g_free (req_path);
+ if (soup_message_headers_get_list (msg->request_headers, "Transfer-Encoding"))
+ status = SOUP_STATUS_NOT_IMPLEMENTED;
+ else
+ status = SOUP_STATUS_BAD_REQUEST;
+ goto failed;
+ }
+ if (priv->input_encoding == SOUP_ENCODING_CONTENT_LENGTH)
+ priv->input_length = soup_message_headers_get_content_length (msg->request_headers);
+ else
+ priv->input_length = -1;
+
+ /* Generate correct context for request */
+ req_host = soup_message_headers_get_one (msg->request_headers, "Host");
+ if (req_host && strchr (req_host, '/')) {
+ g_free (req_path);
+ status = SOUP_STATUS_BAD_REQUEST;
+ goto failed;
+ }
+
+ if (!strcmp (req_path, "*") && req_host) {
+ /* Eg, "OPTIONS * HTTP/1.1" */
+ uri_string = g_strdup_printf ("%s://%s",
+ soup_socket_is_ssl (priv->server_sock) ? "https" : "http",
+ req_host);
+ uri = soup_uri_new (uri_string);
+ if (uri)
+ soup_uri_set_path (uri, "*");
+ g_free (uri_string);
+ } else if (*req_path != '/') {
+ /* Must be an absolute URI */
+ uri = soup_uri_new (req_path);
+ } else if (req_host) {
+ uri_string = g_strdup_printf ("%s://%s%s",
+ soup_socket_is_ssl (priv->server_sock) ? "https" : "http",
+ req_host, req_path);
+ uri = soup_uri_new (uri_string);
+ g_free (uri_string);
+ } else if (version == SOUP_HTTP_1_0) {
+ /* No Host header, no AbsoluteUri */
+ SoupAddress *addr = soup_socket_get_local_address (priv->server_sock);
+
+ uri = soup_uri_new (NULL);
+ soup_uri_set_scheme (uri, soup_socket_is_ssl (priv->server_sock) ? "https" : "http");
+ soup_uri_set_host (uri, soup_address_get_physical (addr));
+ soup_uri_set_port (uri, soup_address_get_port (addr));
+ soup_uri_set_path (uri, req_path);
+ } else
+ uri = NULL;
+
+ g_free (req_path);
+
+ if (!uri || !uri->host) {
+ if (uri)
+ soup_uri_free (uri);
+ status = SOUP_STATUS_BAD_REQUEST;
+ goto failed;
+ }
+
+ g_object_set (msg,
+ SOUP_MESSAGE_URI, uri,
+ NULL);
+ soup_uri_free (uri);
+
+ return TRUE;
+}
+
+static gboolean
+soup_http1_channel_read_response_headers (SoupHTTPChannel *channel,
+ gboolean blocking,
+ GCancellable *cancellable,
+ GError **error)
+{
+ SoupHTTP1ChannelPrivate *priv = SOUP_HTTP1_CHANNEL_GET_PRIVATE (channel);
+ SoupMessage *msg = soup_http_channel_get_message (channel);
+ SoupHTTPVersion version;
+ guint status_code;
+ char *reason_phrase;
+ gboolean ok;
+
+ if (!read_headers (channel, blocking, cancellable, error))
+ return FALSE;
+
+ ok = soup_headers_parse_response (priv->input_headers->str,
+ priv->input_headers->len,
+ msg->response_headers,
+ &version, &status_code, &reason_phrase);
+
+ if (!ok) {
+ g_set_error_literal (error, SOUP_HTTP_ERROR,
+ SOUP_STATUS_MALFORMED,
+ _("Could not parse HTTP response"));
+ return FALSE;
+ }
+
+ g_object_set (msg,
+ SOUP_MESSAGE_STATUS_CODE, status_code,
+ SOUP_MESSAGE_REASON_PHRASE, reason_phrase,
+ SOUP_MESSAGE_HTTP_VERSION, MIN (version, soup_message_get_http_version (msg)),
+ NULL);
+
+ if (msg->method == SOUP_METHOD_HEAD ||
+ status_code == SOUP_STATUS_NO_CONTENT ||
+ status_code == SOUP_STATUS_NOT_MODIFIED ||
+ SOUP_STATUS_IS_INFORMATIONAL (status_code) ||
+ (msg->method == SOUP_METHOD_CONNECT &&
+ SOUP_STATUS_IS_SUCCESSFUL (status_code)))
+ priv->input_encoding = SOUP_ENCODING_NONE;
+ else {
+ priv->input_encoding = soup_message_headers_get_encoding (msg->response_headers);
+
+ if (priv->input_encoding == SOUP_ENCODING_UNRECOGNIZED) {
+ g_set_error_literal (error, SOUP_HTTP_ERROR,
+ SOUP_STATUS_NOT_IMPLEMENTED,
+ _("Unrecognized HTTP encoding"));
+ return FALSE;
+ }
+ }
+
+ if (priv->input_encoding == SOUP_ENCODING_CONTENT_LENGTH) {
+ const char *conn;
+
+ priv->input_length = soup_message_headers_get_content_length (msg->response_headers);
+
+ /* Some servers suck and send incorrect Content-Length
+ * values, so if the message isn't keepalive anyway, allow
+ * EOF termination.
+ */
+ conn = soup_message_headers_get_one (msg->response_headers, "Connection");
+ if (version == SOUP_HTTP_1_0 &&
+ (!conn || !soup_header_contains (conn, "Keep-Alive")))
+ priv->input_encoding = SOUP_ENCODING_EOF;
+ else if (version == SOUP_HTTP_1_1 && conn &&
+ soup_header_contains (conn, "close"))
+ priv->input_encoding = SOUP_ENCODING_EOF;
+ } else
+ priv->input_length = -1;
+
+ return TRUE;
+}
+
+static gboolean
+write_headers (SoupHTTPChannel *channel,
+ gboolean blocking,
+ GCancellable *cancellable,
+ GError **error)
+{
+ SoupHTTP1ChannelPrivate *priv = SOUP_HTTP1_CHANNEL_GET_PRIVATE (channel);
+ gssize nwrote;
+
+ while (priv->headers_nwritten < priv->output_headers->len) {
+ nwrote = g_pollable_stream_write (priv->ostream,
+ priv->output_headers->str + priv->headers_nwritten,
+ priv->output_headers->len - priv->headers_nwritten,
+ blocking, cancellable, error);
+ if (nwrote == -1)
+ return FALSE;
+ priv->headers_nwritten += nwrote;
+ }
+ return TRUE;
+}
+
+static void
+finish_build_headers (SoupHTTPChannel *channel,
+ SoupMessageHeaders *headers)
+{
+ SoupHTTP1ChannelPrivate *priv = SOUP_HTTP1_CHANNEL_GET_PRIVATE (channel);
+ SoupMessageHeadersIter iter;
+ const char *name, *value;
+
+ if (priv->output_encoding == SOUP_ENCODING_CONTENT_LENGTH)
+ priv->output_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->output_headers, "%s: %s\r\n", name, value);
+ g_string_append (priv->output_headers, "\r\n");
+}
+
+static void
+build_request_headers (SoupHTTPChannel *channel)
+{
+ SoupHTTP1ChannelPrivate *priv = SOUP_HTTP1_CHANNEL_GET_PRIVATE (channel);
+ SoupMessage *msg = soup_http_channel_get_message (channel);
+ SoupURI *request_uri = soup_message_get_uri (msg);
+ char *uri_host;
+ char *uri_string;
+
+ g_string_truncate (priv->output_headers, 0);
+ priv->headers_nwritten = 0;
+
+ 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 (msg->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.
+ */
+ if (soup_connection_is_via_proxy (soup_message_get_connection (msg))) {
+ uri_string = soup_uri_to_string (request_uri, FALSE);
+ if (request_uri->fragment) {
+ /* Strip fragment */
+ char *fragment = strchr (uri_string, '#');
+ if (fragment)
+ *fragment = '\0';
+ }
+ } else
+ uri_string = soup_uri_to_string (request_uri, TRUE);
+ }
+
+ g_string_append_printf (priv->output_headers, "%s %s HTTP/1.%d\r\n",
+ msg->method, uri_string,
+ (soup_message_get_http_version (msg) == SOUP_HTTP_1_0) ? 0 : 1);
+
+ if (!soup_message_headers_get_one (msg->request_headers, "Host")) {
+ if (soup_uri_uses_default_port (request_uri)) {
+ g_string_append_printf (priv->output_headers, "Host: %s\r\n",
+ uri_host);
+ } else {
+ g_string_append_printf (priv->output_headers, "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->output_encoding = soup_message_headers_get_encoding (msg->request_headers);
+ finish_build_headers (channel, msg->request_headers);
+}
+
+static void
+build_response_headers (SoupHTTPChannel *channel)
+{
+ SoupHTTP1ChannelPrivate *priv = SOUP_HTTP1_CHANNEL_GET_PRIVATE (channel);
+ SoupMessage *msg = soup_http_channel_get_message (channel);
+ SoupEncoding claimed_encoding;
+
+ g_string_truncate (priv->output_headers, 0);
+ priv->headers_nwritten = 0;
+
+ g_string_append_printf (priv->output_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)))
+ priv->output_encoding = SOUP_ENCODING_NONE;
+ else
+ priv->output_encoding = claimed_encoding;
+
+ finish_build_headers (channel, msg->response_headers);
+}
+
+static gboolean
+soup_http1_channel_write_request_headers (SoupHTTPChannel *channel,
+ gboolean blocking,
+ GCancellable *cancellable,
+ GError **error)
+{
+ SoupHTTP1ChannelPrivate *priv = SOUP_HTTP1_CHANNEL_GET_PRIVATE (channel);
+
+ if (priv->headers_nwritten == priv->output_headers->len)
+ build_request_headers (channel);
+ return write_headers (channel, blocking, cancellable, error);
+}
+
+static gboolean
+soup_http1_channel_write_response_headers (SoupHTTPChannel *channel,
+ gboolean blocking,
+ GCancellable *cancellable,
+ GError **error)
+{
+ SoupHTTP1ChannelPrivate *priv = SOUP_HTTP1_CHANNEL_GET_PRIVATE (channel);
+
+ if (priv->headers_nwritten == priv->output_headers->len)
+ build_response_headers (channel);
+ return write_headers (channel, blocking, cancellable, error);
+}
+
+static GInputStream *
+soup_http1_channel_get_body_input_stream (SoupHTTPChannel *channel)
+{
+ SoupHTTP1ChannelPrivate *priv = SOUP_HTTP1_CHANNEL_GET_PRIVATE (channel);
+ GInputStream *istream;
+
+ g_return_val_if_fail (priv->headers_read, NULL);
+
+ istream = soup_body_input_stream_new (G_INPUT_STREAM (priv->istream),
+ priv->input_encoding,
+ priv->input_length);
+ g_object_unref (priv->poll_istream);
+ priv->poll_istream = g_object_ref (istream);
+
+ return istream;
+}
+
+static GOutputStream *
+soup_http1_channel_get_body_output_stream (SoupHTTPChannel *channel)
+{
+ SoupHTTP1ChannelPrivate *priv = SOUP_HTTP1_CHANNEL_GET_PRIVATE (channel);
+ GOutputStream *ostream;
+
+ g_return_val_if_fail (priv->headers_nwritten != 0, NULL);
+
+ ostream = soup_body_output_stream_new (priv->ostream,
+ priv->output_encoding,
+ priv->output_length);
+ g_object_unref (priv->poll_ostream);
+ priv->poll_ostream = g_object_ref (ostream);
+
+ return ostream;
+}
+
+static GSource *
+soup_http1_channel_create_oneshot_source (SoupHTTPChannel *channel,
+ GIOCondition cond,
+ GCancellable *cancellable)
+{
+ SoupHTTP1ChannelPrivate *priv = SOUP_HTTP1_CHANNEL_GET_PRIVATE (channel);
+
+ if (cond == G_IO_IN)
+ return g_pollable_input_stream_create_source (priv->poll_istream, cancellable);
+ else if (cond == G_IO_OUT)
+ return g_pollable_output_stream_create_source (priv->poll_ostream, cancellable);
+ else
+ g_assert_not_reached ();
+}
+
+static void
+soup_http1_channel_dispose (GObject *object)
+{
+ SoupHTTP1ChannelPrivate *priv = SOUP_HTTP1_CHANNEL_GET_PRIVATE (object);
+
+ g_clear_object (&priv->server_sock);
+ g_clear_object (&priv->istream);
+ g_clear_object (&priv->poll_istream);
+ g_clear_object (&priv->ostream);
+ g_clear_object (&priv->poll_ostream);
+
+ G_OBJECT_CLASS (soup_http1_channel_parent_class)->dispose (object);
+}
+
+static void
+soup_http1_channel_finalize (GObject *object)
+{
+ SoupHTTP1ChannelPrivate *priv = SOUP_HTTP1_CHANNEL_GET_PRIVATE (object);
+
+ g_string_free (priv->input_headers, TRUE);
+ g_string_free (priv->output_headers, TRUE);
+
+ G_OBJECT_CLASS (soup_http1_channel_parent_class)->finalize (object);
+}
+
+static void
+soup_http1_channel_class_init (SoupHTTP1ChannelClass *http1_channel_class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (http1_channel_class);
+ SoupHTTPChannelClass *channel_class = SOUP_HTTP_CHANNEL_CLASS (http1_channel_class);
+
+ g_type_class_add_private (http1_channel_class, sizeof (SoupHTTP1ChannelPrivate));
+
+ object_class->dispose = soup_http1_channel_dispose;
+ object_class->finalize = soup_http1_channel_finalize;
+
+ channel_class->read_request_headers = soup_http1_channel_read_request_headers;
+ channel_class->read_response_headers = soup_http1_channel_read_response_headers;
+ channel_class->write_request_headers = soup_http1_channel_write_request_headers;
+ channel_class->write_response_headers = soup_http1_channel_write_response_headers;
+ channel_class->get_body_input_stream = soup_http1_channel_get_body_input_stream;
+ channel_class->get_body_output_stream = soup_http1_channel_get_body_output_stream;
+ channel_class->create_oneshot_source = soup_http1_channel_create_oneshot_source;
+}
+
+SoupHTTPChannel *
+soup_http1_channel_new_client (SoupMessage *msg)
+{
+ SoupHTTPChannel *channel;
+ SoupHTTP1ChannelPrivate *priv;
+ GIOStream *iostream;
+
+ channel = g_object_new (SOUP_TYPE_HTTP1_CHANNEL,
+ SOUP_HTTP_CHANNEL_MESSAGE, msg,
+ SOUP_HTTP_CHANNEL_MODE, SOUP_HTTP_CHANNEL_CLIENT,
+ NULL);
+ priv = SOUP_HTTP1_CHANNEL_GET_PRIVATE (channel);
+
+ iostream = soup_socket_get_iostream (soup_connection_get_socket (soup_message_get_connection (msg)));
+ priv->istream = g_object_ref (g_io_stream_get_input_stream (iostream));
+ priv->poll_istream = g_object_ref (priv->istream);
+ priv->ostream = g_object_ref (g_io_stream_get_output_stream (iostream));
+ priv->poll_ostream = g_object_ref (priv->ostream);
+
+ return channel;
+}
+
+SoupHTTPChannel *
+soup_http1_channel_new_server (SoupMessage *msg,
+ SoupSocket *sock)
+{
+ SoupHTTPChannel *channel;
+ SoupHTTP1ChannelPrivate *priv;
+ GIOStream *iostream;
+
+ channel = g_object_new (SOUP_TYPE_HTTP1_CHANNEL,
+ SOUP_HTTP_CHANNEL_MESSAGE, msg,
+ SOUP_HTTP_CHANNEL_MODE, SOUP_HTTP_CHANNEL_SERVER,
+ NULL);
+ priv = SOUP_HTTP1_CHANNEL_GET_PRIVATE (channel);
+
+ priv->server_sock = g_object_ref (sock);
+
+ iostream = soup_socket_get_iostream (sock);
+ priv->istream = g_object_ref (g_io_stream_get_input_stream (iostream));
+ priv->poll_istream = g_object_ref (priv->istream);
+ priv->ostream = g_object_ref (g_io_stream_get_output_stream (iostream));
+ priv->poll_ostream = g_object_ref (priv->ostream);
+
+ return channel;
+}
diff --git a/libsoup/soup-http1-channel.h b/libsoup/soup-http1-channel.h
new file mode 100644
index 0000000..d26ad54
--- /dev/null
+++ b/libsoup/soup-http1-channel.h
@@ -0,0 +1,35 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright 2014 Red Hat, Inc.
+ */
+
+#ifndef SOUP_HTTP1_CHANNEL_H
+#define SOUP_HTTP1_CHANNEL_H 1
+
+#include "soup-http-channel.h"
+#include "soup-connection.h"
+
+#define SOUP_TYPE_HTTP1_CHANNEL (soup_http1_channel_get_type ())
+#define SOUP_HTTP1_CHANNEL(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj), SOUP_TYPE_HTTP1_CHANNEL,
SoupHTTP1Channel))
+#define SOUP_HTTP1_CHANNEL_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass), SOUP_TYPE_HTTP1_CHANNEL,
SoupHTTP1ChannelClass))
+#define SOUP_IS_HTTP1_CHANNEL(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj), SOUP_TYPE_HTTP1_CHANNEL))
+#define SOUP_IS_HTTP1_CHANNEL_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), SOUP_TYPE_HTTP1_CHANNEL))
+#define SOUP_HTTP1_CHANNEL_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj), SOUP_TYPE_HTTP1_CHANNEL,
SoupHTTP1ChannelClass))
+
+typedef struct {
+ SoupHTTPChannel parent;
+
+} SoupHTTP1Channel;
+
+typedef struct {
+ SoupHTTPChannelClass parent_class;
+
+} SoupHTTP1ChannelClass;
+
+GType soup_http1_channel_get_type (void);
+
+SoupHTTPChannel *soup_http1_channel_new_client (SoupMessage *msg);
+SoupHTTPChannel *soup_http1_channel_new_server (SoupMessage *msg,
+ SoupSocket *socket);
+
+#endif /* SOUP_HTTP1_CHANNEL_H */
diff --git a/libsoup/soup-message-io.c b/libsoup/soup-message-io.c
index abe909f..834bd0f 100644
--- a/libsoup/soup-message-io.c
+++ b/libsoup/soup-message-io.c
@@ -19,6 +19,7 @@
#include "soup-content-processor.h"
#include "soup-content-sniffer-stream.h"
#include "soup-filter-input-stream.h"
+#include "soup-http1-channel.h"
#include "soup-message-private.h"
#include "soup-message-queue.h"
#include "soup-misc-private.h"
@@ -54,18 +55,14 @@ typedef struct {
SoupMessageIOMode mode;
GCancellable *cancellable;
- GIOStream *iostream;
- SoupFilterInputStream *istream;
- GInputStream *body_istream;
- GOutputStream *ostream;
- GOutputStream *body_ostream;
- GMainContext *async_context;
+ SoupHTTPChannel *channel;
+ GInputStream *body_istream;
+ GOutputStream *body_ostream;
+ GMainContext *async_context;
SoupMessageIOState read_state;
SoupEncoding read_encoding;
- GByteArray *read_header_buf;
SoupMessageBody *read_body;
- goffset read_length;
SoupMessageIOState write_state;
SoupEncoding write_encoding;
@@ -73,7 +70,6 @@ typedef struct {
SoupMessageBody *write_body;
SoupBuffer *write_chunk;
goffset write_body_offset;
- goffset write_length;
goffset written;
GSource *io_source;
@@ -83,9 +79,6 @@ typedef struct {
GCancellable *async_close_wait;
GError *async_close_error;
- SoupMessageGetHeadersFn get_headers_cb;
- SoupMessageParseHeadersFn parse_headers_cb;
- gpointer header_data;
SoupMessageCompletionFn completion_cb;
gpointer completion_data;
} SoupMessageIOData;
@@ -107,22 +100,14 @@ soup_message_io_cleanup (SoupMessage *msg)
return;
priv->io_data = NULL;
- if (io->iostream)
- g_object_unref (io->iostream);
- if (io->body_istream)
- g_object_unref (io->body_istream);
- if (io->body_ostream)
- g_object_unref (io->body_ostream);
- if (io->async_context)
- g_main_context_unref (io->async_context);
- if (io->item)
- soup_message_queue_item_unref (io->item);
-
- g_byte_array_free (io->read_header_buf, TRUE);
+ g_clear_object (&io->channel);
+ g_clear_object (&io->body_istream);
+ g_clear_object (&io->body_ostream);
+ g_clear_pointer (&io->async_context, g_main_context_unref);
+ g_clear_pointer (&io->item, soup_message_queue_item_unref);
g_string_free (io->write_buf, TRUE);
- if (io->write_chunk)
- soup_buffer_free (io->write_chunk);
+ g_clear_pointer (&io->write_chunk, soup_buffer_free);
g_slice_free (SoupMessageIOData, io);
}
@@ -174,62 +159,6 @@ soup_message_io_finished (SoupMessage *msg)
g_object_unref (msg);
}
-static gboolean
-read_headers (SoupMessage *msg, gboolean blocking,
- GCancellable *cancellable, GError **error)
-{
- SoupMessagePrivate *priv = SOUP_MESSAGE_GET_PRIVATE (msg);
- SoupMessageIOData *io = priv->io_data;
- gssize nread, old_len;
- gboolean got_lf;
-
- while (1) {
- old_len = io->read_header_buf->len;
- g_byte_array_set_size (io->read_header_buf, old_len + RESPONSE_BLOCK_SIZE);
- nread = soup_filter_input_stream_read_line (io->istream,
- io->read_header_buf->data + old_len,
- RESPONSE_BLOCK_SIZE,
- blocking,
- &got_lf,
- cancellable, error);
- io->read_header_buf->len = old_len + MAX (nread, 0);
- if (nread == 0) {
- soup_message_set_status (msg, SOUP_STATUS_MALFORMED);
- g_set_error_literal (error, G_IO_ERROR,
- G_IO_ERROR_PARTIAL_INPUT,
- _("Connection terminated unexpectedly"));
- }
- if (nread <= 0)
- return FALSE;
-
- if (got_lf) {
- if (nread == 1 && old_len >= 2 &&
- !strncmp ((char *)io->read_header_buf->data +
- io->read_header_buf->len - 2,
- "\n\n", 2))
- break;
- else if (nread == 2 && old_len >= 3 &&
- !strncmp ((char *)io->read_header_buf->data +
- io->read_header_buf->len - 3,
- "\n\r\n", 3))
- break;
- }
- }
-
- /* We need to "rewind" io->read_header_buf back one line.
- * That SHOULD be two characters (CR LF), but if the
- * web server was stupid, it might only be one.
- */
- if (io->read_header_buf->len < 3 ||
- io->read_header_buf->data[io->read_header_buf->len - 2] == '\n')
- io->read_header_buf->len--;
- else
- io->read_header_buf->len -= 2;
- io->read_header_buf->data[io->read_header_buf->len] = '\0';
-
- return TRUE;
-}
-
static gint
processing_stage_cmp (gconstpointer a,
gconstpointer b)
@@ -360,25 +289,11 @@ 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);
- }
-
- 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_channel_write_headers (io->channel, blocking,
+ cancellable, error))
+ return FALSE;
io->written = 0;
- g_string_truncate (io->write_buf, 0);
if (io->mode == SOUP_MESSAGE_IO_SERVER &&
SOUP_STATUS_IS_INFORMATIONAL (msg->status_code)) {
@@ -402,13 +317,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 */
@@ -431,17 +339,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_channel_get_body_output_stream (io->channel);
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;
}
@@ -470,9 +374,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;
@@ -556,20 +457,19 @@ io_read (SoupMessage *msg, gboolean blocking,
guchar *stack_buf = NULL;
gssize nread;
SoupBuffer *buffer;
- guint status;
+ GError *local = NULL;
switch (io->read_state) {
case SOUP_MESSAGE_IO_STATE_HEADERS:
- if (!read_headers (msg, blocking, cancellable, error))
- return FALSE;
+ soup_http_channel_read_headers (io->channel, blocking,
+ cancellable, &local);
- status = io->parse_headers_cb (msg, (char *)io->read_header_buf->data,
- io->read_header_buf->len,
- &io->read_encoding,
- io->header_data, error);
- g_byte_array_set_size (io->read_header_buf, 0);
+ if (local) {
+ if (local->domain != SOUP_HTTP_ERROR) {
+ g_propagate_error (error, local);
+ return FALSE;
+ }
- if (status != SOUP_STATUS_OK) {
/* Either we couldn't parse the headers, or they
* indicated something that would mean we wouldn't
* be able to parse the body. (Eg, unknown
@@ -577,7 +477,8 @@ io_read (SoupMessage *msg, gboolean blocking,
* reading, and make sure the connection gets
* closed when we're done.
*/
- soup_message_set_status (msg, status);
+ soup_message_set_status (msg, local->code);
+ g_clear_error (&local);
soup_message_headers_append (msg->request_headers,
"Connection", "close");
io->read_state = SOUP_MESSAGE_IO_STATE_FINISHING;
@@ -625,33 +526,13 @@ io_read (SoupMessage *msg, gboolean blocking,
io->write_state = SOUP_MESSAGE_IO_STATE_FINISHING;
}
- if (io->read_encoding == SOUP_ENCODING_CONTENT_LENGTH) {
- SoupMessageHeaders *hdrs =
- (io->mode == SOUP_MESSAGE_IO_CLIENT) ?
- msg->response_headers : msg->request_headers;
- io->read_length = soup_message_headers_get_content_length (hdrs);
-
- if (io->mode == SOUP_MESSAGE_IO_CLIENT &&
- !soup_message_is_keepalive (msg)) {
- /* Some servers suck and send
- * incorrect Content-Length values, so
- * allow EOF termination in this case
- * (iff the message is too short) too.
- */
- io->read_encoding = SOUP_ENCODING_EOF;
- }
- } else
- io->read_length = -1;
-
soup_message_got_headers (msg);
break;
case SOUP_MESSAGE_IO_STATE_BODY_START:
if (!io->body_istream) {
- GInputStream *body_istream = soup_body_input_stream_new (G_INPUT_STREAM (io->istream),
- io->read_encoding,
- io->read_length);
+ GInputStream *body_istream = soup_http_channel_get_body_input_stream (io->channel);
/* TODO: server-side messages do not have a io->item. This means
* that we cannot use content processors for them right now.
@@ -685,7 +566,7 @@ io_read (SoupMessage *msg, gboolean blocking,
case SOUP_MESSAGE_IO_STATE_BODY:
if (priv->chunk_allocator) {
- buffer = priv->chunk_allocator (msg, io->read_length, priv->chunk_allocator_data);
+ buffer = priv->chunk_allocator (msg, 0 /* FIXME? */, priv->chunk_allocator_data);
if (!buffer) {
g_return_val_if_fail (!io->item || !io->item->new_api, FALSE);
soup_message_io_pause (msg);
@@ -839,29 +720,17 @@ soup_message_io_get_oneshot_source (SoupMessage *msg, GCancellable *cancellable,
GSource *base_source, *source;
SoupMessageSource *message_source;
- if (!io) {
+ if (!io)
base_source = g_timeout_source_new (0);
- } else if (io->paused) {
+ else if (io->paused)
base_source = NULL;
- } else if (io->async_close_wait) {
+ else if (io->async_close_wait)
base_source = g_cancellable_source_new (io->async_close_wait);
- } else if (SOUP_MESSAGE_IO_STATE_POLLABLE (io->read_state)) {
- GPollableInputStream *istream;
-
- if (io->body_istream)
- istream = G_POLLABLE_INPUT_STREAM (io->body_istream);
- else
- istream = G_POLLABLE_INPUT_STREAM (io->istream);
- base_source = g_pollable_input_stream_create_source (istream, cancellable);
- } else if (SOUP_MESSAGE_IO_STATE_POLLABLE (io->write_state)) {
- GPollableOutputStream *ostream;
-
- if (io->body_ostream)
- ostream = G_POLLABLE_OUTPUT_STREAM (io->body_ostream);
- else
- ostream = G_POLLABLE_OUTPUT_STREAM (io->ostream);
- base_source = g_pollable_output_stream_create_source (ostream, cancellable);
- } else
+ else if (SOUP_MESSAGE_IO_STATE_POLLABLE (io->read_state))
+ base_source = soup_http_channel_create_oneshot_source (io->channel, G_IO_IN, cancellable);
+ else if (SOUP_MESSAGE_IO_STATE_POLLABLE (io->write_state))
+ base_source = soup_http_channel_create_oneshot_source (io->channel, G_IO_OUT, cancellable);
+ else
base_source = g_timeout_source_new (0);
source = g_source_new (&message_source_funcs,
@@ -891,7 +760,7 @@ request_is_restartable (SoupMessage *msg, GError *error)
return (io->mode == SOUP_MESSAGE_IO_CLIENT &&
io->read_state <= SOUP_MESSAGE_IO_STATE_HEADERS &&
- io->read_header_buf->len == 0 &&
+ //FIXME io->read_header_buf->len == 0 &&
soup_connection_get_ever_used (io->item->conn) &&
!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_TIMED_OUT) &&
!g_error_matches (error, G_IO_ERROR, G_IO_ERROR_WOULD_BLOCK) &&
@@ -1106,11 +975,9 @@ soup_message_io_get_response_istream (SoupMessage *msg,
static SoupMessageIOData *
-new_iostate (SoupMessage *msg, GIOStream *iostream,
- GMainContext *async_context, SoupMessageIOMode mode,
- SoupMessageGetHeadersFn get_headers_cb,
- SoupMessageParseHeadersFn parse_headers_cb,
- gpointer header_data,
+new_iostate (SoupMessage *msg,
+ GMainContext *async_context,
+ SoupMessageIOMode mode,
SoupMessageCompletionFn completion_cb,
gpointer completion_data)
{
@@ -1119,21 +986,13 @@ new_iostate (SoupMessage *msg, GIOStream *iostream,
io = g_slice_new0 (SoupMessageIOData);
io->mode = mode;
- io->get_headers_cb = get_headers_cb;
- io->parse_headers_cb = parse_headers_cb;
- io->header_data = header_data;
io->completion_cb = completion_cb;
io->completion_data = completion_data;
- io->iostream = g_object_ref (iostream);
- io->istream = SOUP_FILTER_INPUT_STREAM (g_io_stream_get_input_stream (iostream));
- io->ostream = g_io_stream_get_output_stream (iostream);
-
if (async_context)
io->async_context = g_main_context_ref (async_context);
- io->read_header_buf = g_byte_array_new ();
- io->write_buf = g_string_new (NULL);
+ io->write_buf = g_string_new (NULL);
io->read_state = SOUP_MESSAGE_IO_STATE_NOT_STARTED;
io->write_state = SOUP_MESSAGE_IO_STATE_NOT_STARTED;
@@ -1146,22 +1005,19 @@ new_iostate (SoupMessage *msg, GIOStream *iostream,
void
soup_message_io_client (SoupMessageQueueItem *item,
- GIOStream *iostream,
GMainContext *async_context,
- SoupMessageGetHeadersFn get_headers_cb,
- SoupMessageParseHeadersFn parse_headers_cb,
- gpointer header_data,
SoupMessageCompletionFn completion_cb,
gpointer completion_data)
{
SoupMessageIOData *io;
- io = new_iostate (item->msg, iostream, async_context,
+ io = new_iostate (item->msg, async_context,
SOUP_MESSAGE_IO_CLIENT,
- get_headers_cb, parse_headers_cb, header_data,
completion_cb, completion_data);
+ io->channel = soup_http1_channel_new_client (item->msg);
io->item = item;
+
soup_message_queue_item_ref (item);
io->cancellable = item->cancellable;
@@ -1180,20 +1036,19 @@ soup_message_io_client (SoupMessageQueueItem *item,
void
soup_message_io_server (SoupMessage *msg,
- GIOStream *iostream, GMainContext *async_context,
- SoupMessageGetHeadersFn get_headers_cb,
- SoupMessageParseHeadersFn parse_headers_cb,
- gpointer header_data,
+ SoupSocket *sock,
+ GMainContext *async_context,
SoupMessageCompletionFn completion_cb,
gpointer completion_data)
{
SoupMessageIOData *io;
- io = new_iostate (msg, iostream, async_context,
+ io = new_iostate (msg, async_context,
SOUP_MESSAGE_IO_SERVER,
- get_headers_cb, parse_headers_cb, header_data,
completion_cb, completion_data);
+ io->channel = soup_http1_channel_new_server (msg, sock);
+
io->read_body = msg->request_body;
io->write_body = msg->response_body;
diff --git a/libsoup/soup-message-private.h b/libsoup/soup-message-private.h
index 9cb4b19..6416f59 100644
--- a/libsoup/soup-message-private.h
+++ b/libsoup/soup-message-private.h
@@ -10,6 +10,7 @@
#include "soup-auth.h"
#include "soup-content-processor.h"
#include "soup-content-sniffer.h"
+#include "soup-http-channel.h"
#include "soup-session.h"
typedef struct {
@@ -49,44 +50,17 @@ 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,
- char *headers,
- guint header_len,
- SoupEncoding *encoding,
- gpointer user_data,
- GError **error);
-typedef void (*SoupMessageCompletionFn) (SoupMessage *msg,
- gboolean io_complete,
- gpointer user_data);
-
-
-void soup_message_send_request (SoupMessageQueueItem *item,
- SoupMessageCompletionFn completion_cb,
- gpointer user_data);
-void soup_message_read_request (SoupMessage *msg,
- SoupSocket *sock,
- gboolean use_thread_context,
- SoupMessageCompletionFn completion_cb,
- gpointer user_data);
+typedef void (*SoupMessageCompletionFn) (SoupMessage *msg,
+ gboolean io_complete,
+ gpointer user_data);
void soup_message_io_client (SoupMessageQueueItem *item,
- GIOStream *iostream,
GMainContext *async_context,
- SoupMessageGetHeadersFn get_headers_cb,
- SoupMessageParseHeadersFn parse_headers_cb,
- gpointer headers_data,
SoupMessageCompletionFn completion_cb,
gpointer user_data);
void soup_message_io_server (SoupMessage *msg,
- GIOStream *iostream,
+ SoupSocket *sock,
GMainContext *async_context,
- SoupMessageGetHeadersFn get_headers_cb,
- SoupMessageParseHeadersFn parse_headers_cb,
- gpointer headers_data,
SoupMessageCompletionFn completion_cb,
gpointer user_data);
void soup_message_io_cleanup (SoupMessage *msg);
diff --git a/libsoup/soup-server.c b/libsoup/soup-server.c
index 30dc4f9..e1ceaba 100644
--- a/libsoup/soup-server.c
+++ b/libsoup/soup-server.c
@@ -1287,6 +1287,7 @@ start_request (SoupServer *server, SoupClientContext *client)
{
SoupServerPrivate *priv = SOUP_SERVER_GET_PRIVATE (server);
SoupMessage *msg;
+ GMainContext *async_context;
soup_client_context_cleanup (client);
@@ -1309,9 +1310,15 @@ start_request (SoupServer *server, SoupClientContext *client)
g_object_ref (client->sock);
- soup_message_read_request (msg, client->sock,
- priv->legacy_iface == NULL,
- request_finished, client);
+ if (priv->legacy_iface == NULL)
+ async_context = g_main_context_ref_thread_default ();
+ else if (priv->async_context)
+ async_context = priv->async_context;
+ else
+ async_context = g_main_context_default ();
+
+ soup_message_io_server (msg, client->sock, async_context,
+ request_finished, client);
}
static void
diff --git a/po/POTFILES.in b/po/POTFILES.in
index ece9e95..93d0e9e 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -1,9 +1,8 @@
libsoup/soup-body-input-stream.c
libsoup/soup-cache-input-stream.c
libsoup/soup-converter-wrapper.c
-libsoup/soup-message-client-io.c
+libsoup/soup-http1-channel.c
libsoup/soup-message-io.c
-libsoup/soup-message-server-io.c
libsoup/soup-request.c
libsoup/soup-server.c
libsoup/soup-session.c
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]