diff options
author | alex <alex> | 2000-12-06 22:28:48 +0000 |
---|---|---|
committer | alex <alex> | 2000-12-06 22:28:48 +0000 |
commit | ee40cef78df148f9f96978e0be9e591edfa5853d (patch) | |
tree | 0ca7a10fffde54a3756c220a1707f84c1f4d7116 | |
download | libsoup-ee40cef78df148f9f96978e0be9e591edfa5853d.tar.gz |
Initial version
-rw-r--r-- | AUTHORS | 5 | ||||
-rw-r--r-- | ChangeLog | 0 | ||||
-rw-r--r-- | Makefile.am | 28 | ||||
-rw-r--r-- | NEWS | 0 | ||||
-rw-r--r-- | README | 0 | ||||
-rwxr-xr-x | autogen.sh | 148 | ||||
-rw-r--r-- | configure.in | 179 | ||||
-rw-r--r-- | libsoup/Makefile.am | 29 | ||||
-rw-r--r-- | libsoup/soup-context.c | 182 | ||||
-rw-r--r-- | libsoup/soup-context.h | 52 | ||||
-rw-r--r-- | libsoup/soup-private.h | 65 | ||||
-rw-r--r-- | libsoup/soup-queue.c | 236 | ||||
-rw-r--r-- | libsoup/soup-queue.h | 53 | ||||
-rw-r--r-- | libsoup/soup-uri.c | 189 | ||||
-rw-r--r-- | libsoup/soup-uri.h | 63 | ||||
-rw-r--r-- | libsoup/soup.h | 20 |
16 files changed, 1249 insertions, 0 deletions
diff --git a/AUTHORS b/AUTHORS new file mode 100644 index 00000000..1139aa67 --- /dev/null +++ b/AUTHORS @@ -0,0 +1,5 @@ +SOUP Authors +============ + +Alex Graveley <alex@helixcode.com> +Miguel De Icaza <miguel@helixcode.com> diff --git a/ChangeLog b/ChangeLog new file mode 100644 index 00000000..e69de29b --- /dev/null +++ b/ChangeLog diff --git a/Makefile.am b/Makefile.am new file mode 100644 index 00000000..45d5428d --- /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/autogen.sh b/autogen.sh new file mode 100755 index 00000000..293b7972 --- /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 00000000..33cce22d --- /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 00000000..052135c3 --- /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 00000000..6353f1bb --- /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 <gnet/gnet.h> + +#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 00000000..c1f95e6a --- /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 <glib.h> + +#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 00000000..be3b7f4e --- /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 00000000..7319a389 --- /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 <glib.h> +#include <gnet/gnet.h> + +#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 00000000..1b6664a9 --- /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 <glib.h> + +#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 00000000..31a2aca0 --- /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 <bertrand@helixcode.com> + * Dan Winship <danw@helixcode.com> + * + * 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 <string.h> +#include <stdlib.h> +#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 00000000..a37b684c --- /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 <bertrand@helixcode.com> + * + * 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 <glib.h> + +#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 00000000..d451e191 --- /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*/ |