summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaolo Borelli <pborelli@gnome.org>2015-02-28 11:05:02 +0100
committerPaolo Borelli <pborelli@gnome.org>2015-03-06 21:23:58 +0100
commited4a742946374f7ee3c46b93eb943c95f04ec4c4 (patch)
treed4400566f63065353e3c481c946d6edb904fe995
parent6fe28eef3ce5dd7ce12c6ace080bac5d9479f50c (diff)
downloadglib-ed4a742946374f7ee3c46b93eb943c95f04ec4c4.tar.gz
HTTP proxy support
Based on code from "WockyHttpProxy" written by Nicolas Dufresne and Marc-André Lureau. Initial glib patch by Brian J. Murrell. https://bugzilla.gnome.org/show_bug.cgi?id=733876
-rw-r--r--docs/reference/gio/Makefile.am1
-rw-r--r--gio/Makefile.am2
-rw-r--r--gio/ghttpproxy.c392
-rw-r--r--gio/ghttpproxy.h54
-rw-r--r--gio/giomodule.c3
5 files changed, 452 insertions, 0 deletions
diff --git a/docs/reference/gio/Makefile.am b/docs/reference/gio/Makefile.am
index ee1173255..d1e7735bd 100644
--- a/docs/reference/gio/Makefile.am
+++ b/docs/reference/gio/Makefile.am
@@ -50,6 +50,7 @@ IGNORE_HFILES = \
gdummytlsbackend.h \
gfileattribute-priv.h \
gfileinfo-priv.h \
+ ghttpproxy.h \
giomodule-priv.h \
gioprivate.h \
giowin32-priv.h \
diff --git a/gio/Makefile.am b/gio/Makefile.am
index bfe27c5fa..28aea41ae 100644
--- a/gio/Makefile.am
+++ b/gio/Makefile.am
@@ -183,6 +183,8 @@ application_sources = \
$(NULL)
local_sources = \
+ ghttpproxy.c \
+ ghttpproxy.h \
glocaldirectorymonitor.c \
glocaldirectorymonitor.h \
glocalfile.c \
diff --git a/gio/ghttpproxy.c b/gio/ghttpproxy.c
new file mode 100644
index 000000000..0bd11d38f
--- /dev/null
+++ b/gio/ghttpproxy.c
@@ -0,0 +1,392 @@
+/* GIO - GLib Input, Output and Streaming Library
+ *
+ * Copyright (C) 2010 Collabora, Ltd.
+ * Copyright (C) 2014 Red Hat, Inc.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * Author: Nicolas Dufresne <nicolas.dufresne@collabora.co.uk>
+ * Marc-André Lureau <marcandre.lureau@redhat.com>
+ */
+
+#include "config.h"
+
+#include "ghttpproxy.h"
+
+#include <string.h>
+#include <stdlib.h>
+
+#include "giomodule.h"
+#include "giomodule-priv.h"
+#include "giostream.h"
+#include "ginputstream.h"
+#include "glibintl.h"
+#include "goutputstream.h"
+#include "gproxy.h"
+#include "gproxyaddress.h"
+#include "gsocketconnectable.h"
+#include "gtask.h"
+#include "gtlsclientconnection.h"
+#include "gtlsconnection.h"
+
+
+struct _GHttpProxy
+{
+ GObject parent;
+};
+
+struct _GHttpProxyClass
+{
+ GObjectClass parent_class;
+};
+
+static void g_http_proxy_iface_init (GProxyInterface *proxy_iface);
+
+#define g_http_proxy_get_type _g_http_proxy_get_type
+G_DEFINE_TYPE_WITH_CODE (GHttpProxy, g_http_proxy, G_TYPE_OBJECT,
+ G_IMPLEMENT_INTERFACE (G_TYPE_PROXY,
+ g_http_proxy_iface_init)
+ _g_io_modules_ensure_extension_points_registered ();
+ g_io_extension_point_implement (G_PROXY_EXTENSION_POINT_NAME,
+ g_define_type_id,
+ "http",
+ 0))
+
+static void
+g_http_proxy_init (GHttpProxy *proxy)
+{
+}
+
+static gchar *
+create_request (GProxyAddress *proxy_address,
+ gboolean *has_cred)
+{
+ const gchar *hostname;
+ gint port;
+ const gchar *username;
+ const gchar *password;
+ GString *request;
+ gchar *ascii_hostname;
+
+ if (has_cred)
+ *has_cred = FALSE;
+
+ hostname = g_proxy_address_get_destination_hostname (proxy_address);
+ port = g_proxy_address_get_destination_port (proxy_address);
+ username = g_proxy_address_get_username (proxy_address);
+ password = g_proxy_address_get_password (proxy_address);
+
+ request = g_string_new (NULL);
+
+ ascii_hostname = g_hostname_to_ascii (hostname);
+ g_string_append_printf (request,
+ "CONNECT %s:%i HTTP/1.0\r\n"
+ "Host: %s:%i\r\n"
+ "Proxy-Connection: keep-alive\r\n"
+ "User-Agent: GLib/%i.%i\r\n",
+ ascii_hostname, port,
+ ascii_hostname, port,
+ GLIB_MAJOR_VERSION, GLIB_MINOR_VERSION);
+ g_free (ascii_hostname);
+
+ if (username != NULL && password != NULL)
+ {
+ gchar *cred;
+ gchar *base64_cred;
+
+ if (has_cred)
+ *has_cred = TRUE;
+
+ cred = g_strdup_printf ("%s:%s", username, password);
+ base64_cred = g_base64_encode ((guchar *) cred, strlen (cred));
+ g_free (cred);
+ g_string_append_printf (request,
+ "Proxy-Authorization: Basic %s\r\n",
+ base64_cred);
+ g_free (base64_cred);
+ }
+
+ g_string_append (request, "\r\n");
+
+ return g_string_free (request, FALSE);
+}
+
+static gboolean
+check_reply (const gchar *buffer,
+ gboolean has_cred,
+ GError **error)
+{
+ gint err_code;
+ const gchar *ptr = buffer + 7;
+
+ if (strncmp (buffer, "HTTP/1.", 7) != 0 || (*ptr != '0' && *ptr != '1'))
+ {
+ g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_PROXY_FAILED,
+ _("Bad HTTP proxy reply"));
+ return FALSE;
+ }
+
+ ptr++;
+ while (*ptr == ' ')
+ ptr++;
+
+ err_code = atoi (ptr);
+
+ if (err_code < 200 || err_code >= 300)
+ {
+ switch (err_code)
+ {
+ case 403:
+ g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_PROXY_NOT_ALLOWED,
+ _("HTTP proxy connection not allowed"));
+ break;
+ case 407:
+ if (has_cred)
+ g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_PROXY_AUTH_FAILED,
+ _("HTTP proxy authentication failed"));
+ else
+ g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_PROXY_NEED_AUTH,
+ _("HTTP proxy authentication required"));
+ break;
+ default:
+ g_set_error (error, G_IO_ERROR, G_IO_ERROR_PROXY_FAILED,
+ _("HTTP proxy connection failed: %i"), err_code);
+ }
+
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+#define HTTP_END_MARKER "\r\n\r\n"
+
+static GIOStream *
+g_http_proxy_connect (GProxy *proxy,
+ GIOStream *io_stream,
+ GProxyAddress *proxy_address,
+ GCancellable *cancellable,
+ GError **error)
+{
+ GInputStream *in;
+ GOutputStream *out;
+ gchar *buffer = NULL;
+ gsize buffer_length;
+ gssize bytes_read;
+ gboolean has_cred;
+ GIOStream *tlsconn = NULL;
+
+ if (G_IS_HTTPS_PROXY (proxy))
+ {
+ tlsconn = g_tls_client_connection_new (io_stream,
+ G_SOCKET_CONNECTABLE (proxy_address),
+ error);
+ if (!tlsconn)
+ goto error;
+
+#ifdef DEBUG
+ {
+ GTlsCertificateFlags tls_validation_flags = G_TLS_CERTIFICATE_VALIDATE_ALL;
+
+ tls_validation_flags &= ~(G_TLS_CERTIFICATE_UNKNOWN_CA | G_TLS_CERTIFICATE_BAD_IDENTITY);
+ g_tls_client_connection_set_validation_flags (G_TLS_CLIENT_CONNECTION (tlsconn),
+ tls_validation_flags);
+ }
+#endif
+
+ if (!g_tls_connection_handshake (G_TLS_CONNECTION (tlsconn), cancellable, error))
+ goto error;
+
+ io_stream = tlsconn;
+ }
+
+ in = g_io_stream_get_input_stream (io_stream);
+ out = g_io_stream_get_output_stream (io_stream);
+
+ buffer = create_request (proxy_address, &has_cred);
+ if (!g_output_stream_write_all (out, buffer, strlen (buffer), NULL,
+ cancellable, error))
+ goto error;
+
+ g_free (buffer);
+
+ bytes_read = 0;
+ buffer_length = 1024;
+ buffer = g_malloc (buffer_length);
+
+ /* Read byte-by-byte instead of using GDataInputStream
+ * since we do not want to read beyond the end marker
+ */
+ do
+ {
+ gsize nread;
+
+ nread = g_input_stream_read (in, buffer + bytes_read, 1, cancellable, error);
+ if (nread == -1)
+ goto error;
+
+ if (nread == 0)
+ break;
+
+ ++bytes_read;
+
+ if (bytes_read == buffer_length)
+ {
+ buffer_length = 2 * buffer_length;
+ buffer = g_realloc (buffer, buffer_length);
+ }
+
+ *(buffer + bytes_read) = '\0';
+
+ if (g_str_has_suffix (buffer, HTTP_END_MARKER))
+ break;
+ }
+ while (TRUE);
+
+ if (bytes_read == 0)
+ {
+ g_set_error_literal (error, G_IO_ERROR, G_IO_ERROR_PROXY_FAILED,
+ _("HTTP proxy server closed connection unexpectedly."));
+ goto error;
+ }
+
+ if (!check_reply (buffer, has_cred, error))
+ goto error;
+
+ g_free (buffer);
+
+ g_object_ref (io_stream);
+ g_clear_object (&tlsconn);
+
+ return io_stream;
+
+error:
+ g_clear_object (&tlsconn);
+ g_free (buffer);
+ return NULL;
+}
+
+typedef struct
+{
+ GIOStream *io_stream;
+ GProxyAddress *proxy_address;
+} ConnectAsyncData;
+
+static void
+free_connect_data (ConnectAsyncData *data)
+{
+ g_object_unref (data->io_stream);
+ g_object_unref (data->proxy_address);
+ g_slice_free (ConnectAsyncData, data);
+}
+
+static void
+connect_thread (GTask *task,
+ gpointer source_object,
+ gpointer task_data,
+ GCancellable *cancellable)
+{
+ GProxy *proxy = source_object;
+ ConnectAsyncData *data = task_data;
+ GIOStream *res;
+ GError *error = NULL;
+
+ res = g_http_proxy_connect (proxy, data->io_stream, data->proxy_address,
+ cancellable, &error);
+
+ if (res == NULL)
+ g_task_return_error (task, error);
+ else
+ g_task_return_pointer (task, res, g_object_unref);
+}
+
+static void
+g_http_proxy_connect_async (GProxy *proxy,
+ GIOStream *io_stream,
+ GProxyAddress *proxy_address,
+ GCancellable *cancellable,
+ GAsyncReadyCallback callback,
+ gpointer user_data)
+{
+ ConnectAsyncData *data;
+ GTask *task;
+
+ data = g_slice_new0 (ConnectAsyncData);
+ data->io_stream = g_object_ref (io_stream);
+ data->proxy_address = g_object_ref (proxy_address);
+
+ task = g_task_new (proxy, cancellable, callback, user_data);
+ g_task_set_task_data (task, data, (GDestroyNotify) free_connect_data);
+
+ g_task_run_in_thread (task, connect_thread);
+ g_object_unref (task);
+}
+
+static GIOStream *
+g_http_proxy_connect_finish (GProxy *proxy,
+ GAsyncResult *result,
+ GError **error)
+{
+ return g_task_propagate_pointer (G_TASK (result), error);
+}
+
+static gboolean
+g_http_proxy_supports_hostname (GProxy *proxy)
+{
+ return TRUE;
+}
+
+static void
+g_http_proxy_class_init (GHttpProxyClass *class)
+{
+}
+
+static void
+g_http_proxy_iface_init (GProxyInterface *proxy_iface)
+{
+ proxy_iface->connect = g_http_proxy_connect;
+ proxy_iface->connect_async = g_http_proxy_connect_async;
+ proxy_iface->connect_finish = g_http_proxy_connect_finish;
+ proxy_iface->supports_hostname = g_http_proxy_supports_hostname;
+}
+
+struct _GHttpsProxy
+{
+ GHttpProxy parent;
+};
+
+struct _GHttpsProxyClass
+{
+ GHttpProxyClass parent_class;
+};
+
+#define g_https_proxy_get_type _g_https_proxy_get_type
+G_DEFINE_TYPE_WITH_CODE (GHttpsProxy, g_https_proxy, G_TYPE_HTTP_PROXY,
+ G_IMPLEMENT_INTERFACE (G_TYPE_PROXY,
+ g_http_proxy_iface_init)
+ _g_io_modules_ensure_extension_points_registered ();
+ g_io_extension_point_implement (G_PROXY_EXTENSION_POINT_NAME,
+ g_define_type_id,
+ "https",
+ 0))
+
+static void
+g_https_proxy_init (GHttpsProxy *proxy)
+{
+}
+
+static void
+g_https_proxy_class_init (GHttpsProxyClass *class)
+{
+}
diff --git a/gio/ghttpproxy.h b/gio/ghttpproxy.h
new file mode 100644
index 000000000..f87f40945
--- /dev/null
+++ b/gio/ghttpproxy.h
@@ -0,0 +1,54 @@
+/* GIO - GLib Input, Output and Streaming Library
+ *
+ * Copyright (C) 2010 Collabora, Ltd.
+ *
+ * This library is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Lesser General Public
+ * License as published by the Free Software Foundation; either
+ * version 2 of the License, or (at your option) any later version.
+ *
+ * This library is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General
+ * Public License along with this library; if not, see <http://www.gnu.org/licenses/>.
+ *
+ * Author: Nicolas Dufresne <nicolas.dufresne@collabora.co.uk>
+ */
+
+#ifndef __G_HTTP_PROXY_H__
+#define __G_HTTP_PROXY_H__
+
+#include <gio/giotypes.h>
+
+G_BEGIN_DECLS
+
+#define G_TYPE_HTTP_PROXY (_g_http_proxy_get_type ())
+#define G_HTTP_PROXY(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_HTTP_PROXY, GHttpProxy))
+#define G_HTTP_PROXY_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), G_TYPE_HTTP_PROXY, GHttpProxyClass))
+#define G_IS_HTTP_PROXY(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_HTTP_PROXY))
+#define G_IS_HTTP_PROXY_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), G_TYPE_HTTP_PROXY))
+#define G_HTTP_PROXY_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), G_TYPE_HTTP_PROXY, GHttpProxyClass))
+
+typedef struct _GHttpProxy GHttpProxy;
+typedef struct _GHttpProxyClass GHttpProxyClass;
+
+GType _g_http_proxy_get_type (void);
+
+#define G_TYPE_HTTPS_PROXY (_g_https_proxy_get_type ())
+#define G_HTTPS_PROXY(o) (G_TYPE_CHECK_INSTANCE_CAST ((o), G_TYPE_HTTPS_PROXY, GHttpsProxy))
+#define G_HTTPS_PROXY_CLASS(k) (G_TYPE_CHECK_CLASS_CAST((k), G_TYPE_HTTPS_PROXY, GHttpsProxyClass))
+#define G_IS_HTTPS_PROXY(o) (G_TYPE_CHECK_INSTANCE_TYPE ((o), G_TYPE_HTTPS_PROXY))
+#define G_IS_HTTPS_PROXY_CLASS(k) (G_TYPE_CHECK_CLASS_TYPE ((k), G_TYPE_HTTPS_PROXY))
+#define G_HTTPS_PROXY_GET_CLASS(o) (G_TYPE_INSTANCE_GET_CLASS ((o), G_TYPE_HTTPS_PROXY, GHttpsProxyClass))
+
+typedef struct _GHttpsProxy GHttpsProxy;
+typedef struct _GHttpsProxyClass GHttpsProxyClass;
+
+GType _g_https_proxy_get_type (void);
+
+G_END_DECLS
+
+#endif /* __G_HTTP_PROXY_H__ */
diff --git a/gio/giomodule.c b/gio/giomodule.c
index b22ea4993..4b16a831c 100644
--- a/gio/giomodule.c
+++ b/gio/giomodule.c
@@ -30,6 +30,7 @@
#include "gproxyresolver.h"
#include "gproxy.h"
#include "gsettingsbackendinternal.h"
+#include "ghttpproxy.h"
#include "gsocks4proxy.h"
#include "gsocks4aproxy.h"
#include "gsocks5proxy.h"
@@ -1098,6 +1099,8 @@ _g_io_modules_ensure_loaded (void)
#endif
g_type_ensure (_g_local_vfs_get_type ());
g_type_ensure (_g_dummy_proxy_resolver_get_type ());
+ g_type_ensure (_g_http_proxy_get_type ());
+ g_type_ensure (_g_https_proxy_get_type ());
g_type_ensure (_g_socks4a_proxy_get_type ());
g_type_ensure (_g_socks4_proxy_get_type ());
g_type_ensure (_g_socks5_proxy_get_type ());