📄 cache.c
字号:
/* * File: cache.c * * Copyright 2000, 2001, 2002, 2003, 2004 Jorge Arellano Cid <jcid@dillo.org> * * 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. *//* * Dillo's cache module */#include <ctype.h> /* for tolower */#include <sys/types.h>#include <sys/stat.h>#include <stdlib.h>#include <string.h>#include <fcntl.h>#include <unistd.h>#include "msg.h"#include "list.h"#include "IO/Url.h"#include "IO/IO.h"#include "web.h"#include "dicache.h"#include "interface.h"#include "nav.h"#include "cookies.h"#include "misc.h"#define NULLKey 0#define DEBUG_LEVEL 5#include "debug.h"/* * Local data types */typedef struct { const DilloUrl *Url; /* Cached Url. Url is used as a primary Key */ const char *Type; /* MIME type string */ GString *Header; /* HTTP header */ const DilloUrl *Location; /* New URI for redirects */ void *Data; /* Pointer to raw data */ size_t ValidSize, /* Actually size of valid range */ TotalSize, /* Goal size of the whole data (0 if unknown) */ BuffSize; /* Buffer Size for unknown length transfers */ guint Flags; /* Look Flag Defines in cache.h */ IOData_t *io; /* Pointer to IO data */ ChainLink *CCCQuery; /* CCC link for querying branch */ ChainLink *CCCAnswer; /* CCC link for answering branch */} CacheData_t;/* * Local data *//* A hash for cached data. Holds pointers to CacheData_t structs */static GHashTable *CacheHash = NULL;/* A list for cache clients. * Although implemented as a list, we'll call it ClientQueue --Jcid */static GSList *ClientQueue = NULL;/* A list for delayed clients (it holds weak pointers to cache entries, * which are used to make deferred calls to Cache_process_queue) */static GSList *DelayedQueue = NULL;static guint DelayedQueueIdleId = 0;/* * Forward declarations */static void Cache_process_queue(CacheData_t *entry);static void Cache_delayed_process_queue(CacheData_t *entry);static void Cache_stop_client(gint Key, gint force);/* * Determine if two hash entries are equal (used by GHashTable) */static gint Cache_hash_entry_equal(gconstpointer v1, gconstpointer v2){ return (!a_Url_cmp((DilloUrl *)v1, (DilloUrl *)v2));}/* * Determine the hash value given the key (used by GHashTable) */static guint Cache_url_hash(gconstpointer key){ const char *p = URL_STR((DilloUrl *)key); guint h = *p; if (h) for (p += 1; *p != '\0' && *p != '#'; p++) h = (h << 5) - h + *p; return h;}/* * Initialize dicache data */void a_Cache_init(void){ CacheHash = g_hash_table_new(Cache_url_hash, Cache_hash_entry_equal);}/* Client operations ------------------------------------------------------ *//* * Make a unique primary-key for cache clients */static gint Cache_client_make_key(void){ static gint ClientKey = 0; /* Provide a primary key for each client */ if ( ++ClientKey < 0 ) ClientKey = 1; return ClientKey;}/* * Add a client to ClientQueue. * - Every client-camp is just a reference (except 'Web'). * - Return a unique number for identifying the client. */static gint Cache_client_enqueue(const DilloUrl *Url, DilloWeb *Web, CA_Callback_t Callback, void *CbData){ gint ClientKey; CacheClient_t *NewClient; NewClient = g_new(CacheClient_t, 1); ClientKey = Cache_client_make_key(); NewClient->Key = ClientKey; NewClient->Url = Url; NewClient->Buf = NULL; NewClient->Callback = Callback; NewClient->CbData = CbData; NewClient->Web = Web; ClientQueue = g_slist_append(ClientQueue, NewClient); return ClientKey;}/* * Compare function for searching a Client by its key */static gint Cache_client_key_cmp(gconstpointer client, gconstpointer key){ return ( GPOINTER_TO_INT(key) != ((CacheClient_t *)client)->Key );}/* * Compare function for searching a Client by its URL */static gint Cache_client_url_cmp(gconstpointer client, gconstpointer url){ return a_Url_cmp((DilloUrl *)url, ((CacheClient_t *)client)->Url);}/* * Compare function for searching a Client by hostname */static gint Cache_client_host_cmp(gconstpointer client, gconstpointer hostname){ return g_strcasecmp(URL_HOST(((CacheClient_t *)client)->Url), (gchar *)hostname );}/* * Remove a client from the queue */static void Cache_client_dequeue(CacheClient_t *Client, gint Key){ GSList *List; if (!Client && (List = g_slist_find_custom(ClientQueue, GINT_TO_POINTER(Key), Cache_client_key_cmp))) Client = List->data; if ( Client ) { ClientQueue = g_slist_remove(ClientQueue, Client); a_Web_free(Client->Web); g_free(Client); }}/* Entry operations ------------------------------------------------------- *//* * Set safe values for a new cache entry */static void Cache_entry_init(CacheData_t *NewEntry, const DilloUrl *Url){ NewEntry->Url = a_Url_dup(Url); NewEntry->Type = NULL; NewEntry->Header = g_string_new(""); NewEntry->Location = NULL; NewEntry->Data = NULL; NewEntry->ValidSize = 0; NewEntry->TotalSize = 0; NewEntry->BuffSize = 4096; NewEntry->Flags = 0; NewEntry->io = NULL; NewEntry->CCCQuery = a_Chain_new(); NewEntry->CCCAnswer = NULL;}/* * Get the data structure for a cached URL (using 'Url' as the search key) * If 'Url' isn't cached, return NULL */static CacheData_t *Cache_entry_search(const DilloUrl *Url){ return g_hash_table_lookup(CacheHash, Url);}/* * Allocate and set a new entry in the cache list */static CacheData_t *Cache_entry_add(const DilloUrl *Url){ CacheData_t *new_entry = g_new(CacheData_t, 1); if (Cache_entry_search(Url)) DEBUG_MSG(5, "WARNING: Cache_entry_add, leaking an entry.\n"); Cache_entry_init(new_entry, Url); /* Set safe values */ g_hash_table_insert(CacheHash, (gpointer)new_entry->Url, new_entry); return new_entry;}/* * Free the components of a CacheData_t struct. */static void Cache_entry_free(CacheData_t *entry){ a_Url_free((DilloUrl *)entry->Url); g_free((gchar *)entry->Type); g_string_free(entry->Header, TRUE); a_Url_free((DilloUrl *)entry->Location); g_free(entry->Data); g_free(entry); /* CCCQuery and CCCAnswer are just references */}/* * Remove an entry from the CacheList (no CCC-function is called) */static gint Cache_entry_remove_raw(CacheData_t *entry, DilloUrl *url){ if (!entry && !(entry = Cache_entry_search(url))) return 0; /* There MUST NOT be any clients */ g_return_val_if_fail( !g_slist_find_custom(ClientQueue, url, Cache_client_url_cmp), 0); /* remove from DelayedQueue */ DelayedQueue = g_slist_remove(DelayedQueue, entry); /* remove from dicache */ a_Dicache_invalidate_entry(url); /* remove from cache */ g_hash_table_remove(CacheHash, entry->Url); Cache_entry_free(entry); return 1;}/* * Remove an entry, using the CCC if necessary. * (entry SHOULD NOT have clients) */static void Cache_entry_remove(CacheData_t *entry, DilloUrl *url){ ChainLink *InfoQuery, *InfoAnswer; if (!entry && !(entry = Cache_entry_search(url))) return; InfoQuery = entry->CCCQuery; InfoAnswer = entry->CCCAnswer; if (InfoQuery) { DEBUG_MSG(2, "## Aborting CCCQuery\n"); a_Cache_ccc(OpAbort, 1, BCK, InfoQuery, NULL, NULL); } else if (InfoAnswer) { DEBUG_MSG(2, "## Aborting CCCAnswer\n"); a_Cache_ccc(OpAbort, 2, BCK, InfoAnswer, NULL, NULL); } else { DEBUG_MSG(2, "## Aborting raw2\n"); Cache_entry_remove_raw(entry, url); }}/* Misc. operations ------------------------------------------------------- *//* * Given an entry (url), remove all its clients (by url or host). */static void Cache_stop_clients(DilloUrl *url, gint ByHost){ gint i; CacheClient_t *Client; for (i = 0; (Client = g_slist_nth_data(ClientQueue, i)); ++i) { if ( (ByHost && Cache_client_host_cmp(Client, URL_HOST(url)) == 0) || (!ByHost && Cache_client_url_cmp(Client, url) == 0) ) { Cache_stop_client(Client->Key, 0); --i; } }}/* * Prepare a reload by stopping clients and removing the entry * Return value: 1 if on success, 0 otherwise */static gint Cache_prepare_reload(DilloUrl *url){ CacheClient_t *Client; DilloWeb *ClientWeb; gint i; for (i = 0; (Client = g_slist_nth_data(ClientQueue, i)); ++i){ if (Cache_client_url_cmp(Client, url) == 0 && (ClientWeb = Client->Web) && !(ClientWeb->flags & WEB_Download)) Cache_stop_client(Client->Key, 0); } if (!g_slist_find_custom(ClientQueue, url, Cache_client_url_cmp)) { /* There're no clients for this entry */ DEBUG_MSG(2, "## No more clients for this entry\n"); Cache_entry_remove(NULL, url); return 1; } else { MSG("Cache_prepare_reload: ERROR, entry still has clients\n"); } return 0;}/* * Try finding the url in the cache. If it hits, send the cache contents * from there. If it misses, set up a new connection. * Return value: A primary key for identifying the client. */static gint Cache_open_url(DilloWeb *Web, CA_Callback_t Call, void *CbData){ void *link; gint ClientKey; ChainFunction_t cccFunct; DilloUrl *Url = Web->url; CacheData_t *entry = Cache_entry_search(Url); _MSG("Cache_open_url: %s %sFOUND\n", URL_STR(Url), entry ? "" : "NOT "); if ( entry ) { /* URL is cached: feed our client with cached data */ ClientKey = Cache_client_enqueue(entry->Url, Web, Call, CbData); Cache_delayed_process_queue(entry); } else { /* URL not cached: create an entry, send our client to the queue, * and open a new connection */
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -