[libsoup/wip/http2-a: 2/7] Add SoupHTTPInputStream, implement SoupHTTP1InputStream



commit b16307ca0d400544894b5ccebbc628e38a24ec38
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    |   25 ++--
 libsoup/soup-message-server-io.c  |   85 ++---------
 po/POTFILES.in                    |    1 +
 10 files changed, 569 insertions(+), 207 deletions(-)
---
diff --git a/libsoup/Makefile.am b/libsoup/Makefile.am
index 19dff18..032dbde 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 f571ef7..13cb104 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 db98dc2..bc00eff 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;
@@ -109,6 +108,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)
@@ -118,8 +119,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);
@@ -174,62 +173,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)
@@ -560,14 +503,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
@@ -625,33 +565,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.
@@ -685,7 +605,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);
@@ -886,7 +806,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) &&
@@ -1122,13 +1042,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 9860dee..190d453 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,19 +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);
+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,
+                                              gboolean              io_complete,
+                                              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 00272f3..c3d3904 100644
--- a/libsoup/soup-message-server-io.c
+++ b/libsoup/soup-message-server-io.c
@@ -19,29 +19,24 @@
 #include "soup-socket-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,
@@ -49,62 +44,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 ece9e95..63e4fc6 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]