Beginnings of test-suite added.
authoralex <alex>
Wed, 13 Dec 2000 07:28:42 +0000 (07:28 +0000)
committeralex <alex>
Wed, 13 Dec 2000 07:28:42 +0000 (07:28 +0000)
* Beginnings of test-suite added.

* Made SoupContext opaque. Removed SoupContextPrivate. Added soup_context_get_uri() to get the uri string for a given context.

* Added a response_headers hashtable to SoupRequest so the callback can do whatever it wants with passed headers. All entries in this hashtable are just parsed strings from req->priv->recv_buf, so no new strings are allocated.

* Renamed custom_headers to request_headers

* Fixed context creation logic

* Made soup_servers hashtable use case insensitive hostname matching.

* Removed SOUP_ERROR_URI_NOT_FOUND, SOUP_ERROR_URI_NOT_PERMITTED, and SOUP_ERROR_URI_OBJECT_MOVED from SoupCallbackResult enum. Its up to the application to figure out all the different HTTP states. This may change however.

* Added querystring to SoupUri, so that contexts can be cached based only on path.

* Added default port logic to SoupUri. Known protocols are https (port 443), http (80), smtp/mailto (25), and ftp (20).

Makefile.am
configure.in
libsoup/soup-context.c
libsoup/soup-context.h
libsoup/soup-misc.c
libsoup/soup-private.h
libsoup/soup-queue.c
libsoup/soup-queue.h
libsoup/soup-uri.c
libsoup/soup-uri.h

index 45d5428..8a5aabe 100644 (file)
@@ -1,6 +1,6 @@
 ## Process this file with automake to produce Makefile.in
 
-SUBDIRS = src
+SUBDIRS = src tests
 
 EXTRA_DIST = \
        autogen.sh
index 889059f..09d8451 100644 (file)
@@ -161,5 +161,6 @@ fi
 AC_OUTPUT([
        Makefile
        src/Makefile
+       tests/Makefile
 ])
 
index 46e87ac..a5bd231 100644 (file)
@@ -16,9 +16,9 @@
 #include "soup-misc.h"
 #include "soup-uri.h"
 
-gint connection_count = 0;
+GHashTable *soup_servers;  /* KEY: hostname, VALUE: SoupServer */
 
-GHashTable *servers;  /* KEY: hostname, VALUE: SoupServer */
+static gint connection_count = 0;
 
 static guint most_recently_used_id = 0;
 
@@ -26,9 +26,8 @@ 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->server = server;
+       ctx->keep_alive = TRUE;
        ctx->uri = uri;
        return ctx;
 }
