* 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).
## Process this file with automake to produce Makefile.in
-SUBDIRS = src
+SUBDIRS = src tests
EXTRA_DIST = \
autogen.sh
AC_OUTPUT([
Makefile
src/Makefile
+ tests/Makefile
])
#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;
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;
}
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;
}
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;
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);
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) {
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 =
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 {
g_free (data);
}
+
+gchar *
+soup_context_get_uri (SoupContext *ctx)
+{
+ return soup_uri_to_string (ctx->uri, TRUE);
+}
#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,
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*/
* 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;
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;
+}
#define SOAP_PRIVATE_H 1
#include "soup-queue.h"
+#include "soup-uri.h"
#ifdef __cplusplus
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;
SOUP_PROTOCOL_SMTP
} SoupProtocol;
-struct _SoupContextPrivate {
+struct _SoupContext {
+ SoupUri *uri;
SoupServer *server;
guint refcnt;
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
#include "soup-misc.h"
#include "soup-private.h"
-GSList *active_requests = NULL;
+GSList *soup_active_requests = NULL;
static guint soup_queue_idle_tag = 0;
{
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;
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",
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;
}
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;
}
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;
};
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);
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. */
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);
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,
static gboolean
soup_idle_handle_new_requests (gpointer unused)
{
- GSList *iter = active_requests;
+ GSList *iter = soup_active_requests;
while (iter) {
SoupRequest *req = iter->data;
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;
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
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);
}
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
#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);
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
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;
}
#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