[libsoup/http2: 2/3] Add SoupHTTPInputStream, implement SoupHTTP1InputStream
- From: Dan Winship <danw src gnome org>
- To: commits-list gnome org
- Cc:
- Subject: [libsoup/http2: 2/3] Add SoupHTTPInputStream, implement SoupHTTP1InputStream
- Date: Mon, 9 Dec 2013 10:04:15 +0000 (UTC)
commit bc8afedef9352b33dd34b29a216dbb545192537f
Author: Dan Winship <danw gnome org>
Date: Sun Dec 8 14:48:52 2013 +0100
Add SoupHTTPInputStream, implement SoupHTTP1InputStream
Abstract out some HTTP/1.x-specific reading/parsing bits from
soup-message-io, etc, into a new HTTP/1.x-specific class.
libsoup/Makefile.am | 4 +
libsoup/soup-http-input-stream.c | 82 ++++++++++
libsoup/soup-http-input-stream.h | 82 ++++++++++
libsoup/soup-http1-input-stream.c | 319 +++++++++++++++++++++++++++++++++++++
libsoup/soup-http1-input-stream.h | 37 +++++
libsoup/soup-message-client-io.c | 38 +----
libsoup/soup-message-io.c | 103 ++-----------
libsoup/soup-message-private.h | 23 ++--
libsoup/soup-message-server-io.c | 85 ++---------
po/POTFILES.in | 1 +
10 files changed, 568 insertions(+), 206 deletions(-)
---
diff --git a/libsoup/Makefile.am b/libsoup/Makefile.am
index 3d3452e..90e0d83 100644
--- a/libsoup/Makefile.am
+++ b/libsoup/Makefile.am
@@ -139,6 +139,10 @@ libsoup_2_4_la_SOURCES = \
soup-filter-input-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-logger.c \
soup-message.c \
soup-message-body.c \
diff --git a/libsoup/soup-http-input-stream.c b/libsoup/soup-http-input-stream.c
new file mode 100644
index 0000000..8d2df64
--- /dev/null
+++ b/libsoup/soup-http-input-stream.c
@@ -0,0 +1,82 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * soup-http-input-stream.c
+ *
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+#ifdef HAVE_CONFIG_H
+#include <config.h>
+#endif
+
+#include "soup-http-input-stream.h"
+#include "soup.h"
+#include "soup-filter-input-stream.h"
+
+G_DEFINE_ABSTRACT_TYPE (SoupHTTPInputStream, soup_http_input_stream, SOUP_TYPE_FILTER_INPUT_STREAM)
+
+static void
+soup_http_input_stream_init (SoupHTTPInputStream *http)
+{
+}
+
+static void
+soup_http_input_stream_class_init (SoupHTTPInputStreamClass *http_class)
+{
+}
+
+gboolean
+soup_http_input_stream_read_headers (SoupHTTPInputStream *http,
+ gboolean blocking,
+ GCancellable *cancellable,
+ GError **error)
+{
+ return SOUP_HTTP_INPUT_STREAM_GET_CLASS (http)->
+ read_headers (http, blocking, cancellable, error);
+}
+
+guint
+soup_http_input_stream_parse_request_headers (SoupHTTPInputStream *http,
+ SoupSocket *sock,
+ char **method,
+ SoupURI **request_uri,
+ SoupHTTPVersion *version,
+ SoupMessageHeaders *headers,
+ GError **error)
+{
+ return SOUP_HTTP_INPUT_STREAM_GET_CLASS (http)->
+ parse_request_headers (http, sock,
+ method, request_uri, version,
+ headers,
+ error);
+}
+
+gboolean
+soup_http_input_stream_parse_response_headers (SoupHTTPInputStream *http,
+ const char *request_method,
+ SoupHTTPVersion *version,
+ guint *status_code,
+ char **reason_phrase,
+ SoupMessageHeaders *headers,
+ GError **error)
+{
+ return SOUP_HTTP_INPUT_STREAM_GET_CLASS (http)->
+ parse_response_headers (http, request_method,
+ version, status_code, reason_phrase,
+ headers,
+ error);
+}
+
+gboolean
+soup_http_input_stream_failed_immediately (SoupHTTPInputStream *http)
+{
+ return SOUP_HTTP_INPUT_STREAM_GET_CLASS (http)->
+ failed_immediately (http);
+}
+
+GInputStream *
+soup_http_input_stream_get_body_stream (SoupHTTPInputStream *http)
+{
+ return SOUP_HTTP_INPUT_STREAM_GET_CLASS (http)->
+ get_body_stream (http);
+}
diff --git a/libsoup/soup-http-input-stream.h b/libsoup/soup-http-input-stream.h
new file mode 100644
index 0000000..8d1c1ab
--- /dev/null
+++ b/libsoup/soup-http-input-stream.h
@@ -0,0 +1,82 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * Copyright 2013 Red Hat, Inc.
+ */
+
+#ifndef SOUP_HTTP_INPUT_STREAM_H
+#define SOUP_HTTP_INPUT_STREAM_H 1
+
+#include "soup-types.h"
+#include "soup-filter-input-stream.h"
+#include "soup-message.h"
+
+G_BEGIN_DECLS
+
+#define SOUP_TYPE_HTTP_INPUT_STREAM (soup_http_input_stream_get_type ())
+#define SOUP_HTTP_INPUT_STREAM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj),
SOUP_TYPE_HTTP_INPUT_STREAM, SoupHTTPInputStream))
+#define SOUP_HTTP_INPUT_STREAM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass),
SOUP_TYPE_HTTP_INPUT_STREAM, SoupHTTPInputStreamClass))
+#define SOUP_IS_HTTP_INPUT_STREAM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj),
SOUP_TYPE_HTTP_INPUT_STREAM))
+#define SOUP_IS_HTTP_INPUT_STREAM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj), SOUP_TYPE_HTTP_INPUT_STREAM))
+#define SOUP_HTTP_INPUT_STREAM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj),
SOUP_TYPE_HTTP_INPUT_STREAM, SoupHTTPInputStreamClass))
+
+typedef struct {
+ SoupFilterInputStream parent;
+
+} SoupHTTPInputStream;
+
+typedef struct {
+ SoupFilterInputStreamClass parent_class;
+
+ gboolean (*read_headers) (SoupHTTPInputStream *http,
+ gboolean blocking,
+ GCancellable *cancellable,
+ GError **error);
+ guint (*parse_request_headers) (SoupHTTPInputStream *http,
+ SoupSocket *sock,
+ char **method,
+ SoupURI **request_uri,
+ SoupHTTPVersion *version,
+ SoupMessageHeaders *headers,
+ GError **error);
+ gboolean (*parse_response_headers) (SoupHTTPInputStream *http,
+ const char *request_method,
+ SoupHTTPVersion *version,
+ guint *status_code,
+ char **reason_phrase,
+ SoupMessageHeaders *headers,
+ GError **error);
+
+ gboolean (*failed_immediately) (SoupHTTPInputStream *http);
+
+ GInputStream *(*get_body_stream) (SoupHTTPInputStream *http);
+
+} SoupHTTPInputStreamClass;
+
+GType soup_http_input_stream_get_type (void);
+
+gboolean soup_http_input_stream_read_headers (SoupHTTPInputStream *http,
+ gboolean blocking,
+ GCancellable *cancellable,
+ GError **error);
+guint soup_http_input_stream_parse_request_headers (SoupHTTPInputStream *http,
+ SoupSocket *sock,
+ char **method,
+ SoupURI **request_uri,
+ SoupHTTPVersion *version,
+ SoupMessageHeaders *headers,
+ GError **error);
+gboolean soup_http_input_stream_parse_response_headers (SoupHTTPInputStream *http,
+ const char *request_method,
+ SoupHTTPVersion *version,
+ guint *status_code,
+ char **reason_phrase,
+ SoupMessageHeaders *headers,
+ GError **error);
+
+gboolean soup_http_input_stream_failed_immediately (SoupHTTPInputStream *http);
+
+GInputStream *soup_http_input_stream_get_body_stream (SoupHTTPInputStream *http);
+
+G_END_DECLS
+
+#endif /* SOUP_HTTP_INPUT_STREAM_H */
diff --git a/libsoup/soup-http1-input-stream.c b/libsoup/soup-http1-input-stream.c
new file mode 100644
index 0000000..87a3e1d
--- /dev/null
+++ b/libsoup/soup-http1-input-stream.c
@@ -0,0 +1,319 @@
+/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */
+/*
+ * soup-http1-input-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-input-stream.h"
+#include "soup.h"
+#include "soup-body-input-stream.h"
+
+G_DEFINE_TYPE (SoupHTTP1InputStream, soup_http1_input_stream, SOUP_TYPE_HTTP_INPUT_STREAM)
+
+typedef struct {
+ GByteArray *header_buf;
+ gboolean headers_complete;
+
+ SoupEncoding encoding;
+ goffset content_length;
+} SoupHTTP1InputStreamPrivate;
+#define SOUP_HTTP1_INPUT_STREAM_GET_PRIVATE(o) (G_TYPE_INSTANCE_GET_PRIVATE ((o),
SOUP_TYPE_HTTP1_INPUT_STREAM, SoupHTTP1InputStreamPrivate))
+
+static void
+soup_http1_input_stream_init (SoupHTTP1InputStream *http1)
+{
+ SoupHTTP1InputStreamPrivate *priv = SOUP_HTTP1_INPUT_STREAM_GET_PRIVATE (http1);
+
+ priv->header_buf = g_byte_array_new ();
+ priv->encoding = SOUP_ENCODING_NONE;
+ priv->content_length = -1;
+}
+
+static void
+soup_http1_input_stream_finalize (GObject *object)
+{
+ SoupHTTP1InputStreamPrivate *priv = SOUP_HTTP1_INPUT_STREAM_GET_PRIVATE (object);
+
+ g_byte_array_unref (priv->header_buf);
+
+ G_OBJECT_CLASS (soup_http1_input_stream_parent_class)->finalize (object);
+}
+
+#define READ_BUFFER_SIZE 8192
+
+static gboolean
+soup_http1_input_stream_read_headers (SoupHTTPInputStream *http,
+ gboolean blocking,
+ GCancellable *cancellable,
+ GError **error)
+{
+ SoupHTTP1InputStreamPrivate *priv = SOUP_HTTP1_INPUT_STREAM_GET_PRIVATE (http);
+ SoupFilterInputStream *istream = SOUP_FILTER_INPUT_STREAM (G_FILTER_INPUT_STREAM (http)->base_stream);
+ gssize nread, old_len;
+ gboolean got_lf;
+
+ if (priv->headers_complete) {
+ /* restart */
+ g_byte_array_set_size (priv->header_buf, 0);
+ priv->headers_complete = FALSE;
+ }
+
+ while (1) {
+ old_len = priv->header_buf->len;
+ g_byte_array_set_size (priv->header_buf, old_len + READ_BUFFER_SIZE);
+ nread = soup_filter_input_stream_read_line (istream,
+ priv->header_buf->data + old_len,
+ READ_BUFFER_SIZE,
+ blocking,
+ &got_lf,
+ cancellable, error);
+ priv->header_buf->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 ((char *)priv->header_buf->data +
+ priv->header_buf->len - 2,
+ "\n\n", 2))
+ break;
+ else if (nread == 2 && old_len >= 3 &&
+ !strncmp ((char *)priv->header_buf->data +
+ priv->header_buf->len - 3,
+ "\n\r\n", 3))
+ break;
+ }
+ }
+
+ /* We need to "rewind" priv->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 (priv->header_buf->len < 3 ||
+ priv->header_buf->data[priv->header_buf->len - 2] == '\n')
+ priv->header_buf->len--;
+ else
+ priv->header_buf->len -= 2;
+ priv->header_buf->data[priv->header_buf->len] = '\0';
+
+ priv->headers_complete = TRUE;
+ return TRUE;
+}
+
+static guint
+soup_http1_input_stream_parse_request_headers (SoupHTTPInputStream *http,
+ SoupSocket *sock,
+ char **method,
+ SoupURI **request_uri,
+ SoupHTTPVersion *version,
+ SoupMessageHeaders *headers,
+ GError **error)
+{
+ SoupHTTP1InputStreamPrivate *priv = SOUP_HTTP1_INPUT_STREAM_GET_PRIVATE (http);
+ guint status;
+ char *req_method, *req_path, *uri_string;
+ const char *req_host;
+ SoupURI *uri;
+
+ status = soup_headers_parse_request ((const char *)priv->header_buf->data,
+ priv->header_buf->len,
+ headers,
+ &req_method, &req_path, version);
+ if (status != SOUP_STATUS_OK) {
+ failed:
+ g_set_error_literal (error, SOUP_REQUEST_ERROR,
+ SOUP_REQUEST_ERROR_PARSING,
+ _("Could not parse HTTP request"));
+ return status;
+ }
+
+ /* Handle request body encoding */
+ priv->encoding = soup_message_headers_get_encoding (headers);
+ if (priv->encoding == SOUP_ENCODING_UNRECOGNIZED) {
+ g_free (req_method);
+ g_free (req_path);
+ if (soup_message_headers_get_list (headers, "Transfer-Encoding"))
+ status = SOUP_STATUS_NOT_IMPLEMENTED;
+ else
+ status = SOUP_STATUS_BAD_REQUEST;
+ goto failed;
+ }
+ if (priv->encoding == SOUP_ENCODING_CONTENT_LENGTH)
+ priv->content_length = soup_message_headers_get_content_length (headers);
+ else
+ priv->content_length = -1;
+
+ /* Generate correct context for request */
+ req_host = soup_message_headers_get_one (headers, "Host");
+ if (req_host && strchr (req_host, '/')) {
+ g_free (req_method);
+ 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 (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 (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 (sock);
+
+ uri = soup_uri_new (NULL);
+ soup_uri_set_scheme (uri, soup_socket_is_ssl (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) {
+ g_free (req_method);
+ if (uri)
+ soup_uri_free (uri);
+ status = SOUP_STATUS_BAD_REQUEST;
+ goto failed;
+ }
+
+ *method = req_method;
+ *request_uri = uri;
+ return SOUP_STATUS_OK;
+}
+
+static gboolean
+soup_http1_input_stream_parse_response_headers (SoupHTTPInputStream *http,
+ const char *request_method,
+ SoupHTTPVersion *version,
+ guint *status_code,
+ char **reason_phrase,
+ SoupMessageHeaders *headers,
+ GError **error)
+{
+ SoupHTTP1InputStreamPrivate *priv = SOUP_HTTP1_INPUT_STREAM_GET_PRIVATE (http);
+
+ if (!soup_headers_parse_response ((const char *)priv->header_buf->data,
+ priv->header_buf->len,
+ headers, version, status_code, reason_phrase)) {
+ g_set_error_literal (error, SOUP_REQUEST_ERROR,
+ SOUP_REQUEST_ERROR_PARSING,
+ _("Could not parse HTTP response"));
+ return FALSE;
+ }
+
+ 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 = soup_message_headers_get_encoding (headers);
+
+ if (priv->encoding == SOUP_ENCODING_UNRECOGNIZED) {
+ g_set_error_literal (error, SOUP_REQUEST_ERROR,
+ SOUP_REQUEST_ERROR_ENCODING,
+ _("Unrecognized HTTP encoding"));
+ return FALSE;
+ }
+ }
+
+ if (priv->encoding == SOUP_ENCODING_CONTENT_LENGTH) {
+ const char *conn;
+
+ priv->content_length = soup_message_headers_get_content_length (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 (headers, "Connection");
+ if (*version == SOUP_HTTP_1_0 &&
+ (!conn || !soup_header_contains (conn, "Keep-Alive")))
+ priv->encoding = SOUP_ENCODING_EOF;
+ else if (*version == SOUP_HTTP_1_1 && conn &&
+ soup_header_contains (conn, "close"))
+ priv->encoding = SOUP_ENCODING_EOF;
+ } else
+ priv->content_length = -1;
+
+ return TRUE;
+}
+
+static gboolean
+soup_http1_input_stream_failed_immediately (SoupHTTPInputStream *http)
+{
+ SoupHTTP1InputStreamPrivate *priv = SOUP_HTTP1_INPUT_STREAM_GET_PRIVATE (http);
+
+ return priv->header_buf->len == 0;
+}
+
+static GInputStream *
+soup_http1_input_stream_get_body_stream (SoupHTTPInputStream *http)
+{
+ SoupHTTP1InputStreamPrivate *priv = SOUP_HTTP1_INPUT_STREAM_GET_PRIVATE (http);
+
+ return soup_body_input_stream_new (G_FILTER_INPUT_STREAM (http)->base_stream,
+ priv->encoding,
+ priv->content_length);
+}
+
+static void
+soup_http1_input_stream_class_init (SoupHTTP1InputStreamClass *http1_class)
+{
+ GObjectClass *object_class = G_OBJECT_CLASS (http1_class);
+ SoupHTTPInputStreamClass *http_class = SOUP_HTTP_INPUT_STREAM_CLASS (http1_class);
+
+ g_type_class_add_private (http1_class, sizeof (SoupHTTP1InputStreamPrivate));
+
+ object_class->finalize = soup_http1_input_stream_finalize;
+
+ http_class->read_headers = soup_http1_input_stream_read_headers;
+ http_class->parse_request_headers = soup_http1_input_stream_parse_request_headers;
+ http_class->parse_response_headers = soup_http1_input_stream_parse_response_headers;
+ http_class->failed_immediately = soup_http1_input_stream_failed_immediately;
+ http_class->get_body_stream = soup_http1_input_stream_get_body_stream;
+}
+
+GInputStream *
+soup_http1_input_stream_new (GInputStream *base_stream)
+{
+ g_return_val_if_fail (SOUP_IS_FILTER_INPUT_STREAM (base_stream), NULL);
+
+ return g_object_new (SOUP_TYPE_HTTP1_INPUT_STREAM,
+ "base-stream", base_stream,
+ "close-base-stream", FALSE,
+ NULL);
+}
diff --git a/libsoup/soup-http1-input-stream.h b/libsoup/soup-http1-input-stream.h
new file mode 100644
index 0000000..6fdc149
--- /dev/null
+++ b/libsoup/soup-http1-input-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_INPUT_STREAM_H
+#define SOUP_HTTP1_INPUT_STREAM_H 1
+
+#include "soup-types.h"
+#include "soup-http-input-stream.h"
+
+G_BEGIN_DECLS
+
+#define SOUP_TYPE_HTTP1_INPUT_STREAM (soup_http1_input_stream_get_type ())
+#define SOUP_HTTP1_INPUT_STREAM(obj) (G_TYPE_CHECK_INSTANCE_CAST ((obj),
SOUP_TYPE_HTTP1_INPUT_STREAM, SoupHTTP1InputStream))
+#define SOUP_HTTP1_INPUT_STREAM_CLASS(klass) (G_TYPE_CHECK_CLASS_CAST ((klass),
SOUP_TYPE_HTTP1_INPUT_STREAM, SoupHTTP1InputStreamClass))
+#define SOUP_IS_HTTP1_INPUT_STREAM(obj) (G_TYPE_CHECK_INSTANCE_TYPE ((obj),
SOUP_TYPE_HTTP1_INPUT_STREAM))
+#define SOUP_IS_HTTP1_INPUT_STREAM_CLASS(klass) (G_TYPE_CHECK_CLASS_TYPE ((obj),
SOUP_TYPE_HTTP1_INPUT_STREAM))
+#define SOUP_HTTP1_INPUT_STREAM_GET_CLASS(obj) (G_TYPE_INSTANCE_GET_CLASS ((obj),
SOUP_TYPE_HTTP1_INPUT_STREAM, SoupHTTP1InputStreamClass))
+
+typedef struct {
+ SoupHTTPInputStream parent;
+
+} SoupHTTP1InputStream;
+
+typedef struct {
+ SoupHTTPInputStreamClass parent_class;
+
+} SoupHTTP1InputStreamClass;
+
+GType soup_http1_input_stream_get_type (void);
+
+GInputStream *soup_http1_input_stream_new (GInputStream *base_stream);
+
+G_END_DECLS
+
+#endif /* SOUP_HTTP1_INPUT_STREAM_H */
diff --git a/libsoup/soup-message-client-io.c b/libsoup/soup-message-client-io.c
index b145bba..2382225 100644
--- a/libsoup/soup-message-client-io.c
+++ b/libsoup/soup-message-client-io.c
@@ -21,26 +21,23 @@
static guint
parse_response_headers (SoupMessage *msg,
- char *headers, guint headers_len,
- SoupEncoding *encoding,
+ SoupHTTPInputStream *http,
gpointer user_data,
GError **error)
{
SoupMessagePrivate *priv = SOUP_MESSAGE_GET_PRIVATE (msg);
SoupHTTPVersion version;
- g_free(msg->reason_phrase);
+ g_free (msg->reason_phrase);
msg->reason_phrase = NULL;
- if (!soup_headers_parse_response (headers, headers_len,
- msg->response_headers,
- &version,
- &msg->status_code,
- &msg->reason_phrase)) {
- g_set_error_literal (error, SOUP_REQUEST_ERROR,
- SOUP_REQUEST_ERROR_PARSING,
- _("Could not parse HTTP response"));
+ if (!soup_http_input_stream_parse_response_headers (http,
+ msg->method,
+ &version,
+ &msg->status_code,
+ &msg->reason_phrase,
+ msg->response_headers,
+ error))
return SOUP_STATUS_MALFORMED;
- }
g_object_notify (G_OBJECT (msg), SOUP_MESSAGE_STATUS_CODE);
g_object_notify (G_OBJECT (msg), SOUP_MESSAGE_REASON_PHRASE);
@@ -50,23 +47,6 @@ parse_response_headers (SoupMessage *msg,
g_object_notify (G_OBJECT (msg), SOUP_MESSAGE_HTTP_VERSION);
}
- 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 = soup_message_headers_get_encoding (msg->response_headers);
-
- if (*encoding == SOUP_ENCODING_UNRECOGNIZED) {
- g_set_error_literal (error, SOUP_REQUEST_ERROR,
- SOUP_REQUEST_ERROR_ENCODING,
- _("Unrecognized HTTP response encoding"));
- return SOUP_STATUS_MALFORMED;
- }
-
return SOUP_STATUS_OK;
}
diff --git a/libsoup/soup-message-io.c b/libsoup/soup-message-io.c
index be5cb2d..fbbac0d 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-input-stream.h"
#include "soup-message-private.h"
#include "soup-message-queue.h"
#include "soup-misc-private.h"
@@ -55,7 +56,7 @@ typedef struct {
GCancellable *cancellable;
GIOStream *iostream;
- SoupFilterInputStream *istream;
+ SoupHTTPInputStream *istream;
GInputStream *body_istream;
GOutputStream *ostream;
GOutputStream *body_ostream;
@@ -63,9 +64,7 @@ typedef struct {
SoupMessageIOState read_state;
SoupEncoding read_encoding;
- GByteArray *read_header_buf;
SoupMessageBody *read_body;
- goffset read_length;
SoupMessageIOState write_state;
SoupEncoding write_encoding;
@@ -105,6 +104,8 @@ soup_message_io_cleanup (SoupMessage *msg)
if (io->iostream)
g_object_unref (io->iostream);
+ if (io->istream)
+ g_object_unref (io->istream);
if (io->body_istream)
g_object_unref (io->body_istream);
if (io->body_ostream)
@@ -114,8 +115,6 @@ soup_message_io_cleanup (SoupMessage *msg)
if (io->item)
soup_message_queue_item_unref (io->item);
- g_byte_array_free (io->read_header_buf, TRUE);
-
g_string_free (io->write_buf, TRUE);
if (io->write_chunk)
soup_buffer_free (io->write_chunk);
@@ -171,62 +170,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)
@@ -505,14 +448,11 @@ io_read (SoupMessage *msg, gboolean blocking,
switch (io->read_state) {
case SOUP_MESSAGE_IO_STATE_HEADERS:
- if (!read_headers (msg, blocking, cancellable, error))
+ if (!soup_http_input_stream_read_headers (io->istream, blocking,
+ cancellable, error))
return FALSE;
- 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);
+ status = io->parse_headers_cb (msg, io->istream, io->header_data, error);
if (status != SOUP_STATUS_OK) {
/* Either we couldn't parse the headers, or they
@@ -570,33 +510,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_input_stream_get_body_stream (io->istream);
/* TODO: server-side messages do not have a io->item. This means
* that we cannot use content processors for them right now.
@@ -630,7 +550,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);
@@ -829,7 +749,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 &&
+ soup_http_input_stream_failed_immediately (io->istream) &&
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) &&
@@ -1066,13 +986,12 @@ new_iostate (SoupMessage *msg, GIOStream *iostream,
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->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);
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->read_state = SOUP_MESSAGE_IO_STATE_NOT_STARTED;
diff --git a/libsoup/soup-message-private.h b/libsoup/soup-message-private.h
index 35cc988..b4b86a9 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-input-stream.h"
#include "soup-session.h"
typedef struct {
@@ -49,18 +50,16 @@ 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,
- gpointer user_data);
+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);
void soup_message_send_request (SoupMessageQueueItem *item,
diff --git a/libsoup/soup-message-server-io.c b/libsoup/soup-message-server-io.c
index 2647b81..01a4fd2 100644
--- a/libsoup/soup-message-server-io.c
+++ b/libsoup/soup-message-server-io.c
@@ -18,29 +18,24 @@
#include "soup-misc-private.h"
static guint
-parse_request_headers (SoupMessage *msg, char *headers, guint headers_len,
- SoupEncoding *encoding, gpointer sock, GError **error)
+parse_request_headers (SoupMessage *msg,
+ SoupHTTPInputStream *http,
+ gpointer sock,
+ GError **error)
{
- SoupMessagePrivate *priv = SOUP_MESSAGE_GET_PRIVATE (msg);
- char *req_method, *req_path, *url;
+ char *req_method;
SoupHTTPVersion version;
- const char *req_host;
guint status;
SoupURI *uri;
- status = soup_headers_parse_request (headers, headers_len,
- msg->request_headers,
- &req_method,
- &req_path,
- &version);
- if (!SOUP_STATUS_IS_SUCCESSFUL (status)) {
- if (status == SOUP_STATUS_MALFORMED) {
- g_set_error_literal (error, SOUP_REQUEST_ERROR,
- SOUP_REQUEST_ERROR_PARSING,
- _("Could not parse HTTP request"));
- }
+ status = soup_http_input_stream_parse_request_headers (http, sock,
+ &req_method,
+ &uri,
+ &version,
+ msg->request_headers,
+ error);
+ if (!SOUP_STATUS_IS_SUCCESSFUL (status))
return status;
- }
g_object_set (G_OBJECT (msg),
SOUP_MESSAGE_METHOD, req_method,
@@ -48,62 +43,6 @@ parse_request_headers (SoupMessage *msg, char *headers, guint headers_len,
NULL);
g_free (req_method);
- /* Handle request body encoding */
- *encoding = soup_message_headers_get_encoding (msg->request_headers);
- if (*encoding == SOUP_ENCODING_UNRECOGNIZED) {
- if (soup_message_headers_get_list (msg->request_headers, "Transfer-Encoding"))
- return SOUP_STATUS_NOT_IMPLEMENTED;
- else
- return SOUP_STATUS_BAD_REQUEST;
- }
-
- /* 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);
- return SOUP_STATUS_BAD_REQUEST;
- }
-
- if (!strcmp (req_path, "*") && req_host) {
- /* Eg, "OPTIONS * HTTP/1.1" */
- url = g_strdup_printf ("%s://%s",
- soup_socket_is_ssl (sock) ? "https" : "http",
- req_host);
- uri = soup_uri_new (url);
- if (uri)
- soup_uri_set_path (uri, "*");
- g_free (url);
- } else if (*req_path != '/') {
- /* Must be an absolute URI */
- uri = soup_uri_new (req_path);
- } else if (req_host) {
- url = g_strdup_printf ("%s://%s%s",
- soup_socket_is_ssl (sock) ? "https" : "http",
- req_host, req_path);
- uri = soup_uri_new (url);
- g_free (url);
- } else if (priv->http_version == SOUP_HTTP_1_0) {
- /* No Host header, no AbsoluteUri */
- SoupAddress *addr = soup_socket_get_local_address (sock);
-
- uri = soup_uri_new (NULL);
- soup_uri_set_scheme (uri, soup_socket_is_ssl (sock) ?
- SOUP_URI_SCHEME_HTTPS :
- SOUP_URI_SCHEME_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);
- return SOUP_STATUS_BAD_REQUEST;
- }
-
soup_message_set_uri (msg, uri);
soup_uri_free (uri);
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 21c70d4..9d2d5eb 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -1,6 +1,7 @@
libsoup/soup-body-input-stream.c
libsoup/soup-cache-input-stream.c
libsoup/soup-converter-wrapper.c
+libsoup/soup-http1-input-stream.c
libsoup/soup-message-client-io.c
libsoup/soup-message-io.c
libsoup/soup-message-server-io.c
[
Date Prev][
Date Next] [
Thread Prev][
Thread Next]
[
Thread Index]
[
Date Index]
[
Author Index]