1 /* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 8; tab-width: 8 -*- */
2 /* http-cache.c - Property caching for DAV
4 Copyright (C) 2000-2001 Eazel, Inc.
6 The Gnome Library is free software; you can redistribute it and/or
7 modify it under the terms of the GNU Library General Public License as
8 published by the Free Software Foundation; either version 2 of the
9 License, or (at your option) any later version.
11 The Gnome Library is distributed in the hope that it will be useful,
12 but WITHOUT ANY WARRANTY; without even the implied warranty of
13 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 Library General Public License for more details.
16 You should have received a copy of the GNU Library General Public
17 License along with the Gnome Library; see the file COPYING.LIB. If not,
18 write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
19 Boston, MA 02111-1307, USA.
22 Michael Fleming <mfleming@eazel.com>
26 #include "http-cache.h"
28 #include "http-method.h"
29 #include <glib/ghash.h>
30 #include <glib/gstrfuncs.h>
31 #include <libgnomevfs/gnome-vfs-method.h>
32 #include <libgnomevfs/gnome-vfs-utils.h>
35 /* Cache file info for 5 minutes */
36 #define US_CACHE_FILE_INFO (1000 * 1000 * 60 * 5)
37 /* Cache directory listings for 500 ms */
38 #define US_CACHE_DIRECTORY (1000 * 500)
40 /* Mutex for cache data structures */
41 static GStaticRecMutex cache_rlock = G_STATIC_REC_MUTEX_INIT;
43 /* Hash maps char * URI ---> FileInfoCacheEntry */
44 static GHashTable * gl_file_info_cache = NULL;
45 /* in-order list of cache entries for expiration */
46 static GList * gl_file_info_cache_list = NULL;
47 static GList * gl_file_info_cache_list_last = NULL;
51 GnomeVFSFileInfo * file_info;
53 GList * my_list_node; /*node for me in gl_file_info_cache_list*/
54 GList * filenames; /* List of char * basenames for files that are in this
55 * collection/directory. Empty for non-directories
57 gboolean has_filenames:1;/* For directories, FALSE if the cache does not contain
58 * the directory's children
60 gboolean is_dav:1; /* Did this result from a PROPFIND or a GET ? */
63 static FileInfoCacheEntry * http_cache_entry_new ();
64 static void http_cache_entry_free (FileInfoCacheEntry * entry);
65 static FileInfoCacheEntry * http_cache_add_no_strdup (gchar *uri_string, GnomeVFSFileInfo *file_info, gboolean is_dav);
66 static FileInfoCacheEntry * http_cache_add (const gchar *uri_string, GnomeVFSFileInfo *file_info, gboolean is_dav);
69 http_cache_init (void)
71 gl_file_info_cache = g_hash_table_new (g_str_hash, g_str_equal);
75 http_cache_shutdown (void)
77 GList *node, *node_next;
79 g_static_rec_mutex_lock (&cache_rlock);
81 for ( node = g_list_first (gl_file_info_cache_list) ;
85 node_next = g_list_next (node);
86 http_cache_entry_free ((FileInfoCacheEntry*) node->data);
89 g_list_free (gl_file_info_cache_list);
91 g_hash_table_destroy (gl_file_info_cache);
93 g_static_rec_mutex_unlock (&cache_rlock);
97 static FileInfoCacheEntry *
98 http_cache_entry_new (void)
100 FileInfoCacheEntry *ret;
102 g_static_rec_mutex_lock (&cache_rlock);
104 ret = g_new0 (FileInfoCacheEntry, 1);
105 ret->create_time = http_util_get_utime();
107 gl_file_info_cache_list = g_list_prepend (gl_file_info_cache_list, ret);
109 /* Note that since we've prepended, gl_file_info_cache_list points to us*/
111 ret->my_list_node = gl_file_info_cache_list;
113 if (gl_file_info_cache_list_last == NULL) {
114 gl_file_info_cache_list_last = ret->my_list_node;
117 g_static_rec_mutex_unlock (&cache_rlock);
122 /* Warning: as this function removes the cache entry from gl_file_info_cache_list,
123 * callee's must be careful when calling this during a list iteration
126 http_cache_entry_free (FileInfoCacheEntry * entry)
131 g_static_rec_mutex_lock (&cache_rlock);
133 g_hash_table_remove (gl_file_info_cache, entry->uri_string);
134 g_free (entry->uri_string); /* This is the same string as in the hash table */
135 gnome_vfs_file_info_unref (entry->file_info);
137 if (gl_file_info_cache_list_last == entry->my_list_node) {
138 gl_file_info_cache_list_last = g_list_previous (entry->my_list_node);
141 gl_file_info_cache_list = g_list_remove_link (gl_file_info_cache_list, entry->my_list_node);
142 g_list_free_1 (entry->my_list_node);
144 for (node = entry->filenames ; node ; node = g_list_next(node)) {
148 g_list_free (entry->filenames);
152 g_static_rec_mutex_unlock (&cache_rlock);
157 http_cache_trim (void)
159 GList *node, *node_previous;
160 utime_t utime_expire;
162 g_static_rec_mutex_lock (&cache_rlock);
164 utime_expire = http_util_get_utime() - US_CACHE_FILE_INFO;
166 for ( node = gl_file_info_cache_list_last ;
167 node && (utime_expire > ((FileInfoCacheEntry *)node->data)->create_time) ;
170 node_previous = g_list_previous (node);
172 DEBUG_HTTP (("Cache: Expire: '%s'",((FileInfoCacheEntry *)node->data)->uri_string));
174 http_cache_entry_free ((FileInfoCacheEntry *)(node->data));
177 g_static_rec_mutex_unlock (&cache_rlock);
180 /* Note: doesn't bother trimming entries, so the check can fast */
182 http_cache_check (const gchar * uri_string)
184 FileInfoCacheEntry *entry;
185 utime_t utime_expire;
186 GnomeVFSFileInfo *ret;
188 g_static_rec_mutex_lock (&cache_rlock);
190 utime_expire = http_util_get_utime() - US_CACHE_FILE_INFO;
192 entry = (FileInfoCacheEntry *)g_hash_table_lookup (gl_file_info_cache, uri_string);
194 if (entry && (utime_expire > entry->create_time)) {
199 gnome_vfs_file_info_ref (entry->file_info);
201 DEBUG_HTTP (("Cache: Hit: '%s'", entry->uri_string));
203 ret = entry->file_info;
207 g_static_rec_mutex_unlock (&cache_rlock);
212 http_cache_uri_to_string (GnomeVFSURI *uri)
217 uri_string = gnome_vfs_uri_to_string (uri,
218 GNOME_VFS_URI_HIDE_USER_NAME
219 | GNOME_VFS_URI_HIDE_PASSWORD
220 | GNOME_VFS_URI_HIDE_TOPLEVEL_METHOD);
223 uri_length = strlen (uri_string);
224 /* Trim off trailing '/'s */
225 if ( '/' == uri_string[uri_length-1] ) {
226 uri_string[uri_length-1] = 0;
234 http_cache_check_uri (GnomeVFSURI *uri)
237 GnomeVFSFileInfo *ret;
239 uri_string = http_cache_uri_to_string (uri);
241 ret = http_cache_check (uri_string);
247 /* Directory operations demand fresher cache entries */
249 http_cache_check_directory (const gchar * uri_string, GList **p_child_file_info_list)
251 FileInfoCacheEntry *entry;
252 utime_t utime_expire;
253 GnomeVFSFileInfo *ret;
254 GList *child_file_info_list = NULL;
255 gboolean cache_incomplete;
257 g_static_rec_mutex_lock (&cache_rlock);
259 utime_expire = http_util_get_utime() - US_CACHE_DIRECTORY;
261 entry = (FileInfoCacheEntry *)g_hash_table_lookup (gl_file_info_cache, uri_string);
263 if (entry && (utime_expire > entry->create_time)) {
267 if (entry && entry->has_filenames) {
268 DEBUG_HTTP (("Cache: Hit: '%s'",entry->uri_string));
270 gnome_vfs_file_info_ref (entry->file_info);
271 ret = entry->file_info;
276 if (ret && p_child_file_info_list != NULL) {
277 GList * filename_node;
279 cache_incomplete = FALSE;
281 for (filename_node = entry->filenames ;
283 filename_node = g_list_next (filename_node)
285 char *child_filename;
286 FileInfoCacheEntry *child_entry;
288 child_filename = g_strconcat (uri_string, "/", (gchar *)filename_node->data, NULL);
290 child_entry = (FileInfoCacheEntry *)g_hash_table_lookup (gl_file_info_cache, child_filename);
292 /* Other HTTP requests on children can cause them to expire before the parent directory */
293 if (child_entry == NULL) {
294 cache_incomplete = TRUE;
298 gnome_vfs_file_info_ref (child_entry->file_info);
299 child_file_info_list = g_list_prepend (child_file_info_list, child_entry->file_info);
301 g_free (child_filename);
304 if (cache_incomplete) {
305 DEBUG_HTTP (("Cache: Directory was incomplete: '%s'",entry->uri_string));
307 gnome_vfs_file_info_unref (ret);
309 *p_child_file_info_list = NULL;
311 *p_child_file_info_list = child_file_info_list;
315 g_static_rec_mutex_unlock (&cache_rlock);
321 http_cache_check_directory_uri (GnomeVFSURI * uri, GList **p_child_file_info_list)
324 GnomeVFSFileInfo *ret;
326 uri_string = http_cache_uri_to_string (uri);
328 ret = http_cache_check_directory (uri_string, p_child_file_info_list);
334 /* Note that this neither strdups uri_string nor calls cache_trim() */
335 static FileInfoCacheEntry *
336 http_cache_add_no_strdup (gchar * uri_string, GnomeVFSFileInfo * file_info, gboolean is_dav)
338 FileInfoCacheEntry *entry_existing;
339 FileInfoCacheEntry *entry;
341 g_static_rec_mutex_lock (&cache_rlock);
343 entry_existing = (FileInfoCacheEntry *)g_hash_table_lookup (gl_file_info_cache, uri_string);
345 DEBUG_HTTP (("Cache: Add: '%s'", uri_string));
347 if (entry_existing) {
348 http_cache_entry_free (entry_existing);
349 entry_existing = NULL;
352 entry = http_cache_entry_new();
354 entry->uri_string = uri_string;
355 entry->file_info = file_info;
356 entry->is_dav = is_dav;
358 gnome_vfs_file_info_ref (file_info);
360 g_hash_table_insert (gl_file_info_cache, entry->uri_string, entry);
362 g_static_rec_mutex_unlock (&cache_rlock);
367 static FileInfoCacheEntry *
368 http_cache_add (const gchar * uri_string, GnomeVFSFileInfo * file_info, gboolean is_dav)
371 return http_cache_add_no_strdup (g_strdup (uri_string), file_info, is_dav);
375 http_cache_add_uri_and_children (GnomeVFSURI *uri, GnomeVFSFileInfo *file_info, GList *file_info_list)
380 FileInfoCacheEntry *parent_entry;
384 g_static_rec_mutex_lock (&cache_rlock);
386 uri_string = http_cache_uri_to_string (uri);
388 if (uri_string != NULL) {
389 /* Note--can't use no_strdup because we use uri_string below */
390 parent_entry = http_cache_add (uri_string, file_info, TRUE);
392 parent_entry->filenames = NULL;
394 for (node = file_info_list ; node != NULL ; node = g_list_next (node)) {
395 GnomeVFSFileInfo *child_info;
396 gchar * child_name_escaped;
398 child_info = (GnomeVFSFileInfo *) node->data;
400 child_name_escaped = gnome_vfs_escape_path_string (child_info->name);
402 child_string = g_strconcat (uri_string, "/", child_name_escaped, NULL);
404 parent_entry->filenames = g_list_prepend (
405 parent_entry->filenames,
407 child_name_escaped = NULL;
409 http_cache_add_no_strdup (child_string, child_info, TRUE);
411 /* I'm not sure that order matters... */
412 parent_entry->filenames = g_list_reverse (parent_entry->filenames);
413 parent_entry->has_filenames = TRUE;
416 g_static_rec_mutex_unlock (&cache_rlock);
422 http_cache_add_uri (GnomeVFSURI *uri, GnomeVFSFileInfo *file_info, gboolean is_dav)
426 http_cache_add_no_strdup (http_cache_uri_to_string (uri), file_info, is_dav);
431 http_cache_invalidate (const gchar * uri_string)
433 FileInfoCacheEntry *entry;
435 g_static_rec_mutex_lock (&cache_rlock);
437 entry = (FileInfoCacheEntry *)g_hash_table_lookup (gl_file_info_cache, uri_string);
440 DEBUG_HTTP (("Cache: Invalidate: '%s'", entry->uri_string));
442 http_cache_entry_free (entry);
445 g_static_rec_mutex_unlock (&cache_rlock);
449 http_cache_invalidate_uri (GnomeVFSURI *uri)
453 uri_string = http_cache_uri_to_string (uri);
456 http_cache_invalidate (uri_string);
463 /* Invalidates entry and everything cached immediately beneath it */
465 http_cache_invalidate_entry_and_children (const gchar * uri_string)
467 FileInfoCacheEntry *entry;
469 g_static_rec_mutex_lock (&cache_rlock);
471 entry = (FileInfoCacheEntry *)g_hash_table_lookup (gl_file_info_cache, uri_string);
476 DEBUG_HTTP (("Cache: Invalidate Recursive: '%s'", entry->uri_string));
478 for (node = entry->filenames ; node ; node = g_list_next (node) ) {
479 char *child_filename;
480 child_filename = g_strconcat (uri_string, "/", (gchar *)node->data, NULL);
481 http_cache_invalidate (child_filename);
482 g_free (child_filename);
485 http_cache_entry_free (entry);
488 g_static_rec_mutex_unlock (&cache_rlock);
491 /* Invalidates entry and everything cached immediately beneath it */
493 http_cache_invalidate_uri_and_children (GnomeVFSURI *uri)
497 uri_string = http_cache_uri_to_string (uri);
500 http_cache_invalidate_entry_and_children (uri_string);
506 /* Invalidate all of this uri's children and all of its parent's children */
508 http_cache_invalidate_uri_parent (GnomeVFSURI *uri)
513 uri_string = http_cache_uri_to_string (uri);
516 http_cache_invalidate_entry_and_children (uri_string);
518 last_slash = strrchr (uri_string, (unsigned char)'/');
521 http_cache_invalidate_entry_and_children (uri_string);