From ee40cef78df148f9f96978e0be9e591edfa5853d Mon Sep 17 00:00:00 2001 From: alex Date: Wed, 6 Dec 2000 22:28:48 +0000 Subject: [PATCH 1/1] Initial version --- AUTHORS | 5 ++ ChangeLog | 0 Makefile.am | 28 ++++++ NEWS | 0 README | 0 autogen.sh | 148 +++++++++++++++++++++++++++++++ configure.in | 179 +++++++++++++++++++++++++++++++++++++ libsoup/Makefile.am | 29 ++++++ libsoup/soup-context.c | 182 ++++++++++++++++++++++++++++++++++++++ libsoup/soup-context.h | 52 +++++++++++ libsoup/soup-private.h | 65 ++++++++++++++ libsoup/soup-queue.c | 236 +++++++++++++++++++++++++++++++++++++++++++++++++ libsoup/soup-queue.h | 53 +++++++++++ libsoup/soup-uri.c | 189 +++++++++++++++++++++++++++++++++++++++ libsoup/soup-uri.h | 63 +++++++++++++ libsoup/soup.h | 20 +++++ 16 files changed, 1249 insertions(+) create mode 100644 AUTHORS create mode 100644 ChangeLog create mode 100644 Makefile.am create mode 100644 NEWS create mode 100644 README create mode 100755 autogen.sh create mode 100644 configure.in create mode 100644 libsoup/Makefile.am create mode 100644 libsoup/soup-context.c create mode 100644 libsoup/soup-context.h create mode 100644 libsoup/soup-private.h create mode 100644 libsoup/soup-queue.c create mode 100644 libsoup/soup-queue.h create mode 100644 libsoup/soup-uri.c create mode 100644 libsoup/soup-uri.h create mode 100644 libsoup/soup.h diff --git a/AUTHORS b/AUTHORS new file mode 100644 index 0000000..1139aa6 --- /dev/null +++ b/AUTHORS @@ -0,0 +1,5 @@ +SOUP Authors +============ + +Alex Graveley +Miguel De Icaza diff --git a/ChangeLog b/ChangeLog new file mode 100644 index 0000000..e69de29 diff --git a/Makefile.am b/Makefile.am new file mode 100644 index 0000000..45d5428 --- /dev/null +++ b/Makefile.am @@ -0,0 +1,28 @@ +## Process this file with automake to produce Makefile.in + +SUBDIRS = src + +EXTRA_DIST = \ + autogen.sh + +install-data-local: + @$(NORMAL_INSTALL) + if test -d $(srcdir)/pixmaps; then \ + $(mkinstalldirs) $(DESTDIR)$(pkgdatadir)/pixmaps; \ + for pixmap in $(srcdir)/pixmaps/*; do \ + if test -f $$pixmap; then \ + $(INSTALL_DATA) $$pixmap $(DESTDIR)$(pkgdatadir)/pixmaps; \ + fi \ + done \ + fi + +dist-hook: + if test -d pixmaps; then \ + mkdir $(distdir)/pixmaps; \ + for pixmap in pixmaps/*; do \ + if test -f $$pixmap; then \ + cp -p $$pixmap $(distdir)/pixmaps; \ + fi \ + done \ + fi + diff --git a/NEWS b/NEWS new file mode 100644 index 0000000..e69de29 diff --git a/README b/README new file mode 100644 index 0000000..e69de29 diff --git a/autogen.sh b/autogen.sh new file mode 100755 index 0000000..293b797 --- /dev/null +++ b/autogen.sh @@ -0,0 +1,148 @@ +#!/bin/sh +# Run this to generate all the initial makefiles, etc. + +srcdir=`dirname $0` +PKG_NAME="the package." + +DIE=0 + +(autoconf --version) < /dev/null > /dev/null 2>&1 || { + echo + echo "**Error**: You must have \`autoconf' installed to." + echo "Download the appropriate package for your distribution," + echo "or get the source tarball at ftp://ftp.gnu.org/pub/gnu/" + DIE=1 +} + +(grep "^AM_PROG_LIBTOOL" $srcdir/configure.in >/dev/null) && { + (libtool --version) < /dev/null > /dev/null 2>&1 || { + echo + echo "**Error**: You must have \`libtool' installed." + echo "Get ftp://ftp.gnu.org/pub/gnu/libtool-1.2d.tar.gz" + echo "(or a newer version if it is available)" + DIE=1 + } +} + +grep "^AM_GNU_GETTEXT" $srcdir/configure.in >/dev/null && { + grep "sed.*POTFILES" $srcdir/configure.in >/dev/null || \ + (gettext --version) < /dev/null > /dev/null 2>&1 || { + echo + echo "**Error**: You must have \`gettext' installed." + echo "Get ftp://alpha.gnu.org/gnu/gettext-0.10.35.tar.gz" + echo "(or a newer version if it is available)" + DIE=1 + } +} + +grep "^AM_GNOME_GETTEXT" $srcdir/configure.in >/dev/null && { + grep "sed.*POTFILES" $srcdir/configure.in >/dev/null || \ + (gettext --version) < /dev/null > /dev/null 2>&1 || { + echo + echo "**Error**: You must have \`gettext' installed." + echo "Get ftp://alpha.gnu.org/gnu/gettext-0.10.35.tar.gz" + echo "(or a newer version if it is available)" + DIE=1 + } +} + +(automake --version) < /dev/null > /dev/null 2>&1 || { + echo + echo "**Error**: You must have \`automake' installed." + echo "Get ftp://ftp.gnu.org/pub/gnu/automake-1.3.tar.gz" + echo "(or a newer version if it is available)" + DIE=1 + NO_AUTOMAKE=yes +} + + +# if no automake, don't bother testing for aclocal +test -n "$NO_AUTOMAKE" || (aclocal --version) < /dev/null > /dev/null 2>&1 || { + echo + echo "**Error**: Missing \`aclocal'. The version of \`automake'" + echo "installed doesn't appear recent enough." + echo "Get ftp://ftp.gnu.org/pub/gnu/automake-1.3.tar.gz" + echo "(or a newer version if it is available)" + DIE=1 +} + +if test "$DIE" -eq 1; then + exit 1 +fi + +if test -z "$*"; then + echo "**Warning**: I am going to run \`configure' with no arguments." + echo "If you wish to pass any to it, please specify them on the" + echo \`$0\'" command line." + echo +fi + +case $CC in +xlc ) + am_opt=--include-deps;; +esac + +for coin in `find $srcdir -name configure.in -print` +do + dr=`dirname $coin` + if test -f $dr/NO-AUTO-GEN; then + echo skipping $dr -- flagged as no auto-gen + else + echo processing $dr + macrodirs=`sed -n -e 's,AM_ACLOCAL_INCLUDE(\(.*\)),\1,gp' < $coin` + ( cd $dr + aclocalinclude="$ACLOCAL_FLAGS" + for k in $macrodirs; do + if test -d $k; then + aclocalinclude="$aclocalinclude -I $k" + ##else + ## echo "**Warning**: No such directory \`$k'. Ignored." + fi + done + if grep "^AM_GNU_GETTEXT" configure.in >/dev/null; then + if grep "sed.*POTFILES" configure.in >/dev/null; then + : do nothing -- we still have an old unmodified configure.in + else + echo "Creating $dr/aclocal.m4 ..." + test -r $dr/aclocal.m4 || touch $dr/aclocal.m4 + echo "Running gettextize... Ignore non-fatal messages." + echo "no" | gettextize --force --copy + echo "Making $dr/aclocal.m4 writable ..." + test -r $dr/aclocal.m4 && chmod u+w $dr/aclocal.m4 + fi + fi + if grep "^AM_GNOME_GETTEXT" configure.in >/dev/null; then + echo "Creating $dr/aclocal.m4 ..." + test -r $dr/aclocal.m4 || touch $dr/aclocal.m4 + echo "Running gettextize... Ignore non-fatal messages." + echo "no" | gettextize --force --copy + echo "Making $dr/aclocal.m4 writable ..." + test -r $dr/aclocal.m4 && chmod u+w $dr/aclocal.m4 + fi + if grep "^AM_PROG_LIBTOOL" configure.in >/dev/null; then + echo "Running libtoolize..." + libtoolize --force --copy + fi + echo "Running aclocal $aclocalinclude ..." + aclocal $aclocalinclude + if grep "^AM_CONFIG_HEADER" configure.in >/dev/null; then + echo "Running autoheader..." + autoheader + fi + echo "Running automake --gnu $am_opt ..." + automake --add-missing --gnu $am_opt + echo "Running autoconf ..." + autoconf + ) + fi +done + +#conf_flags="--enable-maintainer-mode --enable-compile-warnings" #--enable-iso-c + +if test x$NOCONFIGURE = x; then + echo Running $srcdir/configure $conf_flags "$@" ... + $srcdir/configure $conf_flags "$@" \ + && echo Now type \`make\' to compile $PKG_NAME +else + echo Skipping configure process. +fi diff --git a/configure.in b/configure.in new file mode 100644 index 0000000..33cce22 --- /dev/null +++ b/configure.in @@ -0,0 +1,179 @@ +# Process this file with autoconf to produce a configure script. + +# This is almost a direct rip-off of Gnet's configure.in + +# Require autoconf 2.13 +AC_PREREQ(2.13) + +# Initialize autoconf +AC_INIT(src/soup.h) + + +# On making releases: +# SOUP_MICRO_VERSION += 1; +# SOUP_INTERFACE_AGE += 1; +# SOUP_BINARY_AGE += 1; +# if any functions have been added, set SOUP_INTERFACE_AGE to 0. +# if backwards compatibility has been broken, +# set SOUP_BINARY_AGE _and_ SOUP_INTERFACE_AGE to 0. + +AC_DIVERT_PUSH(AC_DIVERSION_NOTICE) +SOUP_MAJOR_VERSION=0 +SOUP_MINOR_VERSION=1 +SOUP_MICRO_VERSION=5 +SOUP_INTERFACE_AGE=0 +SOUP_BINARY_AGE=0 +SOUP_VERSION=$SOUP_MAJOR_VERSION.$SOUP_MINOR_VERSION.$SOUP_MICRO_VERSION +AC_DIVERT_POP() + +AC_SUBST(SOUP_MAJOR_VERSION) +AC_SUBST(SOUP_MINOR_VERSION) +AC_SUBST(SOUP_MICRO_VERSION) +AC_SUBST(SOUP_VERSION) +AC_SUBST(SOUP_INTERFACE_AGE) +AC_SUBST(SOUP_BINARY_AGE) + +# libtool versioning +LT_RELEASE=$SOUP_MAJOR_VERSION.$SOUP_MINOR_VERSION +LT_CURRENT=`expr $SOUP_MICRO_VERSION - $SOUP_INTERFACE_AGE` +LT_REVISION=$SOUP_INTERFACE_AGE +LT_AGE=`expr $SOUP_BINARY_AGE - $SOUP_INTERFACE_AGE` +AC_SUBST(LT_RELEASE) +AC_SUBST(LT_CURRENT) +AC_SUBST(LT_REVISION) +AC_SUBST(LT_AGE) + +VERSION=$SOUP_VERSION +PACKAGE=soup + +# Specify a configuration file +AM_CONFIG_HEADER(config.h) + +# Define version stuff +AC_DEFINE_UNQUOTED(SOUP_MAJOR_VERSION, $SOUP_MAJOR_VERSION) +AC_DEFINE_UNQUOTED(SOUP_MINOR_VERSION, $SOUP_MINOR_VERSION) +AC_DEFINE_UNQUOTED(SOUP_MICRO_VERSION, $SOUP_MICRO_VERSION) +AC_DEFINE_UNQUOTED(SOUP_INTERFACE_AGE, $SOUP_INTERFACE_AGE) +AC_DEFINE_UNQUOTED(SOUP_BINARY_AGE, $SOUP_BINARY_AGE) + +# Initialize automake +AM_INIT_AUTOMAKE($PACKAGE, $VERSION, no-define) + +# Initialize maintainer mode +AM_MAINTAINER_MODE + + +# **************************************** +# Set debugging flags + +# Figure out debugging default, prior to $ac_help setup +AC_DIVERT_PUSH(AC_DIVERSION_NOTICE)dnl +if test `expr $SOUP_MINOR_VERSION \% 2` = 1 ; then + debug_default=yes +else + debug_default=minimum +fi +AC_DIVERT_POP() + +# Declare --enable-* args and collect ac_help strings +AC_ARG_ENABLE(debug, [ --enable-debug=[no/minimum/yes] turn on debugging [default=$debug_default]],,enable_debug=$debug_default) + +# Set the debug flags +AC_MSG_CHECKING(for --enable-debug) +if test "x$enable_debug" = "xyes"; then + test "$cflags_set" = set || CFLAGS="$CFLAGS -g" + SOUP_DEBUG_FLAGS="-DG_ENABLE_DEBUG" + AC_MSG_RESULT(yes) +else + if test "x$enable_debug" = "xno"; then + SOUP_DEBUG_FLAGS="-DG_DISABLE_ASSERT -DG_DISABLE_CHECKS" + AC_MSG_RESULT(no) + fi +fi + +AC_SUBST(SOUP_DEBUG_FLAGS) + + +# **************************************** +# Check for programs + +AC_PROG_CC +AM_PROG_CC_STDC +AC_PROG_INSTALL + +# Use an many warnings as possible +changequote(,) +if test "x$GCC" = "xyes"; then + case " $CFLAGS " in + *[\ \ ]-Wall[\ \ ]*) ;; + *) CFLAGS="$CFLAGS -Wall" ;; + esac + + if test "x$enable_ansi" = "xyes"; then + case " $CFLAGS " in + *[\ \ ]-ansi[\ \ ]*) ;; + *) CFLAGS="$CFLAGS -ansi" ;; + esac + + case " $CFLAGS " in + *[\ \ ]-pedantic[\ \ ]*) ;; + *) CFLAGS="$CFLAGS -pedantic" ;; + esac + fi +fi +changequote([,]) + +# Use reentrant functions +CFLAGS="$CFLAGS -D_REENTRANT" + +# Set STDC_HEADERS +AC_HEADER_STDC + +# Initialize libtool +AM_PROG_LIBTOOL + + +# **************************************** +# Check for libraries + +# Need GLIB +AM_PATH_GLIB(1.2.0, + [LIBS="$LIBS $GLIB_LIBS" CFLAGS="$CFLAGS $GLIB_CFLAGS"], + AC_MSG_ERROR(Cannot find GLIB: Is glib-config in path?)) + +GLIB_CFLAGS=`glib-config --cflags glib` +GLIB_LIBS=`glib-config --libs glib` + +AC_SUBST(GLIB_CFLAGS) +AC_SUBST(GLIB_LIBS) + + +# Need GNET +AM_PATH_GNET(1.0.0, + [LIBS="$LIBS $GNET_LIBS" CFLAGS="$CFLAGS $GNET_CFLAGS"], + AC_MSG_ERROR(Cannot find GNET: Is gnet-config in path?)) + +GNET_CFLAGS=`gnet-config --cflags gnet` +GNET_LIBS=`gnet-config --libs gnet` + +AC_SUBST(GNET_CFLAGS) +AC_SUBST(GNET_LIBS) + + +# Set PACKAGE_SOURCE_DIR in config.h. +packagesrcdir=`cd $srcdir && pwd` +AC_DEFINE_UNQUOTED(PACKAGE_SOURCE_DIR, "${packagesrcdir}") + +# If gcc is the compiler, compile with lots of warnings +if test "x$GCC" = "xyes"; then + CFLAGS="$CFLAGS -Wall -Wstrict-prototypes -Wmissing-prototypes -Wmissing-declarations " +fi + +# doc/Makefile +# gnome-soup.spec + +AC_OUTPUT([ + Makefile + src/Makefile +]) + diff --git a/libsoup/Makefile.am b/libsoup/Makefile.am new file mode 100644 index 0000000..052135c --- /dev/null +++ b/libsoup/Makefile.am @@ -0,0 +1,29 @@ +## Process this file with automake to produce Makefile.in + +@SET_MAKE@ + +INCLUDES = -DG_LOG_DOMAIN=\"SOUP\" @SOUP_DEBUG_FLAGS@ + +soupincludedir = $(includedir)/soup + +lib_LTLIBRARIES = libsoup.la + +libsoup_la_LDFLAGS = \ + -version-info $(LT_CURRENT):$(LT_REVISION):$(LT_AGE) \ + -release $(LT_RELEASE) + +libsoup_la_SOURCES = \ + soup-context.c \ + soup-queue.c \ + soup-request.c \ + soup-uri.c + +soupinclude_HEADERS = \ + soup.h \ + soup-context.h \ + soup-queue.h \ + soup-request.h \ + soup-uri.h + +EXTRA_DIST = soup-private.h + diff --git a/libsoup/soup-context.c b/libsoup/soup-context.c new file mode 100644 index 0000000..6353f1b --- /dev/null +++ b/libsoup/soup-context.c @@ -0,0 +1,182 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * soup-queue.c: Asyncronous Callback-based SOAP Request Queue. + * + * Authors: + * Alex Graveley (alex@helixcode.com) + * + * Copyright (C) 2000, Helix Code, Inc. + */ + +#include + +#include "soup-context.h" +#include "soup-private.h" +#include "soup-uri.h" + +GHashTable *servers; + +static SoupContext * +soup_context_new (SoupServer *server, SoupUri *uri) +{ + SoupContext *ctx = g_new0 (SoupContext, 1); + ctx->priv = g_new0 (SoupContextPrivate, 1); + ctx->priv->server = server; + ctx->priv->keep_alive = TRUE; + ctx->priv->chunk_size = DEFAULT_CHUNK_SIZE; + ctx->uri = uri; + ctx->custom_headers = NULL; + return ctx; +} + +SoupContext * +soup_context_get (gchar *uri) +{ + SoupServer *serv; + SoupContext *ret = NULL; + SoupUri *suri = soup_uri_new (uri); + + if (!servers) + servers = g_hash_table_new (g_str_hash, g_str_equal); + else + serv = g_hash_table_lookup (servers, suri->host); + + if (serv) { + if (serv->contexts) + ret = g_hash_table_lookup (serv->contexts, suri->path); + else + serv->contexts = g_hash_table_new (g_str_hash, + g_str_equal); + } else { + serv = g_new0 (SoupServer, 1); + serv->host = g_strdup (suri->host); + g_hash_table_insert (servers, suri->host, serv); + } + + if (ret) return ret; + + ret = soup_context_new (serv, suri); + + g_hash_table_insert (serv->contexts, suri->path, ret); + + return ret; +} + +void +soup_context_free (SoupContext *ctx) +{ +} + +struct SoupContextConnectFunctor { + SoupContext *ctx; + SoupConnectCallbackFn cb; + gpointer user_data; +}; + +static void +soup_context_connect_cb (GTcpSocket *socket, + GInetAddr* addr, + GTcpSocketConnectAsyncStatus status, + gpointer user_data) +{ + struct SoupContextConnectFunctor *data = user_data; + SoupContext *ctx = data->ctx; + SoupConnectCallbackFn cb = data->cb; + gpointer cb_data = data->user_data; + SoupConnection *new_conn; + + g_free (data); + + gnet_inetaddr_unref(addr); + + switch (status) { + case GTCP_SOCKET_CONNECT_ASYNC_STATUS_OK: + new_conn = g_new0 (SoupConnection, 1); + new_conn->port = ctx->uri->port; + new_conn->in_use = TRUE; + new_conn->socket = socket; + + ctx->priv->server->connections = + g_slist_prepend (ctx->priv->server->connections, + new_conn); + + (*cb) (ctx, SOUP_CONNECT_ERROR_NONE, socket, cb_data); + break; + case GTCP_SOCKET_CONNECT_ASYNC_STATUS_INETADDR_ERROR: + (*cb) (ctx, SOUP_CONNECT_ERROR_ADDR_RESOLVE, NULL, cb_data); + break; + case GTCP_SOCKET_CONNECT_ASYNC_STATUS_TCP_ERROR: + (*cb) (ctx, SOUP_CONNECT_ERROR_NETWORK, NULL, cb_data); + break; + } +} + +void +soup_context_get_connection (SoupContext *ctx, + SoupConnectCallbackFn cb, + gpointer user_data) +{ + GSList *conns; + + if (!ctx->priv->keep_alive) + goto FORCE_NEW_CONNECTION; + + conns = ctx->priv->server->connections; + + while (conns) { + SoupConnection *conn = conns->data; + + if (!conn->in_use && conn->port == ctx->uri->port) { + conn->in_use = TRUE; + (*cb) (ctx, + SOUP_CONNECT_ERROR_NONE, + conn->socket, + user_data); + return; + } + + conns = conns->next; + } + + FORCE_NEW_CONNECTION: + { + struct SoupContextConnectFunctor *data; + data = g_new0 (struct SoupContextConnectFunctor, 1); + data->ctx = ctx; + data->cb = cb; + data->user_data = user_data; + gnet_tcp_socket_connect_async (ctx->uri->host, + ctx->uri->port, + soup_context_connect_cb, + user_data); + return; + } +} + +void +soup_context_return_connection (SoupContext *ctx, + GTcpSocket *socket) +{ + SoupServer *server = ctx->priv->server; + GSList *conns = server->connections; + + while (conns) { + SoupConnection *conn = conns->data; + + if (conn->socket == socket) { + if (ctx->priv->keep_alive) { + conn->in_use = FALSE; + } else { + server->connections = + g_slist_remove (server->connections, + socket); + gnet_tcp_socket_unref (socket); + connection_count--; + } + + return; + } + + conns = conns->next; + } +} diff --git a/libsoup/soup-context.h b/libsoup/soup-context.h new file mode 100644 index 0000000..c1f95e6 --- /dev/null +++ b/libsoup/soup-context.h @@ -0,0 +1,52 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * soup-queue.h: Asyncronous Callback-based SOAP Request Queue. + * + * Authors: + * Alex Graveley (alex@helixcode.com) + * + * Copyright (C) 2000, Helix Code, Inc. + */ + +#ifndef SOUP_CONTEXT_H +#define SOUP_CONTEXT_H 1 + +#include + +#include "soup-uri.h" + +typedef struct _SoupContextPrivate SoupContextPrivate; + +typedef struct { + SoupContextPrivate *priv; + SoupUri *uri; + GList *custom_headers; +} SoupContext; + +typedef enum { + SOUP_CONNECT_ERROR_NONE, + SOUP_CONNECT_ERROR_ADDR_RESOLVE, + SOUP_CONNECT_ERROR_NETWORK +} SoupConnectErrorCode; + +typedef void (*SoupConnectCallbackFn) (SoupContext *ctx, + SoupConnectErrorCode err, + GTcpSocket *socket, + gpointer user_data); + +SoupContext *soup_context_get (gchar *uri); + +void soup_context_free (SoupContext *ctx); + +void soup_context_add_header (SoupContext *ctx, + gchar *name, + gchar *value); + +void soup_context_get_connection (SoupContext *ctx, + SoupConnectCallbackFn cb, + gpointer user_data); + +void soup_context_return_connection (SoupContext *ctx, + GTcpSocket *socket); + +#endif /*SOUP_CONTEXT_H*/ diff --git a/libsoup/soup-private.h b/libsoup/soup-private.h new file mode 100644 index 0000000..be3b7f4 --- /dev/null +++ b/libsoup/soup-private.h @@ -0,0 +1,65 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * soup-queue.h: Asyncronous Callback-based SOAP Request Queue. + * + * Authors: + * Alex Graveley (alex@helixcode.com) + * + * Copyright (C) 2000, Helix Code, Inc. + */ + +/* + * All the things SOUP users shouldn't need to know about except under + * extraneous circumstances. + */ + +#ifndef SOAP_PRIVATE_H +#define SOAP_PRIVATE_H 1 + +#include "soup-queue.h" + +#define DEFAULT_CHUNK_SIZE 1024 +#define RESPONSE_BLOCK_SIZE 1024 + +typedef struct { + guint port; + gboolean in_use; + GTcpSocket *socket; +} SoupConnection; + +typedef struct { + gchar *host; + GSList *connections; /* CONTAINS: SoupConnection */ + GHashTable *contexts; /* KEY: uri->path, VALUE: SoupContext */ +} SoupServer; + +extern GHashTable *servers; +extern guint connection_count; +extern GList *active_requests; + +struct _SoupContextPrivate { + SoupServer *server; + + gboolean keep_alive; + gint chunk_size; +}; + +struct _SoupRequestPrivate { + GTcpSocket *socket; + + gulong write_len; + gulong read_len; + + guint connect_tag; + guint read_tag; + guint write_tag; + guint timeout_tag; + + SoupCallbackFn callback; + gpointer user_data; +}; + +SoupCallbackResult soup_request_issue_callback (SoupRequest *req, + SoupErrorCode error); + +#endif /*SOUP_PRIVATE_H*/ diff --git a/libsoup/soup-queue.c b/libsoup/soup-queue.c new file mode 100644 index 0000000..7319a38 --- /dev/null +++ b/libsoup/soup-queue.c @@ -0,0 +1,236 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * soup-queue.c: Asyncronous Callback-based SOAP Request Queue. + * + * Authors: + * Alex Graveley (alex@helixcode.com) + * + * Copyright (C) 2000, Helix Code, Inc. + */ + +#include +#include + +#include "soup-queue.h" +#include "soup-context.h" +#include "soup-private.h" + +guint connection_count = 0; + +GList *active_requests = NULL; + +static guint max_connections = 4; + +static guint soup_queue_idle_tag = 0; + +static SoupContext *proxy_context; + +static gboolean +soup_queue_read_async (GIOChannel* iochannel, + GIOCondition condition, + SoupRequest *req) +{ + guint bytes_read; + GIOError error; + + if (!req->response.body) { + req->response.body = g_malloc (RESPONSE_BLOCK_SIZE); + req->response.length = RESPONSE_BLOCK_SIZE; + } else if (req->priv->read_len == req->response.length) { + req->response.length += RESPONSE_BLOCK_SIZE; + req->response.body = g_realloc (req->response.body, + req->response.length); + } + + error = g_io_channel_read (iochannel, + &req->response.body[req->priv->read_len], + req->response.length - req->priv->read_len, + &bytes_read); + + if (error == G_IO_ERROR_AGAIN) + return TRUE; + + if (error != G_IO_ERROR_NONE) { + soup_request_issue_callback (req, SOUP_ERROR_IO); + return FALSE; + } + + if (bytes_read == 0) { + req->status = SOUP_STATUS_FINISHED; + soup_request_issue_callback (req, SOUP_ERROR_NONE); + return FALSE; + } + + req->priv->read_len += bytes_read; + + return TRUE; +} + +static gboolean +soup_queue_write_async (GIOChannel* iochannel, + GIOCondition condition, + SoupRequest *req) +{ + guint bytes_written; + GIOError error; + + error = g_io_channel_write (iochannel, + &req->request.body[req->priv->write_len], + req->request.length - req->priv->write_len, + &bytes_written); + + if (error == G_IO_ERROR_AGAIN) + return TRUE; + + if (error != G_IO_ERROR_NONE) { + soup_request_issue_callback (req, SOUP_ERROR_IO); + return FALSE; + } + + req->priv->write_len += bytes_written; + + if (req->priv->write_len == req->request.length) { + req->status = SOUP_STATUS_READING_RESPONSE; + req->priv->read_tag = + g_io_add_watch (iochannel, + G_IO_IN|G_IO_ERR|G_IO_HUP|G_IO_NVAL, + (GIOFunc) soup_queue_read_async, + req); + return FALSE; + } + + return TRUE; +} + +static void +soup_queue_connect (SoupContext *ctx, + SoupConnectErrorCode err, + GTcpSocket *socket, + gpointer user_data) +{ + SoupRequest *req = user_data; + GIOChannel *channel; + + switch (err) { + case SOUP_CONNECT_ERROR_NONE: + channel = gnet_tcp_socket_get_iochannel (socket); + + gnet_tcp_socket_set_tos (socket, GNET_TOS_LOWDELAY); + + req->status = SOUP_STATUS_SENDING_REQUEST; + req->priv->socket = socket; + req->priv->write_tag = + g_io_add_watch (channel, + G_IO_OUT|G_IO_ERR|G_IO_HUP|G_IO_NVAL, + (GIOFunc) soup_queue_write_async, + req); + break; + case SOUP_CONNECT_ERROR_ADDR_RESOLVE: + case SOUP_CONNECT_ERROR_NETWORK: + soup_request_issue_callback (req, SOUP_ERROR_CANT_CONNECT); + connection_count--; + break; + } +} + +static gboolean +soup_idle_handle_new_requests (gpointer unused) +{ + GList *iter; + gboolean work_to_do = FALSE; + + if (connection_count >= max_connections) + return TRUE; + + for (iter = active_requests; iter; iter = iter->next) { + SoupRequest *req = iter->data; + + if (connection_count >= max_connections) + return TRUE; + + if (req->status != SOUP_STATUS_QUEUED) + continue; + + if (req->priv->socket) { + GTcpSocket *sock = req->priv->socket; + GIOChannel *channel; + channel = gnet_tcp_socket_get_iochannel (sock); + + req->status = SOUP_STATUS_SENDING_REQUEST; + req->priv->write_tag = g_io_add_watch ( + channel, + G_IO_OUT|G_IO_ERR|G_IO_HUP|G_IO_NVAL, + (GIOFunc) soup_queue_write_async, + req); + } else { + SoupContext *ctx; + ctx = proxy_context ? proxy_context : req->context; + connection_count++; + + req->status = SOUP_STATUS_CONNECTING; + soup_context_get_connection (ctx, + soup_queue_connect, + req); + } + + work_to_do = TRUE; + } + + if (!work_to_do) { + soup_queue_idle_tag = 0; + return FALSE; + } + + return TRUE; +} + +void +soup_queue_request (SoupRequest *req, + SoupCallbackFn callback, + gpointer user_data) +{ + if (!soup_queue_idle_tag) + soup_queue_idle_tag = + g_idle_add (soup_idle_handle_new_requests, NULL); + + if (req->response.body && + req->response.owner == SOUP_BUFFER_SYSTEM_OWNED) { + g_free (req->response.body); + req->response.body = NULL; + req->response.length = 0; + } + + req->priv->callback = callback; + req->priv->user_data = user_data; + req->status = SOUP_STATUS_QUEUED; + + active_requests = g_list_append (active_requests, req); +} + +void +soup_queue_shutdown () +{ + GList *iter; + + g_source_remove (soup_queue_idle_tag); + soup_queue_idle_tag = 0; + + for (iter = active_requests; iter; iter = iter->next) + soup_request_cancel (iter->data); +} + +void +soup_queue_set_proxy (SoupContext *context) +{ + if (proxy_context) + soup_context_free (proxy_context); + + proxy_context = context; +} + +SoupContext * +soup_queue_get_proxy () +{ + return proxy_context; +} + diff --git a/libsoup/soup-queue.h b/libsoup/soup-queue.h new file mode 100644 index 0000000..1b6664a --- /dev/null +++ b/libsoup/soup-queue.h @@ -0,0 +1,53 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * soup-queue.h: Asyncronous Callback-based SOAP Request Queue. + * + * Authors: + * Alex Graveley (alex@helixcode.com) + * + * Copyright (C) 2000, Helix Code, Inc. + */ + +#ifndef SOUP_QUEUE_H +#define SOUP_QUEUE_H 1 + +#include + +#include "soup-request.h" +#include "soup-context.h" + +typedef enum { + SOUP_RESULT_FREE_REQUEST = 0, + SOUP_RESULT_RESEND_REQUEST, + SOUP_RESULT_DO_NOTHING +} SoupCallbackResult; + +typedef enum { + SOUP_ERROR_NONE = 0, + SOUP_ERROR_CANCELLED, + SOUP_ERROR_CANT_CONNECT, + SOUP_ERROR_URI_NOT_FOUND, + SOUP_ERROR_URI_NOT_PERMITTED, + SOUP_ERROR_URI_OBJECT_MOVED, + SOUP_ERROR_IO, + SOUP_ERROR_MALFORMED_HEADER, + SOUP_ERROR_UNKNOWN +} SoupErrorCode; + +typedef SoupCallbackResult (*SoupCallbackFn) (SoupRequest *req, + SoupErrorCode err, + gpointer user_data); + +void soup_queue_request (SoupRequest *req, + SoupCallbackFn callback, + gpointer user_data); + +void soup_queue_cancel_request (SoupRequest *req); + +void soup_queue_set_proxy (SoupContext *ctx); + +SoupContext *soup_queue_get_proxy (void); + +void soup_queue_shutdown (void); + +#endif /* SOUP_QUEUE_H */ diff --git a/libsoup/soup-uri.c b/libsoup/soup-uri.c new file mode 100644 index 0000000..31a2aca --- /dev/null +++ b/libsoup/soup-uri.c @@ -0,0 +1,189 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* soup-uri.c : utility functions to parse URLs */ + +/* + * Authors : + * Bertrand Guiheneuf + * Dan Winship + * + * Copyright 1999, 2000 Helix Code, Inc. (http://www.helixcode.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + * USA + */ + + + +/* + * Here we deal with URLs following the general scheme: + * protocol://user;AUTH=mech:password@host:port/name + * where name is a path-like string (ie dir1/dir2/....) See RFC 1738 + * for the complete description of Uniform Resource Locators. The + * ";AUTH=mech" addition comes from RFC 2384, "POP URL Scheme". + */ + +/* XXX TODO: + * recover the words between #'s or ?'s after the path + * % escapes + */ + +#include +#include +#include "soup-uri.h" + +/** + * soup_uri_new: create a Gurl object from a string + * + * @uri_string: The string containing the URL to scan + * + * This routine takes a gchar and parses it as a + * URL of the form: + * protocol://user;AUTH=mech:password@host:port/path + * There is no test on the values. For example, + * "port" can be a string, not only a number! + * The Gurl structure fields are filled with + * the scan results. When a member of the + * general URL can not be found, the corresponding + * Gurl member is NULL. + * Fields filled in the Gurl structure are allocated + * and url_string is not modified. + * + * Return value: a Gurl structure containing the URL items. + **/ +SoupUri *soup_uri_new (const gchar* uri_string) +{ + SoupUri *g_uri; + char *semi, *colon, *at, *slash, *path; + char **split; + + g_uri = g_new (SoupUri,1); + + /* Find protocol: initial substring until "://" */ + colon = strchr (uri_string, ':'); + if (colon && !strncmp (colon, "://", 3)) { + g_uri->protocol = g_strndup (uri_string, colon - uri_string); + uri_string = colon + 3; + } else + g_uri->protocol = NULL; + + /* If there is an @ sign, look for user, authmech, and + * password before it. + */ + at = strchr (uri_string, '@'); + if (at) { + colon = strchr (uri_string, ':'); + if (colon && colon < at) + g_uri->passwd = g_strndup (colon + 1, at - colon - 1); + else { + g_uri->passwd = NULL; + colon = at; + } + + semi = strchr(uri_string, ';'); + if (semi && semi < colon && !strncasecmp (semi, ";auth=", 6)) + g_uri->authmech = g_strndup (semi + 6, colon - semi - 6); + else { + g_uri->authmech = NULL; + semi = colon; + } + + g_uri->user = g_strndup (uri_string, semi - uri_string); + uri_string = at + 1; + } else + g_uri->user = g_uri->passwd = g_uri->authmech = NULL; + + /* Find host (required) and port. */ + slash = strchr (uri_string, '/'); + colon = strchr (uri_string, ':'); + if (slash && colon > slash) + colon = 0; + + if (colon) { + g_uri->host = g_strndup (uri_string, colon - uri_string); + if (slash) + g_uri->port = atoi(colon + 1); + else + g_uri->port = atoi(colon + 1); + } else if (slash) { + g_uri->host = g_strndup (uri_string, slash - uri_string); + g_uri->port = -1; + } else { + g_uri->host = g_strdup (uri_string); + g_uri->port = -1; + } + + /* setup a fallback, if relative, then empty string, else + it will be from root */ + if (slash == NULL) { + slash = "/"; + } + if (slash && *slash && g_uri->protocol == NULL) + slash++; + + split = g_strsplit(slash, " ", 0); + path = g_strjoinv("%20", split); + g_strfreev(split); + + g_uri->path = path; + + return g_uri; +} + +gchar * +soup_uri_to_string (const SoupUri *uri, gboolean show_passwd) +{ + if (uri->port != -1) + return g_strdup_printf( + "%s%s%s%s%s%s%s%s%s:%d%s", + uri->protocol ? uri->protocol : "", + uri->protocol ? "://" : "", + uri->user ? uri->user : "", + uri->authmech ? ";auth=" : "", + uri->authmech ? uri->authmech : "", + uri->passwd && show_passwd ? ":" : "", + uri->passwd && show_passwd ? uri->passwd : "", + uri->user ? "@" : "", + uri->host, + uri->port, + uri->path ? uri->path : ""); + else + return g_strdup_printf( + "%s%s%s%s%s%s%s%s%s%s", + uri->protocol ? uri->protocol : "", + uri->protocol ? "://" : "", + uri->user ? uri->user : "", + uri->authmech ? ";auth=" : "", + uri->authmech ? uri->authmech : "", + uri->passwd && show_passwd ? ":" : "", + uri->passwd && show_passwd ? uri->passwd : "", + uri->user ? "@" : "", + uri->host, + uri->path ? uri->path : ""); +} + +void +soup_uri_free (SoupUri *uri) +{ + g_assert (uri); + + g_free (uri->protocol); + g_free (uri->user); + g_free (uri->authmech); + g_free (uri->passwd); + g_free (uri->host); + g_free (uri->path); + + g_free (uri); +} diff --git a/libsoup/soup-uri.h b/libsoup/soup-uri.h new file mode 100644 index 0000000..a37b684 --- /dev/null +++ b/libsoup/soup-uri.h @@ -0,0 +1,63 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* url-util.h : utility functions to parse URLs */ + +/* + * Author : + * Bertrand Guiheneuf + * + * Copyright 1999, 2000 HelixCode (http://www.helixcode.com) + * + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License as + * published by the Free Software Foundation; either version 2 of the + * License, or (at your option) any later version. + * + * This program 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 General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 + * USA + */ + + +#ifndef SOUP_URI_H +#define SOUP_URI_H 1 + +#include + +#ifdef __cplusplus +extern "C" { +#pragma } +#endif /* __cplusplus */ + +typedef struct { + gchar *protocol; + gchar *user; + gchar *authmech; + gchar *passwd; + gchar *host; + int port; + gchar *path; +} SoupUri; + +/* the cache system has been disabled because it would + need the user to use accessors instead of modifying the + structure field. As the speed is not so important here, + I chose not to use it */ + +SoupUri *soup_uri_new (const gchar *uri_string); + +gchar *soup_uri_to_string (const SoupUri *uri, gboolean show_password); + +void soup_uri_free (SoupUri *uri); + +#ifdef __cplusplus +} +#endif /* __cplusplus */ + + +#endif /*SOUP_URI_H*/ diff --git a/libsoup/soup.h b/libsoup/soup.h new file mode 100644 index 0000000..d451e19 --- /dev/null +++ b/libsoup/soup.h @@ -0,0 +1,20 @@ +/* -*- Mode: C; tab-width: 8; indent-tabs-mode: t; c-basic-offset: 8 -*- */ +/* + * soup-queue.h: Asyncronous Callback-based SOAP Request Queue. + * + * Authors: + * Alex Graveley (alex@helixcode.com) + * + * Copyright (C) 2000, Helix Code, Inc. + */ + +#ifndef SOUP_H +#define SOUP_H 1 + +#include "soup-queue.h" +#include "soup-context.h" +#include "soup-request.h" +#include "soup-header.h" +#include "soup-uri.h" + +#endif /*SOUP_H*/ -- 2.7.4