From: alex Date: Wed, 13 Dec 2000 07:28:42 +0000 (+0000) Subject: Beginnings of test-suite added. X-Git-Tag: SOUP_0_4_1~164 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=802764bbc006b3eea86131b798b288d367b8ebc5;p=platform%2Fupstream%2Flibsoup.git Beginnings of test-suite added. * 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). --- diff --git a/Makefile.am b/Makefile.am index 45d5428..8a5aabe 100644 --- a/Makefile.am +++ b/Makefile.am @@ -1,6 +1,6 @@ ## Process this file with automake to produce Makefile.in -SUBDIRS = src +SUBDIRS = src tests EXTRA_DIST = \ autogen.sh diff --git a/configure.in b/configure.in index 889059f..09d8451 100644 --- a/configure.in +++ b/configure.in @@ -161,5 +161,6 @@ fi AC_OUTPUT([ Makefile src/Makefile + tests/Makefile ]) diff --git a/libsoup/soup-context.c b/libsoup/soup-context.c index 46e87ac..a5bd231 100644 --- a/libsoup/soup-context.c +++ b/libsoup/soup-context.c @@ -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); +} diff --git a/libsoup/soup-context.h b/libsoup/soup-context.h index a6aa0a8..0a746c6 100644 --- a/libsoup/soup-context.h +++ b/libsoup/soup-context.h @@ -14,14 +14,7 @@ #include #include -#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*/ diff --git a/libsoup/soup-misc.c b/libsoup/soup-misc.c index 31d19f9..1dfd53a 100644 --- a/libsoup/soup-misc.c +++ b/libsoup/soup-misc.c @@ -8,9 +8,12 @@ * Copyright (C) 2000, Helix Code, Inc. */ +#include + #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; +} diff --git a/libsoup/soup-private.h b/libsoup/soup-private.h index 645fd44..030d89f 100644 --- a/libsoup/soup-private.h +++ b/libsoup/soup-private.h @@ -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 diff --git a/libsoup/soup-queue.c b/libsoup/soup-queue.c index fcd23bf..ab54804 100644 --- a/libsoup/soup-queue.c +++ b/libsoup/soup-queue.c @@ -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); } diff --git a/libsoup/soup-queue.h b/libsoup/soup-queue.h index cc9fa6c..ce12079 100644 --- a/libsoup/soup-queue.h +++ b/libsoup/soup-queue.h @@ -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 diff --git a/libsoup/soup-uri.c b/libsoup/soup-uri.c index 31a2aca..911f058 100644 --- a/libsoup/soup-uri.c +++ b/libsoup/soup-uri.c @@ -41,31 +41,50 @@ #include #include + #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; } diff --git a/libsoup/soup-uri.h b/libsoup/soup-uri.h index 051d08a..cef8c0d 100644 --- a/libsoup/soup-uri.h +++ b/libsoup/soup-uri.h @@ -30,16 +30,17 @@ #include 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