@@ -36,33 +35,33 @@ soup_context_new (SoupServer *server, SoupUri *uri)
 SoupContext *
 soup_context_get (gchar *uri) 
 {
-       SoupServer *serv;
+       SoupServer *serv = NULL;
        SoupContext *ret = NULL;
        SoupUri *suri = soup_uri_new (uri);
 
-       if (!servers)
-               servers = g_hash_table_new (g_str_hash, g_str_equal);
+       if (!soup_servers)
+               soup_servers = g_hash_table_new (soup_str_case_hash, 
+                                                soup_str_case_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_hash_table_lookup (soup_servers, suri->host);
+
+       if (!serv) {
                serv = g_new0 (SoupServer, 1);
                serv->host = g_strdup (suri->host);
-               g_hash_table_insert (servers, suri->host, serv);
+               g_hash_table_insert (soup_servers, suri->host, serv);
        }
 
-       if (ret) return ret;
+       if (!serv->contexts)
+               serv->contexts = g_hash_table_new (g_str_hash, g_str_equal);
+       else
+               ret = g_hash_table_lookup (serv->contexts, suri->path);
 
-       ret = soup_context_new (serv, suri);
-       soup_context_ref (ret);
+       if (!ret) {
+               ret = soup_context_new (serv, suri);
+               g_hash_table_insert (serv->contexts, suri->path, ret);
+       }
 
-       g_hash_table_insert (serv->contexts, suri->path, ret);
+       soup_context_ref (ret);
 
        return ret;
 }
@@ -70,21 +69,21 @@ soup_context_get (gchar *uri)
 void
 soup_context_ref (SoupContext *ctx)
 {
-       ctx->priv->refcnt++;
+       ctx->refcnt++;
 }
 
 void
 soup_context_unref (SoupContext *ctx)
 {
-       if (ctx->priv->refcnt-- == 0) {
-               SoupServer *serv = ctx->priv->server;
+       if (ctx->refcnt-- == 0) {
+               SoupServer *serv = ctx->server;
 
                g_hash_table_remove (serv->contexts, ctx->uri->path);
 
                if (g_hash_table_size (serv->contexts) == 0) {
                        GSList *conns = serv->connections;
 
-                       g_hash_table_remove (servers, serv->host);
+                       g_hash_table_remove (soup_servers, serv->host);
                        
                        while (conns) {
                                SoupConnection *conn = conns->data;
@@ -140,8 +139,8 @@ soup_context_connect_cb (GTcpSocket                   *socket,
 
                connection_count++;
 
-               ctx->priv->server->connections = 
-                       g_slist_prepend (ctx->priv->server->connections, 
+               ctx->server->connections = 
+                       g_slist_prepend (ctx->server->connections, 
                                         new_conn);
 
                (*cb) (ctx, SOUP_CONNECT_ERROR_NONE, socket, cb_data); 
@@ -158,9 +157,9 @@ soup_context_connect_cb (GTcpSocket                   *socket,
 static SoupConnection *
 soup_try_existing_connections (SoupContext *ctx)
 {
-       GSList *conns = ctx->priv->server->connections;
+       GSList *conns = ctx->server->connections;
 
-       if (!ctx->priv->keep_alive)
+       if (!ctx->keep_alive)
                return NULL;
 
        while (conns) {
@@ -207,7 +206,7 @@ soup_prune_least_used_connection (void)
        last.serv = NULL;
        last.conn = NULL;
 
-       g_hash_table_foreach (servers, (GHFunc) soup_prune_foreach, &last);
+       g_hash_table_foreach (soup_servers, (GHFunc) soup_prune_foreach, &last);
 
        if (last.conn) {
                last.serv->connections = 
@@ -282,14 +281,14 @@ void
 soup_context_release_connection (SoupContext       *ctx,
                                 GTcpSocket        *socket)
 {
-       SoupServer *server = ctx->priv->server;
+       SoupServer *server = ctx->server;
        GSList *conns = server->connections;
 
        while (conns) {
                SoupConnection *conn = conns->data;
 
                if (conn->socket == socket) {
-                       if (ctx->priv->keep_alive) {
+                       if (ctx->keep_alive) {
                                conn->last_used_id = ++most_recently_used_id;
                                conn->in_use = FALSE;
                        } else {
@@ -322,3 +321,9 @@ soup_context_cancel_connect (SoupConnectId tag)
 
        g_free (data);
 }
+
+gchar *
+soup_context_get_uri (SoupContext *ctx)
+{
+       return soup_uri_to_string (ctx->uri, TRUE);
+}
index a6aa0a8..0a746c6 100644 (file)
 #include <glib.h>
 #include <gnet/gnet.h>
 
-#include "soup-uri.h"
-
-typedef struct _SoupContextPrivate SoupContextPrivate;
-
-typedef struct {
-       SoupContextPrivate *priv;
-       SoupUri            *uri;
-} SoupContext;
+typedef struct _SoupContext SoupContext;
 
 typedef enum {
        SOUP_CONNECT_ERROR_NONE,
@@ -36,19 +29,21 @@ typedef void (*SoupConnectCallbackFn) (SoupContext          *ctx,
 
 typedef gpointer SoupConnectId;
 
-SoupContext  *soup_context_get                (gchar               *uri);
+SoupContext  *soup_context_get                (gchar                *uri);
 
-void          soup_context_ref                (SoupContext         *ctx);
+void          soup_context_ref                (SoupContext          *ctx);
 
-void          soup_context_unref              (SoupContext         *ctx);
+void          soup_context_unref              (SoupContext          *ctx);
 
 SoupConnectId soup_context_get_connection     (SoupContext          *ctx,
                                               SoupConnectCallbackFn cb,
                                               gpointer              user_data);
 
-void          soup_context_release_connection (SoupContext         *ctx,
-                                              GTcpSocket          *socket);
+void          soup_context_release_connection (SoupContext          *ctx,
+                                              GTcpSocket           *socket);
+
+void          soup_context_cancel_connect     (SoupConnectId         tag);
 
-void          soup_context_cancel_connect     (SoupConnectId        tag);
+gchar        *soup_context_get_uri            (SoupContext          *ctx);
 
 #endif /*SOUP_CONTEXT_H*/
index 31d19f9..1dfd53a 100644 (file)
@@ -8,9 +8,12 @@
  * Copyright (C) 2000, Helix Code, Inc.
  */
 
+#include <ctype.h>
+
 #include "soup-misc.h"
+#include "soup-private.h"
 
-gint max_connections = -1;
+static gint max_connections = -1;
 
 static SoupContext *proxy_context;
 
@@ -42,3 +45,26 @@ soup_get_connection_limit (void)
        return max_connections;
 }
 
+
+guint
+soup_str_case_hash (gconstpointer key)
+{
+       const char *p = key;
+       guint h = toupper(*p);
+       
+       if (h)
+               for (p += 1; *p != '\0'; p++)
+                       h = (h << 5) - h + toupper(*p);
+       
+       return h;
+}
+
+gboolean
+soup_str_case_equal (gconstpointer v1,
+                    gconstpointer v2)
+{
+       const gchar *string1 = v1;
+       const gchar *string2 = v2;
+       
+       return g_strcasecmp (string1, string2) == 0;
+}
index 645fd44..030d89f 100644 (file)
@@ -17,6 +17,7 @@
 #define SOAP_PRIVATE_H 1
 
 #include "soup-queue.h"
+#include "soup-uri.h"
 
 #ifdef __cplusplus
 extern "C" {
@@ -24,9 +25,8 @@ extern "C" {
 
 #define RESPONSE_BLOCK_SIZE 8192
 
-extern gint        connection_count;
-extern GSList     *active_requests;   /* CONTAINS: SoupRequest */
-extern GHashTable *servers;           /* KEY: uri->host, VALUE: SoupServer */
+extern GSList     *soup_active_requests; /* CONTAINS: SoupRequest */
+extern GHashTable *soup_servers;         /* KEY: uri->host, VALUE: SoupServer */
 
 typedef struct {
        GTcpSocket *socket;
@@ -47,7 +47,8 @@ typedef enum {
        SOUP_PROTOCOL_SMTP
 } SoupProtocol;
 
-struct _SoupContextPrivate {
+struct _SoupContext {
+       SoupUri      *uri;
        SoupServer   *server;
        guint         refcnt;
 
@@ -78,6 +79,11 @@ struct _SoupRequestPrivate {
 SoupCallbackResult soup_request_issue_callback (SoupRequest   *req, 
                                                SoupErrorCode  error);
 
+guint              soup_str_case_hash          (gconstpointer key);
+
+gboolean           soup_str_case_equal         (gconstpointer v1,
+                                               gconstpointer v2);
+
 #ifdef __cplusplus
 }
 #endif
index fcd23bf..ab54804 100644 (file)
@@ -31,7 +31,7 @@
 #include "soup-misc.h"
 #include "soup-private.h"
 
-GSList *active_requests = NULL;
+GSList *soup_active_requests = NULL;
 
 static guint soup_queue_idle_tag = 0;
 
@@ -40,7 +40,7 @@ soup_substring_index (gchar *str, gint len, gchar *substr)
 {
        int i, sublen = strlen (substr);
        
-       for (i = 0; i < len; ++i)
+       for (i = 0; i < len - sublen; ++i)
                if (str[i] == substr[0])
                        if (memcmp (&str[i], substr, sublen) == 0)
                                return i;
@@ -48,19 +48,31 @@ soup_substring_index (gchar *str, gint len, gchar *substr)
        return -1;
 }
 
-static inline gchar**
-soup_split_headers (gchar *str, guint len)
+static void
+soup_parse_headers (GHashTable *hash, gchar *str)
 {
-       return NULL;
+       gchar *idx, *idx2;
+
+       while ((idx = strstr (str, "\r\n"))) {
+               idx2 = strstr (idx, ": ");
+
+               if (idx2) {
+                       *idx2 = '\0';
+                       g_hash_table_insert (hash, idx, idx2);
+               } else
+                       return;
+
+               *idx = '\0';
+               str = idx++;
+       }
 }
 
 /* returns TRUE to continue processing, FALSE if a callback was issued */
 static gboolean 
 soup_process_headers (SoupRequest *req, gchar *str, guint len)
 {
-       gchar **headers, *header;
-       gchar reason_phrase[512];
-       gint http_major, http_minor, status_code, read_count, index;
+       gchar reason_phrase [512];
+       gint http_major, http_minor, status_code, read_count;
 
        read_count = sscanf (str, 
                             "HTTP/%d.%d %u %512s\r\n", 
@@ -77,10 +89,13 @@ soup_process_headers (SoupRequest *req, gchar *str, guint len)
                return FALSE;
        }
 
-       index = soup_substring_index (str, len, "\r\n");
+       if (req->response_headers) 
+               g_hash_table_destroy (req->response_headers);
+
+       req->response_headers = g_hash_table_new (soup_str_case_hash, 
+                                                 soup_str_case_equal);
 
-       headers = g_strsplit (str, "\r\n", 0);
-       g_strfreev (headers);
+       soup_parse_headers (req->response_headers, str);
 
        return TRUE;
 }
@@ -116,24 +131,24 @@ soup_queue_read_async (GIOChannel* iochannel,
           was successful */
 
        if (bytes_read == 0) {
-               index = soup_substring_index (req->priv->recv_buf->data, 
-                                             req->priv->recv_buf->len,
+               GByteArray *recv_buf = req->priv->recv_buf;
+
+               index = soup_substring_index (recv_buf->data, 
+                                             recv_buf->len,
                                              "\r\n\r\n");
 
-               req->response.length = req->priv->recv_buf->len - index + 4;
-               req->response.body = 
-                       g_memdup (&req->priv->recv_buf->data [index + 4],
-                                 req->response.length + 1);
+               req->response.length = recv_buf->len - index + 4;
+               req->response.body = g_memdup (&recv_buf->data [index + 4],
+                                              req->response.length + 1);
                req->response.body [req->response.length] = '\0';
 
-               g_byte_array_free (req->priv->recv_buf, TRUE);
-               req->priv->recv_buf = NULL;
+               recv_buf->len = index + 2;
+               recv_buf->data = g_realloc (recv_buf->data, recv_buf->len + 1);
+               recv_buf->data [recv_buf->len + 1] = '\0';
                
                req->status = SOUP_STATUS_FINISHED;
 
-               if (soup_process_headers (req, 
-                                         req->priv->recv_buf->data,
-                                         index + 2))
+               if (soup_process_headers (req, recv_buf->data, recv_buf->len))
                        soup_request_issue_callback (req, SOUP_ERROR_NONE);
 
                return FALSE;
@@ -229,15 +244,15 @@ soup_encode_http_auth (gboolean proxy_auth, SoupUri *uri, GString *header)
 }
 
 struct SoupUsedHeaders {
-       gchar *host;
-       gchar *user_agent;
-       gchar *content_type;
-       gchar *charset;
-       gchar *content_length;
-       gchar *soapaction;
-       gchar *connection;
-       gchar *proxy_auth;
-       gchar *auth;
+       gchar  *host;
+       gchar  *user_agent;
+       gchar  *content_type;
+       gchar  *charset;
+       gchar  *content_length;
+       gchar  *soapaction;
+       gchar  *connection;
+       gchar  *proxy_auth;
+       gchar  *auth;
 
        GSList *custom_headers;
 };
@@ -292,8 +307,8 @@ soup_get_request_header (SoupRequest *req)
                NULL
        };
 
-       if (req->custom_headers) 
-               g_hash_table_foreach (req->custom_headers, 
+       if (req->request_headers) 
+               g_hash_table_foreach (req->request_headers, 
                                      (GHFunc) soup_check_used_headers,
                                      &hdrs);
 
@@ -305,7 +320,8 @@ soup_get_request_header (SoupRequest *req)
        if (proxy)
                uri = soup_uri_to_string (proxy->uri, FALSE);
        else 
-               uri = req->context->uri->path;
+               uri = g_strconcat (req->context->uri->path, 
+                                  req->context->uri->querystring);
 
        /* If we specify an absoluteURI in the request line, the 
           Host header MUST be ignored by the proxy. */
@@ -497,6 +513,8 @@ soup_queue_connect (SoupContext          *ctx,
        SoupRequest *req = user_data;
        GIOChannel *channel;
 
+       req->priv->connect_tag = NULL;
+
        switch (err) {
        case SOUP_CONNECT_ERROR_NONE:
                channel = gnet_tcp_socket_get_iochannel (socket);
@@ -505,7 +523,6 @@ soup_queue_connect (SoupContext          *ctx,
 
                req->status = SOUP_STATUS_SENDING_REQUEST;
                req->priv->socket = socket;
-               req->priv->connect_tag = NULL;
                req->priv->write_tag = 
                        g_io_add_watch (channel, 
                                        G_IO_OUT, 
@@ -527,7 +544,7 @@ soup_queue_connect (SoupContext          *ctx,
 static gboolean 
 soup_idle_handle_new_requests (gpointer unused)
 {
-        GSList *iter = active_requests;
+        GSList *iter = soup_active_requests;
        
        while (iter) {
                SoupRequest *req = iter->data;
@@ -567,6 +584,11 @@ soup_queue_request (SoupRequest    *req,
                req->response.length = 0;
        }
 
+       if (req->response_headers) {
+               g_hash_table_destroy (req->response_headers);
+               req->response_headers = NULL;
+       }
+
        if (req->priv->recv_buf) {
                g_byte_array_free (req->priv->recv_buf, TRUE);
                req->priv->recv_buf = NULL;
@@ -576,9 +598,7 @@ soup_queue_request (SoupRequest    *req,
        req->priv->user_data = user_data;
        req->status = SOUP_STATUS_QUEUED;
 
-       soup_context_ref (req->context);
-
-       active_requests = g_slist_append (active_requests, req);
+       soup_active_requests = g_slist_prepend (soup_active_requests, req);
 }
 
 void 
@@ -589,6 +609,6 @@ soup_queue_shutdown ()
        g_source_remove (soup_queue_idle_tag);
        soup_queue_idle_tag = 0;
 
-       for (iter = active_requests; iter; iter = iter->next)
+       for (iter = soup_active_requests; iter; iter = iter->next)
                soup_request_cancel (iter->data);
 }
index cc9fa6c..ce12079 100644 (file)
@@ -27,9 +27,6 @@ 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
index 31a2aca..911f058 100644 (file)
 
 #include <string.h>
 #include <stdlib.h>
+
 #include "soup-uri.h"
 
+static gint
+soup_uri_get_default_port (gchar *proto)
+{
+       gint len = strlen (proto);
+
+       if (strncasecmp (proto, "https", len) == 0)
+               return 443;
+       else if (strncasecmp (proto, "http", len) == 0)
+               return 80;
+       else if (strncasecmp (proto, "mailto", len) == 0 || 
+                strncasecmp (proto, "smtp", len) == 0)
+               return 25;
+       else if (strncasecmp (proto, "ftp", len) == 0)
+               return 21;
+       else
+               return -1;
+}
+
 /**
- * soup_uri_new: create a Gurl object from a string
+ * soup_uri_new: create a SoupUri 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
+ *   protocol://user;AUTH=mech:password@host:port/path?querystring
  * 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 SoupUri 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
+ * SoupUri member is NULL.
+ * Fields filled in the SoupUri structure are allocated
  * and url_string is not modified. 
  * 
- * Return value: a Gurl structure containing the URL items.
+ * Return value: a SoupUri structure containing the URL items.
  **/
 SoupUri *soup_uri_new (const gchar* uri_string)
 {
        SoupUri *g_uri;
-       char *semi, *colon, *at, *slash, *path;
+       char *semi, *colon, *at, *slash, *path, *query;
        char **split;
 
        g_uri = g_new (SoupUri,1);
@@ -118,10 +137,10 @@ SoupUri *soup_uri_new (const gchar* uri_string)
                        g_uri->port = atoi(colon + 1);
        } else if (slash) {
                g_uri->host = g_strndup (uri_string, slash - uri_string);
-               g_uri->port = -1;
+               g_uri->port = soup_uri_get_default_port (g_uri->protocol);
        } else {
                g_uri->host = g_strdup (uri_string);
-               g_uri->port = -1;
+               g_uri->port = soup_uri_get_default_port (g_uri->protocol);
        }
 
        /* setup a fallback, if relative, then empty string, else
@@ -136,7 +155,13 @@ SoupUri *soup_uri_new (const gchar* uri_string)
        path = g_strjoinv("%20", split);
        g_strfreev(split);
 
-       g_uri->path = path;
+       query = strchr (path, '?');
+
+       if (query) {
+               g_uri->path = g_strndup (path, query - path);
+               g_uri->querystring = g_strdup (++query);
+               g_free (path);
+       }
 
        return g_uri;
 }
index 051d08a..cef8c0d 100644 (file)
 #include <glib.h>
 
 typedef struct {
-       /* Changes here will only affect new connections: */
        gchar *protocol;
-       gchar *host;
-       int port;
 
-       /* Changes will be applied to existing and future connections: */
-       gchar *path;
        gchar *user;
        gchar *authmech;
        gchar *passwd;
+
+       gchar *host;
+       gint port;
+
+       gchar *path;
+       gchar *querystring;
 } SoupUri;
 
 /* the cache system has been disabled because it would