2010-03-28 16:56:39 +04:00
/*
* Copyright 2009 John - Mark Bell < jmb @ netsurf - browser . org >
*
* This file is part of NetSurf , http : //www.netsurf-browser.org/
*
* NetSurf 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 ; version 2 of the License .
*
* NetSurf is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
2010-03-28 20:00:54 +04:00
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
2010-03-28 16:56:39 +04:00
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program . If not , see < http : //www.gnu.org/licenses/>.
*/
2014-10-16 12:48:09 +04:00
/**
* \ file
*
2014-03-22 18:54:51 +04:00
* Low - level resource cache implementation
*
* This is the implementation of the low level cache . This cache
* stores source objects in memory and may use a persistant backing
* store to extend their lifetime .
*
* \ todo fix writeout conditions and ordering .
*
* \ todo instrument and ( auto ) tune
*
2010-03-28 16:56:39 +04:00
*/
# include <stdlib.h>
2014-11-27 00:29:13 +03:00
# include <stdint.h>
2010-03-28 16:56:39 +04:00
# include <string.h>
2014-12-28 01:48:37 +03:00
# include <strings.h>
2015-05-12 12:19:38 +03:00
# include <inttypes.h>
2010-03-28 16:56:39 +04:00
# include <curl/curl.h>
2014-11-27 00:29:13 +03:00
# include <nsutils/time.h>
2010-03-28 16:56:39 +04:00
2014-05-13 19:37:49 +04:00
# include "utils/config.h"
2014-01-25 00:19:20 +04:00
# include "utils/corestrings.h"
2010-03-29 10:27:37 +04:00
# include "utils/log.h"
2010-03-28 16:56:39 +04:00
# include "utils/messages.h"
2011-09-28 15:26:30 +04:00
# include "utils/nsurl.h"
2010-03-28 16:56:39 +04:00
# include "utils/utils.h"
2014-05-15 04:18:52 +04:00
# include "utils/time.h"
2014-10-16 12:48:09 +04:00
# include "desktop/gui_misc.h"
# include "desktop/gui_internal.h"
2014-03-22 18:54:51 +04:00
# include "content/fetch.h"
# include "content/backing_store.h"
# include "content/urldb.h"
2010-03-28 16:56:39 +04:00
2010-03-29 10:27:37 +04:00
/** Define to enable tracing of llcache operations. */
2014-06-04 01:30:04 +04:00
# undef LLCACHE_TRACE
//#define LLCACHE_TRACE 1
2010-03-29 10:27:37 +04:00
2014-02-21 15:43:48 +04:00
# ifdef LLCACHE_TRACE
2015-05-28 19:06:18 +03:00
# define LLCACHE_LOG(x...) LOG(x)
2014-02-21 15:43:48 +04:00
# else
2015-05-28 19:06:18 +03:00
# define LLCACHE_LOG(x...) ((void) 0)
2014-02-21 15:43:48 +04:00
# endif
2014-11-23 18:43:03 +03:00
/**
* State of a low - level cache object fetch .
*/
2010-03-28 16:56:39 +04:00
typedef enum {
LLCACHE_FETCH_INIT , /**< Initial state, before fetch */
LLCACHE_FETCH_HEADERS , /**< Fetching headers */
LLCACHE_FETCH_DATA , /**< Fetching object data */
LLCACHE_FETCH_COMPLETE /**< Fetch completed */
} llcache_fetch_state ;
2014-11-23 18:43:03 +03:00
/**
* Type of low - level cache object .
*/
2010-03-28 16:56:39 +04:00
typedef struct llcache_object llcache_object ;
2014-11-23 18:43:03 +03:00
/**
* Handle to low - level cache object .
*/
2010-03-28 16:56:39 +04:00
struct llcache_handle {
llcache_object * object ; /**< Pointer to associated object */
llcache_handle_callback cb ; /**< Client callback */
void * pw ; /**< Client data */
llcache_fetch_state state ; /**< Last known state of object fetch */
size_t bytes ; /**< Last reported byte count */
} ;
2014-11-23 18:43:03 +03:00
/**
* Low - level cache object user record .
*/
2010-03-28 16:56:39 +04:00
typedef struct llcache_object_user {
2010-12-16 00:15:49 +03:00
llcache_handle * handle ; /**< Handle data for client */
2010-03-28 16:56:39 +04:00
bool iterator_target ; /**< This is the an iterator target */
bool queued_for_delete ; /**< This user is queued for deletion */
struct llcache_object_user * prev ; /**< Previous in list */
struct llcache_object_user * next ; /**< Next in list */
} llcache_object_user ;
2014-11-23 18:43:03 +03:00
/**
* Low - level cache object fetch context .
*/
2010-03-28 16:56:39 +04:00
typedef struct {
uint32_t flags ; /**< Fetch flags */
2011-09-28 15:26:30 +04:00
nsurl * referer ; /**< Referring URL, or NULL if none */
2014-02-21 15:31:39 +04:00
llcache_post_data * post ; /**< POST data, or NULL for GET */
2010-03-28 16:56:39 +04:00
struct fetch * fetch ; /**< Fetch handle for this object */
llcache_fetch_state state ; /**< Current state of object fetch */
2010-04-11 17:58:12 +04:00
2010-04-17 00:00:10 +04:00
uint32_t redirect_count ; /**< Count of redirects followed */
bool tried_with_auth ; /**< Whether we've tried with auth */
2011-03-03 03:40:50 +03:00
2013-01-05 03:13:23 +04:00
bool tried_with_tls_downgrade ; /**< Whether we've tried TLS <= 1.0 */
2011-03-03 03:40:50 +03:00
bool outstanding_query ; /**< Waiting for a query response */
2010-03-28 16:56:39 +04:00
} llcache_fetch_ctx ;
2014-11-23 18:43:03 +03:00
/**
* Validation control .
*/
2011-02-25 00:22:24 +03:00
typedef enum {
LLCACHE_VALIDATE_FRESH , /**< Only revalidate if not fresh */
LLCACHE_VALIDATE_ALWAYS , /**< Always revalidate */
LLCACHE_VALIDATE_ONCE /**< Revalidate once only */
} llcache_validate ;
2014-11-23 18:43:03 +03:00
/**
* cache control value for invalid age .
*/
2014-03-22 18:54:51 +04:00
# define INVALID_AGE -1
2010-03-28 16:56:39 +04:00
/** Cache control data */
typedef struct {
time_t req_time ; /**< Time of request */
time_t res_time ; /**< Time of response */
2014-03-22 18:54:51 +04:00
time_t fin_time ; /**< Time of request completion */
2010-03-28 16:56:39 +04:00
time_t date ; /**< Date: response header */
time_t expires ; /**< Expires: response header */
int age ; /**< Age: response header */
int max_age ; /**< Max-Age Cache-control parameter */
2011-02-25 00:22:24 +03:00
llcache_validate no_cache ; /**< No-Cache Cache-control parameter */
2010-03-28 16:56:39 +04:00
char * etag ; /**< Etag: response header */
time_t last_modified ; /**< Last-Modified: response header */
} llcache_cache_control ;
/** Representation of a fetch header */
typedef struct {
char * name ; /**< Header name */
char * value ; /**< Header value */
} llcache_header ;
2014-03-22 18:54:51 +04:00
/** Current status of objects data */
typedef enum {
LLCACHE_STATE_RAM = 0 , /**< source data is stored in RAM only */
LLCACHE_STATE_DISC , /**< source data is stored on disc */
} llcache_store_state ;
2014-11-23 18:43:03 +03:00
/**
* Low - level cache object
*
* \ todo Consider whether a list is a sane container .
*/
2010-03-28 16:56:39 +04:00
struct llcache_object {
2014-03-22 18:54:51 +04:00
llcache_object * prev ; /**< Previous in list */
llcache_object * next ; /**< Next in list */
2010-03-28 16:56:39 +04:00
2014-03-22 18:54:51 +04:00
nsurl * url ; /**< Post-redirect URL for object */
2014-02-21 15:31:39 +04:00
2010-03-28 16:56:39 +04:00
/** \todo We need a generic dynamic buffer object */
2014-03-22 18:54:51 +04:00
uint8_t * source_data ; /**< Source data for object */
size_t source_len ; /**< Byte length of source data */
size_t source_alloc ; /**< Allocated size of source buffer */
llcache_store_state store_state ; /**< where the data for the object is stored */
llcache_object_user * users ; /**< List of users */
2010-03-28 16:56:39 +04:00
2014-03-22 18:54:51 +04:00
llcache_fetch_ctx fetch ; /**< Fetch context for object */
2010-03-28 16:56:39 +04:00
2014-03-22 18:54:51 +04:00
llcache_cache_control cache ; /**< Cache control data for object */
llcache_object * candidate ; /**< Object to use, if fetch determines
* that it is still fresh
*/
uint32_t candidate_count ; /**< Count of objects this is a
* candidate for
*/
2010-03-28 16:56:39 +04:00
2014-03-22 18:54:51 +04:00
llcache_header * headers ; /**< Fetch headers */
size_t num_headers ; /**< Number of fetch headers */
2010-03-28 16:56:39 +04:00
2014-03-22 18:54:51 +04:00
/* Instrumentation. These elemnts are strictly for information
* to improve the cache performance and to provide performace
* metrics . The values are non - authorative and must not be used to
* determine object lifetime etc .
*/
time_t last_used ; /**< time the last user was removed from the object */
2010-03-28 16:56:39 +04:00
} ;
2014-11-23 18:43:03 +03:00
/**
* Core llcache control context .
*/
2011-09-10 04:55:39 +04:00
struct llcache_s {
/** Handler for fetch-related queries */
llcache_query_callback query_cb ;
2012-04-28 17:24:09 +04:00
2011-09-10 04:55:39 +04:00
/** Data for fetch-related query handler */
void * query_cb_pw ;
2010-03-28 16:56:39 +04:00
2011-09-10 04:55:39 +04:00
/** Head of the low-level cached object list */
llcache_object * cached_objects ;
/** Head of the low-level uncached object list */
llcache_object * uncached_objects ;
2014-03-22 18:54:51 +04:00
/** The target upper bound for the RAM cache size */
2011-09-10 04:55:39 +04:00
uint32_t limit ;
2014-03-22 18:54:51 +04:00
2014-12-03 23:13:10 +03:00
/** Whether or not our users are caught up */
bool all_caught_up ;
/* backing store elements */
/**
* The minimum lifetime to consider sending objects to backing
* store .
2014-03-22 18:54:51 +04:00
*/
int minimum_lifetime ;
2014-12-03 23:13:10 +03:00
/**
* The time over which to apply the bandwidth calculations in ms
*/
2014-11-28 22:17:42 +03:00
unsigned long time_quantum ;
2014-12-03 23:13:10 +03:00
/**
* The minimum bandwidth to allow the backing store to use in
2014-11-28 22:17:42 +03:00
* bytes / second . Below this the backing store will be
* disabled .
*/
size_t minimum_bandwidth ;
2014-12-03 23:13:10 +03:00
/**
* The maximum bandwidth to allow the backing store to use in
* bytes / second
2014-11-28 22:17:42 +03:00
*/
size_t maximum_bandwidth ;
2014-12-03 23:13:10 +03:00
/**
* Total number of bytes written to backing store .
*/
uint64_t total_written ;
/**
* Total nuber of miliseconds taken to write to backing store .
*/
uint64_t total_elapsed ;
2014-03-22 18:54:51 +04:00
2011-09-10 04:55:39 +04:00
} ;
/** low level cache state */
static struct llcache_s * llcache = NULL ;
2010-03-28 16:56:39 +04:00
2012-04-28 17:24:09 +04:00
/* forward referenced callback function */
static void llcache_fetch_callback ( const fetch_msg * msg , void * p ) ;
2014-12-03 23:13:10 +03:00
2014-06-03 20:00:23 +04:00
/* forward referenced catch up function */
static void llcache_users_not_caught_up ( void ) ;
2011-09-28 15:26:30 +04:00
2010-03-28 16:56:39 +04:00
2012-04-28 17:24:09 +04:00
/******************************************************************************
* Low - level cache internals *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2010-03-28 16:56:39 +04:00
2012-04-28 17:24:09 +04:00
/**
2014-12-03 23:13:10 +03:00
* Create a new object user .
2012-04-28 17:24:09 +04:00
*
2014-12-03 23:13:10 +03:00
* \ param cb Callback routine .
* \ param pw Private data for callback .
* \ param user Pointer to location to receive result .
2012-04-28 17:24:09 +04:00
* \ return NSERROR_OK on success , appropriate error otherwise
*/
static nserror llcache_object_user_new ( llcache_handle_callback cb , void * pw ,
llcache_object_user * * user )
{
llcache_handle * h ;
llcache_object_user * u ;
2010-03-28 16:56:39 +04:00
2012-04-28 17:24:09 +04:00
h = calloc ( 1 , sizeof ( llcache_handle ) ) ;
2015-06-24 12:31:13 +03:00
if ( h = = NULL ) {
2012-04-28 17:24:09 +04:00
return NSERROR_NOMEM ;
2015-06-24 12:31:13 +03:00
}
2010-03-28 16:56:39 +04:00
2012-04-28 17:24:09 +04:00
u = calloc ( 1 , sizeof ( llcache_object_user ) ) ;
if ( u = = NULL ) {
free ( h ) ;
return NSERROR_NOMEM ;
}
2010-04-04 16:41:19 +04:00
2012-04-28 17:24:09 +04:00
h - > cb = cb ;
h - > pw = pw ;
2010-03-28 16:56:39 +04:00
2012-04-28 17:24:09 +04:00
u - > handle = h ;
2010-03-28 16:56:39 +04:00
2015-05-28 19:06:18 +03:00
LLCACHE_LOG ( " Created user %p (%p, %p, %p) " , u , h , ( void * ) cb , pw ) ;
2010-03-28 16:56:39 +04:00
2012-04-28 17:24:09 +04:00
* user = u ;
2010-10-28 02:12:00 +04:00
2012-04-28 17:24:09 +04:00
return NSERROR_OK ;
2010-10-28 02:12:00 +04:00
}
2010-03-28 16:56:39 +04:00
2012-04-28 17:24:09 +04:00
/**
* Destroy an object user
*
* \ param user User to destroy
* \ return NSERROR_OK on success , appropriate error otherwise
*
* \ pre User is not attached to an object
*/
static nserror llcache_object_user_destroy ( llcache_object_user * user )
2010-09-16 23:47:15 +04:00
{
2015-05-28 19:06:18 +03:00
LLCACHE_LOG ( " Destroyed user %p " , user ) ;
2014-02-21 15:31:39 +04:00
2012-04-28 17:24:09 +04:00
assert ( user - > next = = NULL ) ;
assert ( user - > prev = = NULL ) ;
2014-02-21 15:31:39 +04:00
2012-04-28 17:24:09 +04:00
if ( user - > handle ! = NULL )
free ( user - > handle ) ;
2011-02-24 13:14:50 +03:00
2012-04-28 17:24:09 +04:00
free ( user ) ;
return NSERROR_OK ;
2010-09-16 23:47:15 +04:00
}
2012-04-28 17:24:09 +04:00
/**
* Remove a user from a low - level cache object
*
* \ param object Object to remove user from
* \ param user User to remove
* \ return NSERROR_OK .
*/
2014-02-21 15:31:39 +04:00
static nserror llcache_object_remove_user ( llcache_object * object ,
2012-04-28 17:24:09 +04:00
llcache_object_user * user )
{
assert ( user ! = NULL ) ;
assert ( object ! = NULL ) ;
assert ( object - > users ! = NULL ) ;
assert ( user - > handle = = NULL | | user - > handle - > object = = object ) ;
assert ( ( user - > prev ! = NULL ) | | ( object - > users = = user ) ) ;
2014-02-21 15:31:39 +04:00
2012-04-28 17:24:09 +04:00
if ( user = = object - > users )
object - > users = user - > next ;
else
user - > prev - > next = user - > next ;
if ( user - > next ! = NULL )
user - > next - > prev = user - > prev ;
2014-02-21 15:31:39 +04:00
2012-04-28 17:24:09 +04:00
user - > next = user - > prev = NULL ;
2014-02-21 15:31:39 +04:00
2014-03-22 18:54:51 +04:00
/* record the time the last user was removed from the object */
if ( object - > users = = NULL ) {
object - > last_used = time ( NULL ) ;
}
2015-05-28 19:06:18 +03:00
LLCACHE_LOG ( " Removing user %p from %p " , user , object ) ;
2010-09-16 23:47:15 +04:00
2012-04-28 17:24:09 +04:00
return NSERROR_OK ;
}
2010-03-28 16:56:39 +04:00
2012-04-28 17:24:09 +04:00
/**
* Iterate the users of an object , calling their callbacks .
*
* \ param object The object to iterate
* \ param event The event to pass to the callback .
* \ return NSERROR_OK on success , appropriate error otherwise .
*/
static nserror llcache_send_event_to_users ( llcache_object * object ,
llcache_event * event )
2010-03-28 16:56:39 +04:00
{
2012-04-28 17:24:09 +04:00
nserror error = NSERROR_OK ;
llcache_object_user * user , * next_user ;
2014-02-21 15:31:39 +04:00
2012-04-28 17:24:09 +04:00
user = object - > users ;
while ( user ! = NULL ) {
user - > iterator_target = true ;
2011-09-10 04:55:39 +04:00
2012-04-28 17:24:09 +04:00
error = user - > handle - > cb ( user - > handle , event ,
user - > handle - > pw ) ;
2011-09-10 04:55:39 +04:00
2012-04-28 17:24:09 +04:00
next_user = user - > next ;
2011-09-28 15:26:30 +04:00
2012-04-28 17:24:09 +04:00
user - > iterator_target = false ;
2011-09-28 15:26:30 +04:00
2012-04-28 17:24:09 +04:00
if ( user - > queued_for_delete ) {
llcache_object_remove_user ( object , user ) ;
llcache_object_user_destroy ( user ) ;
}
2011-09-28 15:26:30 +04:00
2012-04-28 17:24:09 +04:00
if ( error ! = NSERROR_OK )
break ;
2010-03-28 16:56:39 +04:00
2012-04-28 17:24:09 +04:00
user = next_user ;
}
2014-02-21 15:31:39 +04:00
2012-04-28 17:24:09 +04:00
return error ;
2010-03-28 16:56:39 +04:00
}
2012-04-28 17:24:09 +04:00
/**
* Create a new low - level cache object
*
* \ param url URL of object to create
* \ param result Pointer to location to receive result
* \ return NSERROR_OK on success , appropriate error otherwise
*/
static nserror llcache_object_new ( nsurl * url , llcache_object * * result )
2010-04-27 10:48:09 +04:00
{
2012-04-28 17:24:09 +04:00
llcache_object * obj = calloc ( 1 , sizeof ( llcache_object ) ) ;
if ( obj = = NULL )
return NSERROR_NOMEM ;
2010-04-27 10:48:09 +04:00
2015-05-28 19:06:18 +03:00
LLCACHE_LOG ( " Created object %p (%s) " , obj , nsurl_access ( url ) ) ;
2010-12-16 00:15:49 +03:00
2012-04-28 17:24:09 +04:00
obj - > url = nsurl_ref ( url ) ;
2010-04-27 10:48:09 +04:00
2012-04-28 17:24:09 +04:00
* result = obj ;
2010-04-27 10:48:09 +04:00
2012-04-28 17:24:09 +04:00
return NSERROR_OK ;
}
2010-04-27 10:48:09 +04:00
2012-04-28 17:24:09 +04:00
/**
* Clone a POST data object
*
* \ param orig Object to clone
* \ param clone Pointer to location to receive clone
* \ return NSERROR_OK on success , appropriate error otherwise
*/
2014-02-21 15:31:39 +04:00
static nserror llcache_post_data_clone ( const llcache_post_data * orig ,
2012-04-28 17:24:09 +04:00
llcache_post_data * * clone )
{
llcache_post_data * post_clone ;
2010-04-27 10:48:09 +04:00
2012-04-28 17:24:09 +04:00
post_clone = calloc ( 1 , sizeof ( llcache_post_data ) ) ;
if ( post_clone = = NULL )
return NSERROR_NOMEM ;
2010-04-27 10:48:09 +04:00
2012-04-28 17:24:09 +04:00
post_clone - > type = orig - > type ;
2010-04-27 10:48:09 +04:00
2012-04-28 17:24:09 +04:00
/* Deep-copy the type-specific data */
if ( orig - > type = = LLCACHE_POST_URL_ENCODED ) {
post_clone - > data . urlenc = strdup ( orig - > data . urlenc ) ;
if ( post_clone - > data . urlenc = = NULL ) {
free ( post_clone ) ;
2010-12-16 00:15:49 +03:00
2012-04-28 17:24:09 +04:00
return NSERROR_NOMEM ;
2010-04-27 10:48:09 +04:00
}
2012-04-28 17:24:09 +04:00
} else {
post_clone - > data . multipart = fetch_multipart_data_clone (
orig - > data . multipart ) ;
if ( post_clone - > data . multipart = = NULL ) {
free ( post_clone ) ;
2010-04-27 10:48:09 +04:00
2012-04-28 17:24:09 +04:00
return NSERROR_NOMEM ;
}
2010-04-27 10:48:09 +04:00
}
2011-09-10 04:55:39 +04:00
2012-04-28 17:24:09 +04:00
* clone = post_clone ;
2011-09-28 15:26:30 +04:00
2012-04-28 17:24:09 +04:00
return NSERROR_OK ;
2010-04-27 10:48:09 +04:00
}
2012-04-28 17:24:09 +04:00
/**
* Split a fetch header into name and value
*
* \ param data Header string
* \ param len Byte length of header
* \ param name Pointer to location to receive header name
* \ param value Pointer to location to receive header value
* \ return NSERROR_OK on success , appropriate error otherwise
*/
2014-02-21 15:31:39 +04:00
static nserror llcache_fetch_split_header ( const uint8_t * data , size_t len ,
2012-04-28 17:24:09 +04:00
char * * name , char * * value )
2010-03-28 16:56:39 +04:00
{
2012-04-28 17:24:09 +04:00
char * n , * v ;
const uint8_t * colon ;
2010-03-28 16:56:39 +04:00
2014-03-08 18:25:15 +04:00
/* Strip leading whitespace from name */
while ( data [ 0 ] = = ' ' | | data [ 0 ] = = ' \t ' | |
data [ 0 ] = = ' \r ' | | data [ 0 ] = = ' \n ' ) {
data + + ;
}
2012-04-28 17:24:09 +04:00
/* Find colon */
colon = ( const uint8_t * ) strchr ( ( const char * ) data , ' : ' ) ;
if ( colon = = NULL ) {
/* Failed, assume a key with no value */
2014-03-08 18:25:15 +04:00
colon = data + strlen ( ( const char * ) data ) ;
/* Strip trailing whitespace from name */
while ( ( colon > data ) & &
( colon [ - 1 ] = = ' ' | | colon [ - 1 ] = = ' \t ' | |
colon [ - 1 ] = = ' \r ' | | colon [ - 1 ] = = ' \n ' ) ) {
colon - - ;
}
n = strndup ( ( const char * ) data , colon - data ) ;
2012-04-28 17:24:09 +04:00
if ( n = = NULL )
return NSERROR_NOMEM ;
v = strdup ( " " ) ;
if ( v = = NULL ) {
free ( n ) ;
return NSERROR_NOMEM ;
}
} else {
/* Split header into name & value */
/* Strip trailing whitespace from name */
2014-02-21 15:31:39 +04:00
while ( colon > data & & ( colon [ - 1 ] = = ' ' | |
colon [ - 1 ] = = ' \t ' | | colon [ - 1 ] = = ' \r ' | |
2012-04-28 17:24:09 +04:00
colon [ - 1 ] = = ' \n ' ) )
colon - - ;
n = strndup ( ( const char * ) data , colon - data ) ;
if ( n = = NULL )
return NSERROR_NOMEM ;
/* Find colon again */
while ( * colon ! = ' : ' ) {
colon + + ;
}
/* Skip over colon and any subsequent whitespace */
do {
colon + + ;
2014-02-21 15:31:39 +04:00
} while ( * colon = = ' ' | | * colon = = ' \t ' | |
2012-04-28 17:24:09 +04:00
* colon = = ' \r ' | | * colon = = ' \n ' ) ;
/* Strip trailing whitespace from value */
2014-02-21 15:31:39 +04:00
while ( len > 0 & & ( data [ len - 1 ] = = ' ' | |
data [ len - 1 ] = = ' \t ' | |
2012-04-28 17:24:09 +04:00
data [ len - 1 ] = = ' \r ' | |
data [ len - 1 ] = = ' \n ' ) ) {
len - - ;
}
v = strndup ( ( const char * ) colon , len - ( colon - data ) ) ;
if ( v = = NULL ) {
free ( n ) ;
return NSERROR_NOMEM ;
}
2010-03-28 16:56:39 +04:00
}
2012-04-28 17:24:09 +04:00
* name = n ;
* value = v ;
2010-03-28 16:56:39 +04:00
return NSERROR_OK ;
}
2012-04-28 17:24:09 +04:00
/**
* Parse a fetch header
*
* \ param object Object to parse header for
* \ param data Header string
* \ param len Byte length of header
* \ param name Pointer to location to receive header name
* \ param value Pointer to location to receive header value
* \ return NSERROR_OK on success , appropriate error otherwise
*
2014-02-21 15:31:39 +04:00
* \ note This function also has the side - effect of updating
2012-04-28 17:24:09 +04:00
* the cache control data for the object if an interesting
* header is encountered
*/
2014-02-21 15:31:39 +04:00
static nserror llcache_fetch_parse_header ( llcache_object * object ,
2012-04-28 17:24:09 +04:00
const uint8_t * data , size_t len , char * * name , char * * value )
2010-03-28 16:56:39 +04:00
{
nserror error ;
2012-04-28 17:24:09 +04:00
/* Set fetch response time if not already set */
if ( object - > cache . res_time = = 0 )
object - > cache . res_time = time ( NULL ) ;
2010-03-28 16:56:39 +04:00
2012-04-28 17:24:09 +04:00
/* Decompose header into name-value pair */
error = llcache_fetch_split_header ( data , len , name , value ) ;
2010-03-28 16:56:39 +04:00
if ( error ! = NSERROR_OK )
return error ;
2012-04-28 17:24:09 +04:00
/* Parse cache headers to populate cache control data */
# define SKIP_ST(p) while (*p != '\0' && (*p == ' ' || *p == '\t')) p++
2010-03-28 16:56:39 +04:00
2012-04-28 17:24:09 +04:00
if ( 5 < len & & strcasecmp ( * name , " Date " ) = = 0 ) {
/* extract Date header */
object - > cache . date = curl_getdate ( * value , NULL ) ;
} else if ( 4 < len & & strcasecmp ( * name , " Age " ) = = 0 ) {
/* extract Age header */
if ( ' 0 ' < = * * value & & * * value < = ' 9 ' )
object - > cache . age = atoi ( * value ) ;
} else if ( 8 < len & & strcasecmp ( * name , " Expires " ) = = 0 ) {
/* extract Expires header */
object - > cache . expires = curl_getdate ( * value , NULL ) ;
} else if ( 14 < len & & strcasecmp ( * name , " Cache-Control " ) = = 0 ) {
/* extract and parse Cache-Control header */
const char * start = * value ;
const char * comma = * value ;
2010-03-28 16:56:39 +04:00
2012-04-28 17:24:09 +04:00
while ( * comma ! = ' \0 ' ) {
while ( * comma ! = ' \0 ' & & * comma ! = ' , ' )
comma + + ;
2010-03-28 16:56:39 +04:00
2014-02-21 15:31:39 +04:00
if ( 8 < comma - start & & ( strncasecmp ( start ,
" no-cache " , 8 ) = = 0 | |
2012-04-28 17:24:09 +04:00
strncasecmp ( start , " no-store " , 8 ) = = 0 ) )
/* When we get a disk cache we should
* distinguish between these two */
object - > cache . no_cache = LLCACHE_VALIDATE_ALWAYS ;
2014-02-21 15:31:39 +04:00
else if ( 7 < comma - start & &
2012-04-28 17:24:09 +04:00
strncasecmp ( start , " max-age " , 7 ) = = 0 ) {
/* Find '=' */
while ( start < comma & & * start ! = ' = ' )
start + + ;
2010-03-28 16:56:39 +04:00
2012-04-28 17:24:09 +04:00
/* Skip over it */
start + + ;
2010-03-28 16:56:39 +04:00
2012-04-28 17:24:09 +04:00
/* Skip whitespace */
SKIP_ST ( start ) ;
2010-03-28 16:56:39 +04:00
2012-04-28 17:24:09 +04:00
if ( start < comma )
object - > cache . max_age = atoi ( start ) ;
}
2010-03-28 16:56:39 +04:00
2012-04-28 17:24:09 +04:00
if ( * comma ! = ' \0 ' ) {
/* Skip past comma */
comma + + ;
/* Skip whitespace */
SKIP_ST ( comma ) ;
}
2010-12-16 00:15:49 +03:00
2012-04-28 17:24:09 +04:00
/* Set start for next token */
start = comma ;
2010-12-16 00:15:49 +03:00
}
2012-04-28 17:24:09 +04:00
} else if ( 5 < len & & strcasecmp ( * name , " ETag " ) = = 0 ) {
/* extract ETag header */
free ( object - > cache . etag ) ;
object - > cache . etag = strdup ( * value ) ;
2013-05-02 01:15:24 +04:00
if ( object - > cache . etag = = NULL ) {
free ( * name ) ;
free ( * value ) ;
2012-04-28 17:24:09 +04:00
return NSERROR_NOMEM ;
2013-05-02 01:15:24 +04:00
}
2012-04-28 17:24:09 +04:00
} else if ( 14 < len & & strcasecmp ( * name , " Last-Modified " ) = = 0 ) {
/* extract Last-Modified header */
object - > cache . last_modified = curl_getdate ( * value , NULL ) ;
2010-03-28 16:56:39 +04:00
}
2012-04-28 17:24:09 +04:00
# undef SKIP_ST
2014-02-21 15:31:39 +04:00
return NSERROR_OK ;
2010-03-28 16:56:39 +04:00
}
2014-11-23 18:43:03 +03:00
/**
* Destroy headers .
*
* \ param object The object to destroy headers within .
*/
2012-04-28 17:24:09 +04:00
static inline void llcache_destroy_headers ( llcache_object * object )
2010-04-04 16:41:19 +04:00
{
2012-04-28 17:24:09 +04:00
while ( object - > num_headers > 0 ) {
object - > num_headers - - ;
free ( object - > headers [ object - > num_headers ] . name ) ;
free ( object - > headers [ object - > num_headers ] . value ) ;
2010-04-04 16:41:19 +04:00
}
2012-04-28 17:24:09 +04:00
free ( object - > headers ) ;
object - > headers = NULL ;
2010-04-04 16:41:19 +04:00
}
2014-11-23 18:43:03 +03:00
/**
* Invalidate cache control data .
*
* \ param object The object to invalidate cache control for .
*/
2012-04-28 17:24:09 +04:00
static inline void llcache_invalidate_cache_control_data ( llcache_object * object )
2010-04-04 16:41:19 +04:00
{
2012-04-28 17:24:09 +04:00
free ( object - > cache . etag ) ;
memset ( & ( object - > cache ) , 0 , sizeof ( llcache_cache_control ) ) ;
2010-12-16 00:15:49 +03:00
2012-04-28 17:24:09 +04:00
object - > cache . age = INVALID_AGE ;
object - > cache . max_age = INVALID_AGE ;
}
2010-12-16 00:15:49 +03:00
2012-04-28 17:24:09 +04:00
/**
* Process a fetch header
*
* \ param object Object being fetched
* \ param data Header string
* \ param len Byte length of header
* \ return NSERROR_OK on success , appropriate error otherwise
*/
2014-02-21 15:31:39 +04:00
static nserror llcache_fetch_process_header ( llcache_object * object ,
2012-04-28 17:24:09 +04:00
const uint8_t * data , size_t len )
{
nserror error ;
char * name , * value ;
llcache_header * temp ;
2010-12-16 00:15:49 +03:00
2014-11-23 18:43:03 +03:00
/**
* \ note The headers for multiple HTTP responses may be
* delivered to us if the fetch layer receives a 401 response
* for which it has authentication credentials . This will
* result in a silent re - request after which we ' ll receive the
* actual response headers for the object we want to fetch
* ( assuming that the credentials were correct of course )
2012-04-28 17:24:09 +04:00
*
2014-02-21 15:31:39 +04:00
* Therefore , if the header is an HTTP response start marker , then we
* must discard any headers we ' ve read so far , reset the cache data
2012-04-28 17:24:09 +04:00
* that we might have computed , and start again .
*/
/** \todo Properly parse the response line */
if ( strncmp ( ( const char * ) data , " HTTP/ " , SLEN ( " HTTP/ " ) ) = = 0 ) {
time_t req_time = object - > cache . req_time ;
2010-12-16 00:15:49 +03:00
2010-09-16 23:47:15 +04:00
llcache_invalidate_cache_control_data ( object ) ;
2010-04-06 01:35:38 +04:00
2012-04-28 17:24:09 +04:00
/* Restore request time, so we compute object's age correctly */
object - > cache . req_time = req_time ;
2010-04-06 01:35:38 +04:00
2012-04-28 17:24:09 +04:00
llcache_destroy_headers ( object ) ;
2010-04-06 01:35:38 +04:00
}
2012-04-28 17:24:09 +04:00
error = llcache_fetch_parse_header ( object , data , len , & name , & value ) ;
2013-05-02 01:15:24 +04:00
if ( error ! = NSERROR_OK ) {
2012-04-28 17:24:09 +04:00
return error ;
2013-05-02 01:15:24 +04:00
}
2010-04-06 01:35:38 +04:00
2014-03-08 18:25:15 +04:00
/* deal with empty header */
if ( name [ 0 ] = = 0 ) {
free ( name ) ;
free ( value ) ;
return NSERROR_OK ;
}
2012-04-28 17:24:09 +04:00
/* Append header data to the object's headers array */
2014-02-21 15:31:39 +04:00
temp = realloc ( object - > headers , ( object - > num_headers + 1 ) *
2012-04-28 17:24:09 +04:00
sizeof ( llcache_header ) ) ;
if ( temp = = NULL ) {
free ( name ) ;
free ( value ) ;
return NSERROR_NOMEM ;
2010-04-17 00:42:10 +04:00
}
2012-04-28 17:24:09 +04:00
object - > headers = temp ;
2010-04-17 00:42:10 +04:00
2012-04-28 17:24:09 +04:00
object - > headers [ object - > num_headers ] . name = name ;
object - > headers [ object - > num_headers ] . value = value ;
2010-03-28 16:56:39 +04:00
2012-04-28 17:24:09 +04:00
object - > num_headers + + ;
2010-03-28 16:56:39 +04:00
2012-04-28 17:24:09 +04:00
return NSERROR_OK ;
2010-03-28 16:56:39 +04:00
}
2012-04-28 17:24:09 +04:00
/**
* ( Re ) fetch an object
*
2015-06-24 12:31:13 +03:00
* sets up headers and attempts to start an actual fetch from the
* fetchers system updating the llcache object with the new fetch on
* sucessful start .
*
2014-11-23 18:43:03 +03:00
* \ pre The fetch parameters in object - > fetch must be populated
*
2015-06-24 12:31:13 +03:00
* \ param object Object to refetch
2012-04-28 17:24:09 +04:00
* \ return NSERROR_OK on success , appropriate error otherwise
2014-02-21 15:31:39 +04:00
*/
2012-04-28 17:24:09 +04:00
static nserror llcache_object_refetch ( llcache_object * object )
2010-03-28 16:56:39 +04:00
{
2012-04-28 17:24:09 +04:00
const char * urlenc = NULL ;
struct fetch_multipart_data * multipart = NULL ;
char * * headers = NULL ;
int header_idx = 0 ;
2015-06-24 12:31:13 +03:00
nserror res ;
2010-03-28 16:56:39 +04:00
2012-04-28 17:24:09 +04:00
if ( object - > fetch . post ! = NULL ) {
2014-11-23 18:43:03 +03:00
if ( object - > fetch . post - > type = = LLCACHE_POST_URL_ENCODED ) {
2012-04-28 17:24:09 +04:00
urlenc = object - > fetch . post - > data . urlenc ;
2014-11-23 18:43:03 +03:00
} else {
2012-04-28 17:24:09 +04:00
multipart = object - > fetch . post - > data . multipart ;
2014-11-23 18:43:03 +03:00
}
2010-03-28 16:56:39 +04:00
}
2012-04-28 17:24:09 +04:00
/* Generate cache-control headers */
headers = malloc ( 3 * sizeof ( char * ) ) ;
2015-06-24 12:31:13 +03:00
if ( headers = = NULL ) {
2012-04-28 17:24:09 +04:00
return NSERROR_NOMEM ;
2015-06-24 12:31:13 +03:00
}
2010-03-28 16:56:39 +04:00
2012-04-28 17:24:09 +04:00
if ( object - > cache . etag ! = NULL ) {
2014-02-21 15:31:39 +04:00
const size_t len = SLEN ( " If-None-Match: " ) +
2012-04-28 17:24:09 +04:00
strlen ( object - > cache . etag ) + 1 ;
2010-03-28 16:56:39 +04:00
2012-04-28 17:24:09 +04:00
headers [ header_idx ] = malloc ( len ) ;
if ( headers [ header_idx ] = = NULL ) {
free ( headers ) ;
return NSERROR_NOMEM ;
}
2010-03-28 16:56:39 +04:00
2012-04-28 17:24:09 +04:00
snprintf ( headers [ header_idx ] , len , " If-None-Match: %s " ,
object - > cache . etag ) ;
2010-12-16 00:15:49 +03:00
2012-04-28 17:24:09 +04:00
header_idx + + ;
}
2014-11-23 18:43:03 +03:00
2012-04-28 17:24:09 +04:00
if ( object - > cache . date ! = 0 ) {
/* Maximum length of an RFC 1123 date is 29 bytes */
const size_t len = SLEN ( " If-Modified-Since: " ) + 29 + 1 ;
2010-12-16 00:15:49 +03:00
2012-04-28 17:24:09 +04:00
headers [ header_idx ] = malloc ( len ) ;
if ( headers [ header_idx ] = = NULL ) {
while ( - - header_idx > = 0 )
free ( headers [ header_idx ] ) ;
free ( headers ) ;
return NSERROR_NOMEM ;
}
snprintf ( headers [ header_idx ] , len , " If-Modified-Since: %s " ,
rfc1123_date ( object - > cache . date ) ) ;
header_idx + + ;
2010-12-16 00:15:49 +03:00
}
2012-04-28 17:24:09 +04:00
headers [ header_idx ] = NULL ;
2010-12-16 00:15:49 +03:00
2012-04-28 17:24:09 +04:00
/* Reset cache control data */
llcache_invalidate_cache_control_data ( object ) ;
object - > cache . req_time = time ( NULL ) ;
2014-03-22 18:54:51 +04:00
object - > cache . fin_time = object - > cache . req_time ;
2010-03-28 16:56:39 +04:00
2012-04-28 17:24:09 +04:00
/* Reset fetch state */
object - > fetch . state = LLCACHE_FETCH_INIT ;
2010-03-28 16:56:39 +04:00
2015-05-28 19:06:18 +03:00
LLCACHE_LOG ( " Refetching %p " , object ) ;
2010-03-29 10:27:37 +04:00
2012-04-28 17:24:09 +04:00
/* Kick off fetch */
2015-06-24 12:31:13 +03:00
res = fetch_start ( object - > url ,
object - > fetch . referer ,
llcache_fetch_callback ,
object ,
object - > fetch . flags & LLCACHE_RETRIEVE_NO_ERROR_PAGES ,
urlenc ,
multipart ,
object - > fetch . flags & LLCACHE_RETRIEVE_VERIFIABLE ,
object - > fetch . tried_with_tls_downgrade ,
( const char * * ) headers ,
& object - > fetch . fetch ) ;
2012-04-28 17:24:09 +04:00
/* Clean up cache-control headers */
2015-06-24 12:31:13 +03:00
while ( - - header_idx > = 0 ) {
2012-04-28 17:24:09 +04:00
free ( headers [ header_idx ] ) ;
2015-06-24 12:31:13 +03:00
}
2012-04-28 17:24:09 +04:00
free ( headers ) ;
2015-06-24 12:31:13 +03:00
return res ;
2010-03-28 16:56:39 +04:00
}
/**
2012-04-28 17:24:09 +04:00
* Kick - off a fetch for an object
2010-03-28 16:56:39 +04:00
*
2014-11-23 18:43:03 +03:00
* \ pre object : : url must contain the URL to fetch
* \ pre If there is a freshness validation candidate ,
* object : : candidate and object : : cache must be filled in
* \ pre There must not be a fetch in progress for \ a object
*
2012-04-28 17:24:09 +04:00
* \ param object Object to fetch
* \ param flags Fetch flags
* \ param referer Referring URL , or NULL for none
* \ param post POST data , or NULL for GET
* \ param redirect_count Number of redirects followed so far
2010-03-28 16:56:39 +04:00
* \ return NSERROR_OK on success , appropriate error otherwise
2010-04-14 14:56:44 +04:00
*/
2012-04-28 17:24:09 +04:00
static nserror llcache_object_fetch ( llcache_object * object , uint32_t flags ,
nsurl * referer , const llcache_post_data * post ,
uint32_t redirect_count )
2010-04-14 14:56:44 +04:00
{
2012-04-28 17:24:09 +04:00
nserror error ;
nsurl * referer_clone = NULL ;
llcache_post_data * post_clone = NULL ;
2010-12-16 00:15:49 +03:00
2015-05-28 19:06:18 +03:00
LLCACHE_LOG ( " Starting fetch for %p " , object ) ;
2010-12-16 00:15:49 +03:00
2012-04-28 17:24:09 +04:00
if ( post ! = NULL ) {
error = llcache_post_data_clone ( post , & post_clone ) ;
if ( error ! = NSERROR_OK )
return error ;
}
2010-12-16 00:15:49 +03:00
2012-04-28 17:24:09 +04:00
if ( referer ! = NULL )
referer_clone = nsurl_ref ( referer ) ;
2010-12-16 00:15:49 +03:00
2012-04-28 17:24:09 +04:00
object - > fetch . flags = flags ;
object - > fetch . referer = referer_clone ;
object - > fetch . post = post_clone ;
object - > fetch . redirect_count = redirect_count ;
2010-12-16 01:59:19 +03:00
2012-04-28 17:24:09 +04:00
return llcache_object_refetch ( object ) ;
2010-04-14 14:56:44 +04:00
}
2010-03-28 16:56:39 +04:00
/**
2012-04-28 17:24:09 +04:00
* Destroy a low - level cache object
2010-03-28 16:56:39 +04:00
*
2012-04-28 17:24:09 +04:00
* \ pre Object is detached from cache list
* \ pre Object has no users
* \ pre Object is not a candidate ( i . e . object : : candidate_count = = 0 )
2014-11-23 18:43:03 +03:00
*
* \ param object Object to destroy
* \ return NSERROR_OK on success , appropriate error otherwise
2010-03-28 16:56:39 +04:00
*/
2012-04-28 17:24:09 +04:00
static nserror llcache_object_destroy ( llcache_object * object )
2010-03-28 16:56:39 +04:00
{
2012-04-28 17:24:09 +04:00
size_t i ;
2010-03-28 16:56:39 +04:00
2015-05-28 19:06:18 +03:00
LLCACHE_LOG ( " Destroying object %p " , object ) ;
2010-03-29 10:27:37 +04:00
2014-11-23 21:21:56 +03:00
if ( object - > source_data ! = NULL ) {
if ( object - > store_state = = LLCACHE_STATE_DISC ) {
guit - > llcache - > release ( object - > url , BACKING_STORE_NONE ) ;
} else {
free ( object - > source_data ) ;
}
}
2012-04-28 17:24:09 +04:00
nsurl_unref ( object - > url ) ;
2010-03-28 16:56:39 +04:00
2012-04-28 17:24:09 +04:00
if ( object - > fetch . fetch ! = NULL ) {
fetch_abort ( object - > fetch . fetch ) ;
object - > fetch . fetch = NULL ;
2011-10-05 22:05:41 +04:00
}
2010-04-14 18:53:21 +04:00
2012-04-28 17:24:09 +04:00
if ( object - > fetch . referer ! = NULL )
nsurl_unref ( object - > fetch . referer ) ;
2010-03-28 16:56:39 +04:00
2012-04-28 17:24:09 +04:00
if ( object - > fetch . post ! = NULL ) {
if ( object - > fetch . post - > type = = LLCACHE_POST_URL_ENCODED ) {
free ( object - > fetch . post - > data . urlenc ) ;
} else {
fetch_multipart_data_destroy (
object - > fetch . post - > data . multipart ) ;
2010-03-28 16:56:39 +04:00
}
2012-04-28 17:24:09 +04:00
free ( object - > fetch . post ) ;
}
2010-03-28 16:56:39 +04:00
2012-04-28 17:24:09 +04:00
free ( object - > cache . etag ) ;
for ( i = 0 ; i < object - > num_headers ; i + + ) {
free ( object - > headers [ i ] . name ) ;
free ( object - > headers [ i ] . value ) ;
2010-03-28 16:56:39 +04:00
}
2012-04-28 17:24:09 +04:00
free ( object - > headers ) ;
free ( object ) ;
2010-03-29 10:27:37 +04:00
2010-03-28 16:56:39 +04:00
return NSERROR_OK ;
}
/**
2012-04-28 17:24:09 +04:00
* Add a low - level cache object to a cache list
2010-03-28 16:56:39 +04:00
*
2012-04-28 17:24:09 +04:00
* \ param object Object to add
* \ param list List to add to
* \ return NSERROR_OK
2010-03-28 16:56:39 +04:00
*/
2012-04-28 17:24:09 +04:00
static nserror llcache_object_add_to_list ( llcache_object * object ,
llcache_object * * list )
2010-03-28 16:56:39 +04:00
{
2012-04-28 17:24:09 +04:00
object - > prev = NULL ;
object - > next = * list ;
2010-08-29 21:41:54 +04:00
2012-04-28 17:24:09 +04:00
if ( * list ! = NULL )
( * list ) - > prev = object ;
* list = object ;
2010-03-28 16:56:39 +04:00
2012-04-28 17:24:09 +04:00
return NSERROR_OK ;
}
2010-03-28 16:56:39 +04:00
2012-04-28 17:24:09 +04:00
/**
2013-01-07 22:45:29 +04:00
* Determine the remaining lifetime of a cache object using the
2012-04-28 17:24:09 +04:00
*
2014-11-09 15:50:30 +03:00
* \ param cd cache control data .
* \ return The length of time remaining for the object or 0 if expired .
2012-04-28 17:24:09 +04:00
*/
2013-01-07 22:45:29 +04:00
static int
llcache_object_rfc2616_remaining_lifetime ( const llcache_cache_control * cd )
2012-04-28 17:24:09 +04:00
{
int current_age , freshness_lifetime ;
time_t now = time ( NULL ) ;
2010-03-29 10:27:37 +04:00
2012-04-28 17:24:09 +04:00
/* Calculate staleness of cached object as per RFC 2616 13.2.3/13.2.4 */
current_age = max ( 0 , ( cd - > res_time - cd - > date ) ) ;
current_age = max ( current_age , ( cd - > age = = INVALID_AGE ) ? 0 : cd - > age ) ;
current_age + = cd - > res_time - cd - > req_time + now - cd - > res_time ;
2010-03-29 10:27:37 +04:00
2012-04-28 17:24:09 +04:00
/* Determine freshness lifetime of this object */
if ( cd - > max_age ! = INVALID_AGE )
freshness_lifetime = cd - > max_age ;
else if ( cd - > expires ! = 0 )
freshness_lifetime = cd - > expires - cd - > date ;
else if ( cd - > last_modified ! = 0 )
freshness_lifetime = ( now - cd - > last_modified ) / 10 ;
else
freshness_lifetime = 0 ;
2010-03-28 16:56:39 +04:00
2015-05-28 19:06:18 +03:00
/* LLCACHE_LOG("%d:%d", freshness_lifetime, current_age); */
2013-01-07 22:45:29 +04:00
if ( ( cd - > no_cache = = LLCACHE_VALIDATE_FRESH ) & &
( freshness_lifetime > current_age ) ) {
/* object was not forbidden from being returned from
* the cache unvalidated ( i . e . the response contained
* a no - cache directive )
*
* The object current age is within the freshness lifetime .
*/
return freshness_lifetime - current_age ;
}
return 0 ; /* object has no remaining lifetime */
}
/**
* Determine if an object is still fresh
*
* \ param object Object to consider
* \ return True if object is still fresh , false otherwise
*/
static bool llcache_object_is_fresh ( const llcache_object * object )
{
int remaining_lifetime ;
const llcache_cache_control * cd = & object - > cache ;
remaining_lifetime = llcache_object_rfc2616_remaining_lifetime ( cd ) ;
2015-05-28 19:06:18 +03:00
LLCACHE_LOG ( " %p: (%d > 0 || %d != %d) " , object ,
2013-01-07 22:45:29 +04:00
remaining_lifetime ,
2015-05-28 19:06:18 +03:00
object - > fetch . state , LLCACHE_FETCH_COMPLETE ) ;
2010-03-29 10:27:37 +04:00
2010-09-16 22:58:32 +04:00
/* The object is fresh if:
2013-01-07 22:45:29 +04:00
* - it was not forbidden from being returned from the cache
* unvalidated .
2010-09-16 22:58:32 +04:00
*
2014-02-21 15:31:39 +04:00
* - it has remaining lifetime or still being fetched .
2010-09-16 22:58:32 +04:00
*/
2013-01-07 22:45:29 +04:00
return ( ( cd - > no_cache = = LLCACHE_VALIDATE_FRESH ) & &
( ( remaining_lifetime > 0 ) | |
( object - > fetch . state ! = LLCACHE_FETCH_COMPLETE ) ) ) ;
2010-03-28 16:56:39 +04:00
}
/**
* Clone an object ' s cache data
*
2014-11-23 18:43:03 +03:00
* \ post If \ a deep is false , then any pointers in \ a source will be set to NULL
*
2010-03-28 16:56:39 +04:00
* \ param source Source object containing cache data to clone
* \ param destination Destination object to clone cache data into
2010-03-28 20:00:54 +04:00
* \ param deep Whether to deep - copy the data or not
2010-03-28 16:56:39 +04:00
* \ return NSERROR_OK on success , appropriate error otherwise
*/
2012-04-28 17:24:09 +04:00
static nserror llcache_object_clone_cache_data ( llcache_object * source ,
2010-03-28 16:56:39 +04:00
llcache_object * destination , bool deep )
{
/* ETag must be first, as it can fail when deep cloning */
if ( source - > cache . etag ! = NULL ) {
char * etag = source - > cache . etag ;
if ( deep ) {
/* Copy the etag */
etag = strdup ( source - > cache . etag ) ;
if ( etag = = NULL )
return NSERROR_NOMEM ;
2010-09-16 23:33:18 +04:00
} else {
/* Destination takes ownership */
source - > cache . etag = NULL ;
2010-03-28 16:56:39 +04:00
}
if ( destination - > cache . etag ! = NULL )
free ( destination - > cache . etag ) ;
destination - > cache . etag = etag ;
}
destination - > cache . req_time = source - > cache . req_time ;
destination - > cache . res_time = source - > cache . res_time ;
2014-03-22 18:54:51 +04:00
destination - > cache . fin_time = source - > cache . fin_time ;
2010-03-28 16:56:39 +04:00
if ( source - > cache . date ! = 0 )
destination - > cache . date = source - > cache . date ;
if ( source - > cache . expires ! = 0 )
destination - > cache . expires = source - > cache . expires ;
if ( source - > cache . age ! = INVALID_AGE )
destination - > cache . age = source - > cache . age ;
if ( source - > cache . max_age ! = INVALID_AGE )
destination - > cache . max_age = source - > cache . max_age ;
2011-02-25 00:22:24 +03:00
if ( source - > cache . no_cache ! = LLCACHE_VALIDATE_FRESH )
2010-03-28 16:56:39 +04:00
destination - > cache . no_cache = source - > cache . no_cache ;
2014-02-21 15:31:39 +04:00
2010-03-28 16:56:39 +04:00
if ( source - > cache . last_modified ! = 0 )
destination - > cache . last_modified = source - > cache . last_modified ;
return NSERROR_OK ;
}
2014-03-22 18:54:51 +04:00
/**
* Remove a low - level cache object from a cache list
*
* \ param object Object to remove
* \ param list List to remove from
* \ return NSERROR_OK
*/
static nserror
llcache_object_remove_from_list ( llcache_object * object , llcache_object * * list )
{
if ( object = = * list )
* list = object - > next ;
else
object - > prev - > next = object - > next ;
if ( object - > next ! = NULL )
object - > next - > prev = object - > prev ;
return NSERROR_OK ;
}
/**
* Retrieve source data for an object from persistant store if necessary .
*
* If an objects source data has been placed in the persistant store
2014-11-23 18:43:03 +03:00
* and the in memory copy released this will attempt to retrive the
2014-03-22 18:54:51 +04:00
* source data .
*
2014-11-23 18:43:03 +03:00
* \ param object the object to operate on .
* \ return apropriate error code .
2014-03-22 18:54:51 +04:00
*/
static nserror llcache_persist_retrieve ( llcache_object * object )
{
/* ensure the source data is present if necessary */
if ( ( object - > source_data ! = NULL ) | |
( object - > store_state ! = LLCACHE_STATE_DISC ) ) {
/* source data does not require retriving from
* persistant store .
*/
return NSERROR_OK ;
}
/* Source data for the object may be in the persiatant store */
return guit - > llcache - > fetch ( object - > url ,
2014-11-23 02:56:13 +03:00
BACKING_STORE_NONE ,
2014-03-22 18:54:51 +04:00
& object - > source_data ,
& object - > source_len ) ;
}
/**
* Generate a serialised version of an objects metadata
*
2014-11-23 18:43:03 +03:00
* The metadata includes object headers .
*
* \ param object The cache object to serialise teh metadata of .
* \ param data_out Where the serialised buffer will be placed .
* \ param datasize_out The size of the serialised data .
* \ return NSERROR_OK on success with \ a data_out and \ a datasize_out
* updated , NSERROR_NOMEM on memory exhaustion or
* NSERROR_INVALID if there was an error serialising the
* stream .
2014-03-22 18:54:51 +04:00
*/
static nserror
llcache_serialise_metadata ( llcache_object * object ,
uint8_t * * data_out ,
size_t * datasize_out )
{
size_t allocsize ;
int datasize ;
uint8_t * data ;
char * op ;
unsigned int hloop ;
int use ;
allocsize = 10 + 1 ; /* object length */
allocsize + = 10 + 1 ; /* request time */
allocsize + = 10 + 1 ; /* response time */
allocsize + = 10 + 1 ; /* completion time */
allocsize + = 10 + 1 ; /* space for number of header entries */
allocsize + = nsurl_length ( object - > url ) + 1 ;
for ( hloop = 0 ; hloop < object - > num_headers ; hloop + + ) {
allocsize + = strlen ( object - > headers [ hloop ] . name ) + 1 ;
allocsize + = strlen ( object - > headers [ hloop ] . value ) + 1 ;
}
data = malloc ( allocsize ) ;
if ( data = = NULL ) {
return NSERROR_NOMEM ;
}
op = ( char * ) data ;
datasize = allocsize ;
/* the url, used for checking for collisions */
2014-05-15 04:18:52 +04:00
use = snprintf ( op , datasize , " %s " , nsurl_access ( object - > url ) ) ;
if ( use < 0 ) {
goto operror ;
}
use + + ; /* does not count the null */
if ( use > datasize ) {
2014-03-22 18:54:51 +04:00
goto overflow ;
2014-05-15 04:18:52 +04:00
}
2014-03-22 18:54:51 +04:00
op + = use ;
datasize - = use ;
/* object size */
2014-05-15 04:18:52 +04:00
use = snprintf ( op , datasize , " %zu " , object - > source_len ) ;
if ( use < 0 ) {
goto operror ;
}
use + + ; /* does not count the null */
2014-03-22 18:54:51 +04:00
if ( use > datasize )
goto overflow ;
op + = use ;
datasize - = use ;
/* Time of request */
2014-05-15 04:18:52 +04:00
use = nsc_sntimet ( op , datasize , & object - > cache . req_time ) ;
2014-03-22 18:54:51 +04:00
if ( use = = 0 )
goto overflow ;
use + + ; /* does not count the null */
op + = use ;
datasize - = use ;
/* Time of response */
2014-05-15 04:18:52 +04:00
use = nsc_sntimet ( op , datasize , & object - > cache . res_time ) ;
2014-03-22 18:54:51 +04:00
if ( use = = 0 )
goto overflow ;
use + + ; /* does not count the null */
op + = use ;
datasize - = use ;
/* Time of completion */
2014-05-15 04:18:52 +04:00
use = nsc_sntimet ( op , datasize , & object - > cache . fin_time ) ;
2014-03-22 18:54:51 +04:00
if ( use = = 0 )
goto overflow ;
use + + ; /* does not count the null */
op + = use ;
datasize - = use ;
/* number of headers */
2014-05-15 04:18:52 +04:00
use = snprintf ( op , datasize , " %zu " , object - > num_headers ) ;
if ( use < 0 ) {
goto operror ;
}
use + + ; /* does not count the null */
2014-03-22 18:54:51 +04:00
if ( use > datasize )
goto overflow ;
op + = use ;
datasize - = use ;
/* headers */
for ( hloop = 0 ; hloop < object - > num_headers ; hloop + + ) {
use = snprintf ( op , datasize ,
2014-05-15 04:18:52 +04:00
" %s:%s " ,
2014-03-22 18:54:51 +04:00
object - > headers [ hloop ] . name ,
2014-05-15 04:18:52 +04:00
object - > headers [ hloop ] . value ) ;
if ( use < 0 ) {
goto operror ;
}
use + + ; /* does not count the null */
2014-03-22 18:54:51 +04:00
if ( use > datasize )
goto overflow ;
op + = use ;
datasize - = use ;
}
2015-05-28 19:06:18 +03:00
LLCACHE_LOG ( " Filled buffer with %d spare " , datasize ) ;
2014-03-22 18:54:51 +04:00
* data_out = data ;
* datasize_out = allocsize - datasize ;
return NSERROR_OK ;
overflow :
/* somehow we overflowed the buffer - hth? */
2015-05-28 18:08:46 +03:00
LOG ( " Overflowed metadata buffer " ) ;
2014-03-22 18:54:51 +04:00
free ( data ) ;
return NSERROR_INVALID ;
2014-05-15 04:18:52 +04:00
operror :
/* output error */
2015-05-28 18:08:46 +03:00
LOG ( " Output error " ) ;
2014-05-15 04:18:52 +04:00
free ( data ) ;
return NSERROR_INVALID ;
2014-03-22 18:54:51 +04:00
}
/**
2014-11-23 18:43:03 +03:00
* Deserialisation of an objects metadata .
*
* Attempt to retrive and deserialise the metadata for an object from
* the backing store .
*
2015-01-29 01:36:10 +03:00
* This must only update object if it is sucessful otherwise difficult
* to debug crashes happen later by using bad leftover object state .
*
2014-11-23 18:43:03 +03:00
* \ param object The object to retrieve the metadata for .
* \ return NSERROR_OK if the metatdata was retrived and deserialised
* or error code if url is not in persistant storage or in
* event of deserialisation error .
2014-03-22 18:54:51 +04:00
*/
static nserror
llcache_process_metadata ( llcache_object * object )
{
nserror res ;
uint8_t * metadata = NULL ;
size_t metadatalen = 0 ;
nsurl * metadataurl ;
unsigned int line ;
uint8_t * end ;
char * ln ;
int lnsize ;
2015-01-29 01:36:10 +03:00
size_t source_length ;
time_t request_time ;
time_t reponse_time ;
time_t completion_time ;
2014-03-22 18:54:51 +04:00
size_t num_headers ;
size_t hloop ;
2015-05-28 18:08:46 +03:00
LOG ( " Retriving metadata " ) ;
2014-03-22 18:54:51 +04:00
/* attempt to retrieve object metadata from the backing store */
res = guit - > llcache - > fetch ( object - > url ,
2014-11-23 02:56:13 +03:00
BACKING_STORE_META ,
2014-03-22 18:54:51 +04:00
& metadata ,
& metadatalen ) ;
if ( res ! = NSERROR_OK ) {
return res ;
}
end = metadata + metadatalen ;
2015-05-28 18:08:46 +03:00
LOG ( " Processing retrived data " ) ;
2014-03-22 18:54:51 +04:00
/* metadata line 1 is the url the metadata referrs to */
line = 1 ;
ln = ( char * ) metadata ;
lnsize = strlen ( ln ) ;
2015-01-29 01:36:10 +03:00
if ( lnsize < 7 ) {
res = NSERROR_INVALID ;
2014-03-22 18:54:51 +04:00
goto format_error ;
2015-01-29 01:36:10 +03:00
}
2014-03-22 18:54:51 +04:00
res = nsurl_create ( ln , & metadataurl ) ;
2015-01-29 01:36:10 +03:00
if ( res ! = NSERROR_OK )
goto format_error ;
2014-03-22 18:54:51 +04:00
if ( nsurl_compare ( object - > url , metadataurl , NSURL_COMPLETE ) ! = true ) {
/* backing store returned the wrong object for the
* request . This may occour if the backing store had
2014-11-23 02:56:13 +03:00
* a collision in its storage method . We cope with this
2014-03-22 18:54:51 +04:00
* by simply skipping caching of this object .
*/
2015-05-28 18:08:46 +03:00
LOG ( " Got metadata for %s instead of %s " , nsurl_access ( metadataurl ) , nsurl_access ( object - > url ) ) ;
2014-03-22 18:54:51 +04:00
nsurl_unref ( metadataurl ) ;
2014-11-23 02:56:13 +03:00
guit - > llcache - > release ( object - > url , BACKING_STORE_META ) ;
2014-03-22 18:54:51 +04:00
return NSERROR_BAD_URL ;
}
nsurl_unref ( metadataurl ) ;
/* metadata line 2 is the objects length */
line = 2 ;
ln + = lnsize + 1 ;
lnsize = strlen ( ln ) ;
2015-01-29 01:36:10 +03:00
if ( ( lnsize < 1 ) | | ( sscanf ( ln , " %zu " , & source_length ) ! = 1 ) ) {
res = NSERROR_INVALID ;
2014-03-22 18:54:51 +04:00
goto format_error ;
2015-01-29 01:36:10 +03:00
}
2014-03-22 18:54:51 +04:00
/* metadata line 3 is the time of request */
line = 3 ;
ln + = lnsize + 1 ;
lnsize = strlen ( ln ) ;
2015-01-29 01:36:10 +03:00
res = nsc_snptimet ( ln , lnsize , & request_time ) ;
if ( res ! = NSERROR_OK )
2014-03-22 18:54:51 +04:00
goto format_error ;
2015-01-29 01:36:10 +03:00
2014-03-22 18:54:51 +04:00
/* metadata line 4 is the time of response */
line = 4 ;
ln + = lnsize + 1 ;
lnsize = strlen ( ln ) ;
2015-01-29 01:36:10 +03:00
res = nsc_snptimet ( ln , lnsize , & reponse_time ) ;
if ( res ! = NSERROR_OK )
2014-03-22 18:54:51 +04:00
goto format_error ;
2015-01-29 01:36:10 +03:00
2014-03-22 18:54:51 +04:00
/* metadata line 5 is the time of request completion */
line = 5 ;
ln + = lnsize + 1 ;
lnsize = strlen ( ln ) ;
2015-01-29 01:36:10 +03:00
res = nsc_snptimet ( ln , lnsize , & completion_time ) ;
if ( res ! = NSERROR_OK )
2014-03-22 18:54:51 +04:00
goto format_error ;
2015-01-29 01:36:10 +03:00
2014-03-22 18:54:51 +04:00
/* metadata line 6 is the number of headers */
line = 6 ;
ln + = lnsize + 1 ;
lnsize = strlen ( ln ) ;
2015-01-29 01:36:10 +03:00
if ( ( lnsize < 1 ) | | ( sscanf ( ln , " %zu " , & num_headers ) ! = 1 ) ) {
res = NSERROR_INVALID ;
2014-03-22 18:54:51 +04:00
goto format_error ;
2015-01-29 01:36:10 +03:00
}
2014-03-22 18:54:51 +04:00
/* read headers */
for ( hloop = 0 ; hloop < num_headers ; hloop + + ) {
line + + ;
ln + = lnsize + 1 ;
lnsize = strlen ( ln ) ;
2015-01-29 01:36:10 +03:00
res = llcache_fetch_process_header ( object ,
( uint8_t * ) ln ,
lnsize ) ;
if ( res ! = NSERROR_OK )
goto format_error ;
2014-03-22 18:54:51 +04:00
}
2014-11-23 02:56:13 +03:00
guit - > llcache - > release ( object - > url , BACKING_STORE_META ) ;
2014-03-22 18:54:51 +04:00
2015-01-29 01:36:10 +03:00
/* update object on successful parse of metadata */
object - > source_len = source_length ;
/** \todo really not sure this is right, nothing is allocated here? */
object - > source_alloc = metadatalen ;
object - > cache . req_time = request_time ;
object - > cache . res_time = reponse_time ;
object - > cache . fin_time = completion_time ;
2014-03-22 18:54:51 +04:00
/* object stored in backing store */
object - > store_state = LLCACHE_STATE_DISC ;
return NSERROR_OK ;
format_error :
2015-05-28 18:08:46 +03:00
LOG ( " metadata error on line %d error code %d \n " , line , res ) ;
2014-11-23 02:56:13 +03:00
guit - > llcache - > release ( object - > url , BACKING_STORE_META ) ;
2015-01-29 01:36:10 +03:00
return res ;
2014-03-22 18:54:51 +04:00
}
/**
2014-11-23 18:43:03 +03:00
* Attempt to retrieve an object from persistant storage .
*
* \ param object The object to populate from persistant store .
* \ param flags Fetch flags .
* \ param referer The referring url .
* \ param post Post data for fetch .
* \ param redirect_count how many times this fetch has been redirected .
* \ return NSERROR_OK if the object was sucessfully retrived from the
* cache else appropriate error code .
2014-03-22 18:54:51 +04:00
*/
static nserror
llcache_object_fetch_persistant ( llcache_object * object ,
uint32_t flags ,
nsurl * referer ,
const llcache_post_data * post ,
uint32_t redirect_count )
{
nserror error ;
nsurl * referer_clone = NULL ;
llcache_post_data * post_clone = NULL ;
object - > cache . req_time = time ( NULL ) ;
object - > cache . fin_time = object - > cache . req_time ;
/* retrieve and process metadata */
error = llcache_process_metadata ( object ) ;
if ( error ! = NSERROR_OK ) {
return error ;
}
/* entry came out of cache - need to setup object state */
if ( post ! = NULL ) {
error = llcache_post_data_clone ( post , & post_clone ) ;
if ( error ! = NSERROR_OK )
return error ;
}
if ( referer ! = NULL ) {
referer_clone = nsurl_ref ( referer ) ;
}
object - > fetch . flags = flags ;
object - > fetch . referer = referer_clone ;
object - > fetch . post = post_clone ;
object - > fetch . redirect_count = redirect_count ;
/* fetch is "finished" */
object - > fetch . state = LLCACHE_FETCH_COMPLETE ;
object - > fetch . fetch = NULL ;
return NSERROR_OK ;
}
2010-03-28 16:56:39 +04:00
/**
2012-04-28 17:24:09 +04:00
* Retrieve a potentially cached object
2010-03-28 16:56:39 +04:00
*
2012-04-28 17:24:09 +04:00
* \ param url URL of object to retrieve
2010-04-14 14:56:44 +04:00
* \ param flags Fetch flags
2012-04-28 17:24:09 +04:00
* \ param referer Referring URL , or NULL if none
* \ param post POST data , or NULL for a GET request
2010-04-11 17:58:12 +04:00
* \ param redirect_count Number of redirects followed so far
2012-04-28 17:24:09 +04:00
* \ param result Pointer to location to recieve retrieved object
2010-03-28 16:56:39 +04:00
* \ return NSERROR_OK on success , appropriate error otherwise
*/
2014-03-22 18:54:51 +04:00
static nserror
llcache_object_retrieve_from_cache ( nsurl * url ,
uint32_t flags ,
nsurl * referer ,
const llcache_post_data * post ,
uint32_t redirect_count ,
llcache_object * * result )
2010-03-28 16:56:39 +04:00
{
nserror error ;
2012-04-28 17:24:09 +04:00
llcache_object * obj , * newest = NULL ;
2010-03-28 16:56:39 +04:00
2015-05-28 19:06:18 +03:00
LLCACHE_LOG ( " Searching cache for %s flags:%x referer:%s post:%p " ,
nsurl_access ( url ) , flags , referer = = NULL ? " " : nsurl_access ( referer ) , post ) ;
2010-03-29 10:27:37 +04:00
2012-04-28 17:24:09 +04:00
/* Search for the most recently fetched matching object */
for ( obj = llcache - > cached_objects ; obj ! = NULL ; obj = obj - > next ) {
2010-03-28 16:56:39 +04:00
2014-02-21 15:31:39 +04:00
if ( ( newest = = NULL | |
2014-03-22 18:54:51 +04:00
obj - > cache . req_time > newest - > cache . req_time ) & &
nsurl_compare ( obj - > url , url ,
NSURL_COMPLETE ) = = true ) {
2012-04-28 17:24:09 +04:00
newest = obj ;
}
2010-03-28 16:56:39 +04:00
}
2014-03-22 18:54:51 +04:00
/* No viable object found in cache create one and attempt to
* pull from persistant store .
*/
if ( newest = = NULL ) {
2015-05-28 19:06:18 +03:00
LLCACHE_LOG ( " No viable object found in llcache " ) ;
2014-03-22 18:54:51 +04:00
error = llcache_object_new ( url , & obj ) ;
if ( error ! = NSERROR_OK )
return error ;
/* attempt to retrieve object from persistant store */
error = llcache_object_fetch_persistant ( obj , flags , referer , post , redirect_count ) ;
if ( error = = NSERROR_OK ) {
2015-05-28 19:06:18 +03:00
LLCACHE_LOG ( " retrived object from persistant store " ) ;
2014-03-22 18:54:51 +04:00
2014-12-04 19:38:11 +03:00
/* set newest object from persistant store which
* will cause the normal object handling to be used .
*/
2014-03-22 18:54:51 +04:00
newest = obj ;
2010-03-28 16:56:39 +04:00
2014-03-22 18:54:51 +04:00
/* Add new object to cached object list */
llcache_object_add_to_list ( obj , & llcache - > cached_objects ) ;
}
/* else no object found and unretrivable from cache,
2014-12-04 19:38:11 +03:00
* fall through with newest unset to start fetch
2014-03-22 18:54:51 +04:00
*/
}
if ( ( newest ! = NULL ) & & ( llcache_object_is_fresh ( newest ) ) ) {
/* Found a suitable object, and it's still fresh */
2015-05-28 19:06:18 +03:00
LLCACHE_LOG ( " Found fresh %p " , newest ) ;
2010-03-28 16:56:39 +04:00
2012-04-28 17:24:09 +04:00
/* The client needs to catch up with the object's state.
* This will occur the next time that llcache_poll is called .
*/
2010-03-28 16:56:39 +04:00
2014-03-22 18:54:51 +04:00
/* ensure the source data is present */
error = llcache_persist_retrieve ( newest ) ;
if ( error = = NSERROR_OK ) {
/* source data was sucessfully retrived from
* persistant store
*/
* result = newest ;
2010-03-28 16:56:39 +04:00
2014-03-22 18:54:51 +04:00
return NSERROR_OK ;
}
/* retrival of source data from persistant store
* failed , destroy cache object and fall though to
2014-11-23 02:56:13 +03:00
* cache miss to re - fetch
2014-03-22 18:54:51 +04:00
*/
2015-05-28 19:06:18 +03:00
LLCACHE_LOG ( " Persistant retrival failed for %p " , newest ) ;
2014-03-22 18:54:51 +04:00
llcache_object_remove_from_list ( newest , & llcache - > cached_objects ) ;
llcache_object_destroy ( newest ) ;
2010-03-28 16:56:39 +04:00
2014-03-22 18:54:51 +04:00
error = llcache_object_new ( url , & obj ) ;
2012-04-28 17:24:09 +04:00
if ( error ! = NSERROR_OK ) {
return error ;
2014-02-21 15:31:39 +04:00
}
2014-03-22 18:54:51 +04:00
} else if ( newest ! = NULL ) {
/* Found a candidate object but it needs freshness validation */
2010-03-28 16:56:39 +04:00
2014-03-22 18:54:51 +04:00
/* ensure the source data is present */
error = llcache_persist_retrieve ( newest ) ;
if ( error = = NSERROR_OK ) {
2010-03-28 16:56:39 +04:00
2014-03-22 18:54:51 +04:00
/* Create a new object */
error = llcache_object_new ( url , & obj ) ;
if ( error ! = NSERROR_OK )
return error ;
2015-05-28 19:06:18 +03:00
LLCACHE_LOG ( " Found candidate %p (%p) " , obj , newest ) ;
2014-03-22 18:54:51 +04:00
/* Clone candidate's cache data */
error = llcache_object_clone_cache_data ( newest , obj , true ) ;
if ( error ! = NSERROR_OK ) {
llcache_object_destroy ( obj ) ;
return error ;
}
/* Record candidate, so we can fall back if it is still fresh */
newest - > candidate_count + + ;
obj - > candidate = newest ;
/* Attempt to kick-off fetch */
error = llcache_object_fetch ( obj , flags , referer , post ,
redirect_count ) ;
if ( error ! = NSERROR_OK ) {
newest - > candidate_count - - ;
llcache_object_destroy ( obj ) ;
return error ;
}
/* Add new object to cache */
llcache_object_add_to_list ( obj , & llcache - > cached_objects ) ;
* result = obj ;
return NSERROR_OK ;
2012-04-28 17:24:09 +04:00
}
2010-03-28 16:56:39 +04:00
2015-05-28 19:06:18 +03:00
LLCACHE_LOG ( " Persistant retrival failed for %p " , newest ) ;
2010-04-17 00:08:27 +04:00
2014-03-22 18:54:51 +04:00
/* retrival of source data from persistant store
* failed , destroy cache object and fall though to
* cache miss to re - retch
*/
llcache_object_remove_from_list ( newest ,
& llcache - > cached_objects ) ;
llcache_object_destroy ( newest ) ;
2010-03-29 10:27:37 +04:00
2014-03-22 18:54:51 +04:00
error = llcache_object_new ( url , & obj ) ;
2012-04-28 17:24:09 +04:00
if ( error ! = NSERROR_OK ) {
return error ;
}
2014-03-22 18:54:51 +04:00
}
2010-03-28 16:56:39 +04:00
2014-03-22 18:54:51 +04:00
/* Attempt to kick-off fetch */
error = llcache_object_fetch ( obj , flags , referer , post , redirect_count ) ;
if ( error ! = NSERROR_OK ) {
llcache_object_destroy ( obj ) ;
return error ;
2012-04-28 17:24:09 +04:00
}
2010-03-28 16:56:39 +04:00
2014-03-22 18:54:51 +04:00
/* Add new object to cache */
llcache_object_add_to_list ( obj , & llcache - > cached_objects ) ;
2012-04-28 17:24:09 +04:00
* result = obj ;
2010-03-28 16:56:39 +04:00
return NSERROR_OK ;
}
/**
2012-04-28 17:24:09 +04:00
* Retrieve an object from the cache , fetching it if necessary .
2010-03-28 16:56:39 +04:00
*
2012-04-28 17:24:09 +04:00
* \ param url URL of object to retrieve
* \ param flags Fetch flags
* \ param referer Referring URL , or NULL if none
* \ param post POST data , or NULL for a GET request
* \ param redirect_count Number of redirects followed so far
* \ param result Pointer to location to recieve retrieved object
2010-03-28 16:56:39 +04:00
* \ return NSERROR_OK on success , appropriate error otherwise
*/
2015-06-24 12:31:13 +03:00
static nserror
llcache_object_retrieve ( nsurl * url ,
uint32_t flags ,
nsurl * referer ,
const llcache_post_data * post ,
uint32_t redirect_count ,
llcache_object * * result )
2010-03-28 16:56:39 +04:00
{
2012-04-28 17:24:09 +04:00
nserror error ;
llcache_object * obj ;
nsurl * defragmented_url ;
2014-03-07 03:55:40 +04:00
bool uncachable = false ;
2010-03-28 16:56:39 +04:00
2015-05-28 19:06:18 +03:00
LLCACHE_LOG ( " Retrieve %s (%x, %s, %p) " , nsurl_access ( url ) , flags ,
referer = = NULL ? " " : nsurl_access ( referer ) , post ) ;
2010-03-29 10:27:37 +04:00
2012-04-28 17:24:09 +04:00
/* Get rid of any url fragment */
2014-03-05 19:10:02 +04:00
error = nsurl_defragment ( url , & defragmented_url ) ;
if ( error ! = NSERROR_OK )
return error ;
2010-03-28 16:56:39 +04:00
2014-03-07 03:55:40 +04:00
/* determine if content is cachable */
if ( ( flags & LLCACHE_RETRIEVE_FORCE_FETCH ) ! = 0 ) {
/* Forced fetches are never cached */
uncachable = true ;
} else if ( post ! = NULL ) {
/* POST requests are never cached */
uncachable = true ;
} else {
/* only http and https schemes are cached */
lwc_string * scheme ;
bool match ;
scheme = nsurl_get_component ( defragmented_url , NSURL_SCHEME ) ;
if ( lwc_string_caseless_isequal ( scheme , corestring_lwc_http ,
& match ) = = lwc_error_ok & &
( match = = false ) ) {
if ( lwc_string_caseless_isequal ( scheme , corestring_lwc_https ,
& match ) = = lwc_error_ok & &
( match = = false ) ) {
uncachable = true ;
}
}
2014-05-16 21:58:06 +04:00
lwc_string_unref ( scheme ) ;
2014-03-07 03:55:40 +04:00
}
if ( uncachable ) {
2012-04-28 17:24:09 +04:00
/* Create new object */
error = llcache_object_new ( defragmented_url , & obj ) ;
if ( error ! = NSERROR_OK ) {
nsurl_unref ( defragmented_url ) ;
return error ;
2010-03-28 16:56:39 +04:00
}
2012-04-28 17:24:09 +04:00
/* Attempt to kick-off fetch */
2014-02-21 15:31:39 +04:00
error = llcache_object_fetch ( obj , flags , referer , post ,
2012-04-28 17:24:09 +04:00
redirect_count ) ;
if ( error ! = NSERROR_OK ) {
llcache_object_destroy ( obj ) ;
nsurl_unref ( defragmented_url ) ;
return error ;
}
2010-03-28 16:56:39 +04:00
2012-04-28 17:24:09 +04:00
/* Add new object to uncached list */
llcache_object_add_to_list ( obj , & llcache - > uncached_objects ) ;
} else {
error = llcache_object_retrieve_from_cache ( defragmented_url ,
flags , referer , post , redirect_count , & obj ) ;
if ( error ! = NSERROR_OK ) {
nsurl_unref ( defragmented_url ) ;
return error ;
}
2010-03-28 16:56:39 +04:00
2012-04-28 17:24:09 +04:00
/* Returned object is already in the cached list */
2010-03-28 16:56:39 +04:00
}
2014-02-21 15:31:39 +04:00
2015-05-28 19:06:18 +03:00
LLCACHE_LOG ( " Retrieved %p " , obj ) ;
2014-02-21 15:31:39 +04:00
2012-04-28 17:24:09 +04:00
* result = obj ;
2014-02-21 15:31:39 +04:00
2012-04-28 17:24:09 +04:00
nsurl_unref ( defragmented_url ) ;
2014-02-21 15:31:39 +04:00
2010-03-28 16:56:39 +04:00
return NSERROR_OK ;
}
2014-11-23 18:43:03 +03:00
2010-03-28 16:56:39 +04:00
/**
* Add a user to a low - level cache object
*
2014-11-23 18:43:03 +03:00
* \ param object Object to add user to
* \ param user User to add
2010-03-28 16:56:39 +04:00
* \ return NSERROR_OK .
*/
2012-04-28 17:24:09 +04:00
static nserror llcache_object_add_user ( llcache_object * object ,
2010-03-28 16:56:39 +04:00
llcache_object_user * user )
{
2010-04-09 04:06:36 +04:00
assert ( user - > next = = NULL ) ;
assert ( user - > prev = = NULL ) ;
2013-05-04 11:52:12 +04:00
assert ( user - > handle ! = NULL ) ;
2010-04-09 04:06:36 +04:00
2010-12-16 00:15:49 +03:00
user - > handle - > object = object ;
2010-03-28 16:56:39 +04:00
user - > prev = NULL ;
user - > next = object - > users ;
if ( object - > users ! = NULL )
object - > users - > prev = user ;
object - > users = user ;
2015-05-28 19:06:18 +03:00
LLCACHE_LOG ( " Adding user %p to %p " , user , object ) ;
2010-03-29 10:27:37 +04:00
2010-03-28 16:56:39 +04:00
return NSERROR_OK ;
}
/**
2012-04-28 17:24:09 +04:00
* Handle FETCH_REDIRECT event
2010-03-28 16:56:39 +04:00
*
2012-04-28 17:24:09 +04:00
* \ param object Object being redirected
* \ param target Target of redirect ( may be relative )
* \ param replacement Pointer to location to receive replacement object
* \ return NSERROR_OK on success , appropriate error otherwise
2010-03-28 16:56:39 +04:00
*/
2012-04-28 17:24:09 +04:00
static nserror llcache_fetch_redirect ( llcache_object * object , const char * target ,
llcache_object * * replacement )
2010-03-28 16:56:39 +04:00
{
2012-04-28 17:24:09 +04:00
nserror error ;
llcache_object * dest ;
llcache_object_user * user , * next ;
const llcache_post_data * post = object - > fetch . post ;
nsurl * url ;
lwc_string * scheme ;
lwc_string * object_scheme ;
bool match ;
/* Extract HTTP response code from the fetch object */
long http_code = fetch_http_code ( object - > fetch . fetch ) ;
2013-05-27 17:51:00 +04:00
llcache_event event ;
2010-03-28 16:56:39 +04:00
2012-04-28 17:24:09 +04:00
/* Abort fetch for this object */
fetch_abort ( object - > fetch . fetch ) ;
object - > fetch . fetch = NULL ;
2014-02-21 15:31:39 +04:00
2012-04-28 17:24:09 +04:00
/* Invalidate the cache control data */
llcache_invalidate_cache_control_data ( object ) ;
/* And mark it complete */
object - > fetch . state = LLCACHE_FETCH_COMPLETE ;
2014-02-21 15:31:39 +04:00
2012-04-28 17:24:09 +04:00
/* Forcibly stop redirecting if we've followed too many redirects */
# define REDIRECT_LIMIT 10
if ( object - > fetch . redirect_count > REDIRECT_LIMIT ) {
2015-05-28 18:08:46 +03:00
LOG ( " Too many nested redirects " ) ;
2010-03-28 16:56:39 +04:00
2012-04-28 17:24:09 +04:00
event . type = LLCACHE_EVENT_ERROR ;
event . data . msg = messages_get ( " BadRedirect " ) ;
2014-02-21 15:31:39 +04:00
2012-04-28 17:24:09 +04:00
return llcache_send_event_to_users ( object , & event ) ;
}
# undef REDIRECT_LIMIT
2010-12-16 00:15:49 +03:00
2012-04-28 17:24:09 +04:00
/* Make target absolute */
error = nsurl_join ( object - > url , target , & url ) ;
if ( error ! = NSERROR_OK )
return error ;
2010-12-16 00:15:49 +03:00
2013-05-27 17:51:00 +04:00
/* Inform users of redirect */
event . type = LLCACHE_EVENT_REDIRECT ;
event . data . redirect . from = object - > url ;
event . data . redirect . to = url ;
error = llcache_send_event_to_users ( object , & event ) ;
if ( error ! = NSERROR_OK ) {
nsurl_unref ( url ) ;
return error ;
}
2012-04-28 17:24:09 +04:00
/* Reject attempts to redirect from unvalidated to validated schemes
* A " validated " scheme is one over which we have some guarantee that
* the source is trustworthy . */
object_scheme = nsurl_get_component ( object - > url , NSURL_SCHEME ) ;
scheme = nsurl_get_component ( url , NSURL_SCHEME ) ;
/* resource: and about: are allowed to redirect anywhere */
2014-01-25 00:19:20 +04:00
if ( ( lwc_string_isequal ( object_scheme , corestring_lwc_resource ,
2012-04-28 17:24:09 +04:00
& match ) = = lwc_error_ok & & match = = false ) & &
2014-01-25 00:19:20 +04:00
( lwc_string_isequal ( object_scheme , corestring_lwc_about ,
2012-04-28 17:24:09 +04:00
& match ) = = lwc_error_ok & & match = = false ) ) {
/* file, about and resource are not valid redirect targets */
2014-01-25 00:19:20 +04:00
if ( ( lwc_string_isequal ( object_scheme , corestring_lwc_file ,
2012-04-28 17:24:09 +04:00
& match ) = = lwc_error_ok & & match = = true ) | |
2014-01-25 00:19:20 +04:00
( lwc_string_isequal ( object_scheme , corestring_lwc_about ,
2012-04-28 17:24:09 +04:00
& match ) = = lwc_error_ok & & match = = true ) | |
2014-01-25 00:19:20 +04:00
( lwc_string_isequal ( object_scheme , corestring_lwc_resource ,
2012-04-28 17:24:09 +04:00
& match ) = = lwc_error_ok & & match = = true ) ) {
lwc_string_unref ( object_scheme ) ;
lwc_string_unref ( scheme ) ;
nsurl_unref ( url ) ;
return NSERROR_OK ;
}
2010-12-16 00:15:49 +03:00
}
2012-04-28 17:24:09 +04:00
lwc_string_unref ( scheme ) ;
lwc_string_unref ( object_scheme ) ;
2010-12-16 00:15:49 +03:00
2012-04-28 17:24:09 +04:00
/* Bail out if we've no way of handling this URL */
if ( fetch_can_fetch ( url ) = = false ) {
nsurl_unref ( url ) ;
return NSERROR_OK ;
}
2010-03-28 16:56:39 +04:00
2012-04-28 17:24:09 +04:00
if ( http_code = = 301 | | http_code = = 302 | | http_code = = 303 ) {
/* 301, 302, 303 redirects are all unconditional GET requests */
post = NULL ;
} else if ( http_code ! = 307 | | post ! = NULL ) {
/** \todo 300, 305, 307 with POST */
nsurl_unref ( url ) ;
return NSERROR_OK ;
}
2010-03-28 16:56:39 +04:00
2012-04-28 17:24:09 +04:00
/* Attempt to fetch target URL */
error = llcache_object_retrieve ( url , object - > fetch . flags ,
2014-02-21 15:31:39 +04:00
object - > fetch . referer , post ,
2012-04-28 17:24:09 +04:00
object - > fetch . redirect_count + 1 , & dest ) ;
2010-03-28 16:56:39 +04:00
2012-04-28 17:24:09 +04:00
/* No longer require url */
nsurl_unref ( url ) ;
if ( error ! = NSERROR_OK )
return error ;
/* Move user(s) to replacement object */
for ( user = object - > users ; user ! = NULL ; user = next ) {
next = user - > next ;
llcache_object_remove_user ( object , user ) ;
llcache_object_add_user ( dest , user ) ;
}
/* Dest is now our object */
* replacement = dest ;
2014-02-21 15:31:39 +04:00
return NSERROR_OK ;
2012-04-28 17:24:09 +04:00
}
/**
* Update an object ' s cache state
*
* \ param object Object to update cache for
* \ return NSERROR_OK .
2010-03-28 16:56:39 +04:00
*/
2012-04-28 17:24:09 +04:00
static nserror llcache_object_cache_update ( llcache_object * object )
2010-03-28 16:56:39 +04:00
{
2012-04-28 17:24:09 +04:00
if ( object - > cache . date = = 0 )
object - > cache . date = time ( NULL ) ;
2010-03-28 16:56:39 +04:00
2012-04-28 17:24:09 +04:00
return NSERROR_OK ;
}
/**
* Handle FETCH_NOTMODIFIED event
*
* \ param object Object to process
* \ param replacement Pointer to location to receive replacement object
* \ return NSERROR_OK .
*/
static nserror llcache_fetch_notmodified ( llcache_object * object ,
llcache_object * * replacement )
{
/* There may be no candidate if the server erroneously responded
* to an unconditional request with a 304 Not Modified response .
* In this case , we simply retain the initial object , having
* invalidated it and marked it as complete .
*/
if ( object - > candidate ! = NULL ) {
llcache_object_user * user , * next ;
/* Move user(s) to candidate content */
for ( user = object - > users ; user ! = NULL ; user = next ) {
next = user - > next ;
llcache_object_remove_user ( object , user ) ;
llcache_object_add_user ( object - > candidate , user ) ;
}
/* Candidate is no longer a candidate for us */
object - > candidate - > candidate_count - - ;
/* Clone our cache control data into the candidate */
2014-02-21 15:31:39 +04:00
llcache_object_clone_cache_data ( object , object - > candidate ,
2012-04-28 17:24:09 +04:00
false ) ;
/* Bring candidate's cache data up to date */
llcache_object_cache_update ( object - > candidate ) ;
/* Revert no-cache to normal, if required */
2014-02-21 15:31:39 +04:00
if ( object - > candidate - > cache . no_cache = =
2012-04-28 17:24:09 +04:00
LLCACHE_VALIDATE_ONCE ) {
2014-02-21 15:31:39 +04:00
object - > candidate - > cache . no_cache =
2012-04-28 17:24:09 +04:00
LLCACHE_VALIDATE_FRESH ;
}
/* Candidate is now our object */
* replacement = object - > candidate ;
object - > candidate = NULL ;
} else {
/* There was no candidate: retain object */
* replacement = object ;
}
/* Ensure fetch has stopped */
fetch_abort ( object - > fetch . fetch ) ;
object - > fetch . fetch = NULL ;
/* Invalidate our cache-control data */
llcache_invalidate_cache_control_data ( object ) ;
/* Mark it complete */
object - > fetch . state = LLCACHE_FETCH_COMPLETE ;
/* Old object will be flushed from the cache on the next poll */
2010-03-28 16:56:39 +04:00
return NSERROR_OK ;
}
2010-04-06 01:35:38 +04:00
/**
2012-04-28 17:24:09 +04:00
* Process a chunk of fetched data
2010-04-06 01:35:38 +04:00
*
2012-04-28 17:24:09 +04:00
* \ param object Object being fetched
* \ param data Data to process
* \ param len Byte length of data
* \ return NSERROR_OK on success , appropriate error otherwise .
2010-04-06 01:35:38 +04:00
*/
2014-05-29 17:00:13 +04:00
static nserror
llcache_fetch_process_data ( llcache_object * object ,
const uint8_t * data ,
size_t len )
2010-04-06 01:35:38 +04:00
{
2014-05-29 17:00:13 +04:00
if ( object - > fetch . state ! = LLCACHE_FETCH_DATA ) {
2014-11-23 18:43:03 +03:00
/**
* \ note
* On entry into this state , check if we need to
2014-05-29 17:00:13 +04:00
* invalidate the cache control data . We are guaranteed
* to have received all response headers .
*
* There are two cases in which we want to suppress
* cacheing of an object :
*
* 1 ) The HTTP response code is not 200 or 203
* 2 ) The request URI had a query string and the
* response headers did not provide an explicit
* object expiration time .
*/
long http_code = fetch_http_code ( object - > fetch . fetch ) ;
if ( ( http_code ! = 200 & & http_code ! = 203 ) | |
( nsurl_has_component ( object - > url , NSURL_QUERY ) & &
( object - > cache . max_age = = INVALID_AGE & &
object - > cache . expires = = 0 ) ) ) {
/* Invalidate cache control data */
llcache_invalidate_cache_control_data ( object ) ;
}
/* Release candidate, if any */
if ( object - > candidate ! = NULL ) {
object - > candidate - > candidate_count - - ;
object - > candidate = NULL ;
}
object - > fetch . state = LLCACHE_FETCH_DATA ;
}
2012-04-28 17:24:09 +04:00
/* Resize source buffer if it's too small */
if ( object - > source_len + len > = object - > source_alloc ) {
const size_t new_len = object - > source_len + len + 64 * 1024 ;
uint8_t * temp = realloc ( object - > source_data , new_len ) ;
if ( temp = = NULL )
return NSERROR_NOMEM ;
2010-04-06 01:35:38 +04:00
2012-04-28 17:24:09 +04:00
object - > source_data = temp ;
object - > source_alloc = new_len ;
2010-04-06 01:35:38 +04:00
}
2012-04-28 17:24:09 +04:00
/* Append this data chunk to source buffer */
memcpy ( object - > source_data + object - > source_len , data , len ) ;
object - > source_len + = len ;
return NSERROR_OK ;
2010-04-06 01:35:38 +04:00
}
2010-03-28 16:56:39 +04:00
/**
2012-04-28 17:24:09 +04:00
* Handle a query response
2010-03-28 16:56:39 +04:00
*
2012-04-28 17:24:09 +04:00
* \ param proceed Whether to proceed with fetch
* \ param cbpw Our context for query
2010-03-28 16:56:39 +04:00
* \ return NSERROR_OK on success , appropriate error otherwise
*/
2012-04-28 17:24:09 +04:00
static nserror llcache_query_handle_response ( bool proceed , void * cbpw )
2010-03-28 16:56:39 +04:00
{
llcache_event event ;
2012-04-28 17:24:09 +04:00
llcache_object * object = cbpw ;
2010-03-28 16:56:39 +04:00
2012-04-28 17:24:09 +04:00
object - > fetch . outstanding_query = false ;
2010-03-29 10:27:37 +04:00
2012-04-28 17:24:09 +04:00
/* Refetch, using existing fetch parameters, if client allows us to */
if ( proceed )
return llcache_object_refetch ( object ) ;
2010-03-28 16:56:39 +04:00
2012-04-28 17:24:09 +04:00
/* Invalidate cache-control data */
llcache_invalidate_cache_control_data ( object ) ;
2010-03-28 16:56:39 +04:00
2012-04-28 17:24:09 +04:00
/* Mark it complete */
object - > fetch . state = LLCACHE_FETCH_COMPLETE ;
2010-12-13 02:37:21 +03:00
2012-04-28 17:24:09 +04:00
/* Inform client(s) that object fetch failed */
event . type = LLCACHE_EVENT_ERROR ;
/** \todo More appropriate error message */
event . data . msg = messages_get ( " FetchFailed " ) ;
2014-02-21 15:31:39 +04:00
2012-04-28 17:24:09 +04:00
return llcache_send_event_to_users ( object , & event ) ;
}
2010-03-28 16:56:39 +04:00
2012-04-28 17:24:09 +04:00
/**
* Handle an authentication request
*
* \ param object Object being fetched
* \ param realm Authentication realm
* \ return NSERROR_OK on success , appropriate error otherwise .
*/
static nserror llcache_fetch_auth ( llcache_object * object , const char * realm )
{
const char * auth ;
nserror error = NSERROR_OK ;
2010-08-11 01:46:45 +04:00
2012-04-28 17:24:09 +04:00
/* Abort fetch for this object */
fetch_abort ( object - > fetch . fetch ) ;
object - > fetch . fetch = NULL ;
2010-03-29 10:27:37 +04:00
2012-04-28 17:24:09 +04:00
/* Invalidate cache-control data */
llcache_invalidate_cache_control_data ( object ) ;
2010-03-28 16:56:39 +04:00
2012-04-28 17:24:09 +04:00
/* Destroy headers */
llcache_destroy_headers ( object ) ;
2010-04-04 16:41:19 +04:00
2012-04-28 17:24:09 +04:00
/* If there was no realm, then default to the URL */
/** \todo If there was no WWW-Authenticate header, use response body */
if ( realm = = NULL )
realm = nsurl_access ( object - > url ) ;
2010-03-28 16:56:39 +04:00
2012-10-09 14:20:21 +04:00
auth = urldb_get_auth_details ( object - > url , realm ) ;
2010-03-28 16:56:39 +04:00
2012-04-28 17:24:09 +04:00
if ( auth = = NULL | | object - > fetch . tried_with_auth = = true ) {
/* No authentication details, or tried what we had, so ask */
object - > fetch . tried_with_auth = false ;
2011-09-08 12:26:16 +04:00
2012-04-28 17:24:09 +04:00
if ( llcache - > query_cb ! = NULL ) {
llcache_query query ;
2010-03-28 16:56:39 +04:00
2012-04-28 17:24:09 +04:00
/* Emit query for authentication details */
query . type = LLCACHE_QUERY_AUTH ;
query . url = object - > url ;
query . data . auth . realm = realm ;
2011-09-04 10:28:09 +04:00
2012-04-28 17:24:09 +04:00
object - > fetch . outstanding_query = true ;
2010-04-04 16:41:19 +04:00
2014-02-21 15:31:39 +04:00
error = llcache - > query_cb ( & query , llcache - > query_cb_pw ,
2012-04-28 17:24:09 +04:00
llcache_query_handle_response , object ) ;
} else {
llcache_event event ;
2010-03-28 16:56:39 +04:00
2012-04-28 17:24:09 +04:00
/* Mark object complete */
object - > fetch . state = LLCACHE_FETCH_COMPLETE ;
2010-12-16 01:59:19 +03:00
2012-04-28 17:24:09 +04:00
/* Inform client(s) that object fetch failed */
event . type = LLCACHE_EVENT_ERROR ;
/** \todo More appropriate error message */
event . data . msg = messages_get ( " FetchFailed " ) ;
2014-02-21 15:31:39 +04:00
2012-04-28 17:24:09 +04:00
error = llcache_send_event_to_users ( object , & event ) ;
}
} else {
/* Flag that we've tried to refetch with credentials, so
* that if the fetch fails again , we ask the user again */
object - > fetch . tried_with_auth = true ;
error = llcache_object_refetch ( object ) ;
}
2010-12-16 01:59:19 +03:00
2012-04-28 17:24:09 +04:00
return error ;
}
2011-09-04 10:28:09 +04:00
2012-04-28 17:24:09 +04:00
/**
* Handle a TLS certificate verification failure
*
* \ param object Object being fetched
* \ param certs Certificate chain
* \ param num Number of certificates in chain
* \ return NSERROR_OK on success , appropriate error otherwise
*/
static nserror llcache_fetch_cert_error ( llcache_object * object ,
const struct ssl_cert_info * certs , size_t num )
{
nserror error = NSERROR_OK ;
2010-03-28 16:56:39 +04:00
2012-04-28 17:24:09 +04:00
/* Fetch has been stopped, and destroyed. Invalidate object's pointer */
object - > fetch . fetch = NULL ;
2010-04-04 16:41:19 +04:00
2012-04-28 17:24:09 +04:00
/* Invalidate cache-control data */
llcache_invalidate_cache_control_data ( object ) ;
2010-03-28 16:56:39 +04:00
2012-04-28 17:24:09 +04:00
if ( llcache - > query_cb ! = NULL ) {
llcache_query query ;
2010-12-16 01:59:19 +03:00
2012-04-28 17:24:09 +04:00
/* Emit query for TLS */
query . type = LLCACHE_QUERY_SSL ;
query . url = object - > url ;
query . data . ssl . certs = certs ;
query . data . ssl . num = num ;
2010-12-16 01:59:19 +03:00
2012-04-28 17:24:09 +04:00
object - > fetch . outstanding_query = true ;
2011-09-08 12:26:16 +04:00
2012-04-28 17:24:09 +04:00
error = llcache - > query_cb ( & query , llcache - > query_cb_pw ,
llcache_query_handle_response , object ) ;
} else {
llcache_event event ;
2010-03-28 16:56:39 +04:00
2012-04-28 17:24:09 +04:00
/* Mark object complete */
object - > fetch . state = LLCACHE_FETCH_COMPLETE ;
2010-12-13 02:37:21 +03:00
2012-04-28 17:24:09 +04:00
/* Inform client(s) that object fetch failed */
event . type = LLCACHE_EVENT_ERROR ;
/** \todo More appropriate error message */
event . data . msg = messages_get ( " FetchFailed " ) ;
2014-02-21 15:31:39 +04:00
2012-04-28 17:24:09 +04:00
error = llcache_send_event_to_users ( object , & event ) ;
2010-03-28 16:56:39 +04:00
}
2012-04-28 17:24:09 +04:00
return error ;
2010-03-28 16:56:39 +04:00
}
2013-01-05 03:13:23 +04:00
/**
* Handle a TLS connection setup failure
*
* \ param object Object being fetched
* \ return NSERROR_OK on success , appropriate error otherwise
*/
static nserror llcache_fetch_ssl_error ( llcache_object * object )
{
nserror error = NSERROR_OK ;
/* Fetch has been stopped, and destroyed. Invalidate object's pointer */
object - > fetch . fetch = NULL ;
/* Invalidate cache-control data */
llcache_invalidate_cache_control_data ( object ) ;
if ( object - > fetch . tried_with_tls_downgrade = = true ) {
/* Have already tried to downgrade, so give up */
llcache_event event ;
/* Mark object complete */
object - > fetch . state = LLCACHE_FETCH_COMPLETE ;
/* Inform client(s) that object fetch failed */
event . type = LLCACHE_EVENT_ERROR ;
/** \todo More appropriate error message */
event . data . msg = messages_get ( " FetchFailed " ) ;
2014-02-21 15:31:39 +04:00
2013-01-05 03:13:23 +04:00
error = llcache_send_event_to_users ( object , & event ) ;
} else {
2013-01-07 22:45:29 +04:00
/* Flag that we've tried to downgrade, so that if the
2013-01-05 03:13:23 +04:00
* fetch fails again , we give up */
object - > fetch . tried_with_tls_downgrade = true ;
error = llcache_object_refetch ( object ) ;
}
return error ;
}
2014-03-22 18:54:51 +04:00
/**
2014-11-23 18:43:03 +03:00
* Construct a sorted list of objects available for writeout operation .
2014-03-22 18:54:51 +04:00
*
* The list contains fresh cacheable objects held in RAM with no
* pending fetches . Any objects with a remaining lifetime less than
* the configured minimum lifetime are simply not considered , they will
* become stale before pushing to backing store is worth the cost .
*
* \ todo calculate useful cost metrics to improve sorting .
*
2014-11-23 18:43:03 +03:00
* \ param [ out ] lst_out list of candidate objects .
* \ param [ out ] lst_len_out Number of candidate objects in result .
* \ return NSERROR_OK with \ a lst_out and \ a lst_len_out updated or
* error code .
2014-03-22 18:54:51 +04:00
*/
static nserror
build_candidate_list ( struct llcache_object * * * lst_out , int * lst_len_out )
{
llcache_object * object , * next ;
struct llcache_object * * lst ;
int lst_len = 0 ;
int remaining_lifetime ;
2014-11-30 02:56:33 +03:00
# define MAX_PERSIST_PER_RUN 128
2014-06-04 01:30:04 +04:00
lst = calloc ( MAX_PERSIST_PER_RUN , sizeof ( struct llcache_object * ) ) ;
2014-11-28 22:17:42 +03:00
if ( lst = = NULL ) {
2014-03-22 18:54:51 +04:00
return NSERROR_NOMEM ;
2014-11-28 22:17:42 +03:00
}
2014-03-22 18:54:51 +04:00
for ( object = llcache - > cached_objects ; object ! = NULL ; object = next ) {
next = object - > next ;
remaining_lifetime = llcache_object_rfc2616_remaining_lifetime ( & object - > cache ) ;
/* cacehable objects with no pending fetches, not
* already on disc and with sufficient lifetime to
* make disc cache worthwile
*/
if ( ( object - > candidate_count = = 0 ) & &
( object - > fetch . fetch = = NULL ) & &
( object - > fetch . outstanding_query = = false ) & &
( object - > store_state = = LLCACHE_STATE_RAM ) & &
( remaining_lifetime > llcache - > minimum_lifetime ) ) {
lst [ lst_len ] = object ;
lst_len + + ;
2014-06-04 01:30:04 +04:00
if ( lst_len = = MAX_PERSIST_PER_RUN )
2014-03-22 18:54:51 +04:00
break ;
}
}
if ( lst_len = = 0 ) {
free ( lst ) ;
return NSERROR_NOT_FOUND ;
}
2014-06-04 01:30:04 +04:00
/** \todo sort list here */
2014-03-22 18:54:51 +04:00
* lst_len_out = lst_len ;
* lst_out = lst ;
2014-06-04 01:30:04 +04:00
# undef MAX_PERSIST_PER_RUN
2014-03-22 18:54:51 +04:00
return NSERROR_OK ;
}
2014-11-23 18:43:03 +03:00
/**
* Write an object to the backing store .
*
* \ param object The object to put in the backing store .
* \ param written_out The amount of data written out .
2014-11-27 16:31:43 +03:00
* \ param elapsed The time in ms it took to complete the write to backing store .
2014-11-23 18:43:03 +03:00
* \ return NSERROR_OK on success or appropriate error code .
*/
2014-03-22 18:54:51 +04:00
static nserror
2014-11-27 16:31:43 +03:00
write_backing_store ( struct llcache_object * object ,
size_t * written_out ,
unsigned long * elapsed )
2014-03-22 18:54:51 +04:00
{
nserror ret ;
uint8_t * metadata ;
size_t metadatasize ;
2014-11-27 00:29:13 +03:00
uint64_t startms = 0 ;
uint64_t endms = 1000 ;
2014-11-26 02:11:41 +03:00
2014-11-27 00:29:13 +03:00
nsu_getmonotonic_ms ( & startms ) ;
2014-03-22 18:54:51 +04:00
/* put object data in backing store */
ret = guit - > llcache - > store ( object - > url ,
BACKING_STORE_NONE ,
object - > source_data ,
object - > source_len ) ;
if ( ret ! = NSERROR_OK ) {
/* unable to put source data in backing store */
return ret ;
}
ret = llcache_serialise_metadata ( object , & metadata , & metadatasize ) ;
if ( ret ! = NSERROR_OK ) {
/* There has been a metadata serialisation error. Ensure the
* already written data object is invalidated .
*/
guit - > llcache - > invalidate ( object - > url ) ;
return ret ;
}
ret = guit - > llcache - > store ( object - > url ,
BACKING_STORE_META ,
metadata ,
metadatasize ) ;
2014-11-23 02:56:13 +03:00
guit - > llcache - > release ( object - > url , BACKING_STORE_META ) ;
2014-03-22 18:54:51 +04:00
if ( ret ! = NSERROR_OK ) {
/* There has been an error putting the metadata in the
* backing store . Ensure the data object is invalidated .
*/
guit - > llcache - > invalidate ( object - > url ) ;
return ret ;
}
2014-11-27 00:29:13 +03:00
nsu_getmonotonic_ms ( & endms ) ;
2014-11-26 02:11:41 +03:00
2014-03-22 18:54:51 +04:00
object - > store_state = LLCACHE_STATE_DISC ;
* written_out = object - > source_len + metadatasize ;
2014-11-27 00:29:13 +03:00
/* by ignoring the overflow this assumes the writeout took
* less than 5 weeks .
*/
* elapsed = endms - startms ;
2014-11-26 02:11:41 +03:00
2014-12-01 12:18:31 +03:00
/* ensure the writeout is reported to have taken at least the
* minimal amount of time
*/
if ( * elapsed = = 0 ) {
* elapsed = 1 ;
}
2014-03-22 18:54:51 +04:00
return NSERROR_OK ;
}
2014-12-03 23:13:10 +03:00
/**
* Check for overall write performance .
*
* If the overall write bandwidth has fallen below a useful level for
* the backing store to be effective disable it .
*
2015-04-30 16:28:18 +03:00
* It is important to ensure a useful amount of data has been written
* before calculating bandwidths otherwise tiny files taking a
* disproportionately long time to write might trigger this erroneously .
*
2014-12-03 23:13:10 +03:00
* \ param p The context pointer passed to the callback .
*/
static void llcache_persist_slowcheck ( void * p )
{
2015-04-30 16:28:18 +03:00
uint64_t total_bandwidth ; /* total bandwidth */
if ( llcache - > total_written > ( 2 * llcache - > minimum_bandwidth ) ) {
2014-12-03 23:13:10 +03:00
2015-04-30 16:28:18 +03:00
total_bandwidth = ( llcache - > total_written * 1000 ) / llcache - > total_elapsed ;
if ( total_bandwidth < llcache - > minimum_bandwidth ) {
2015-05-28 18:08:46 +03:00
LOG ( " Current bandwidth % " PRIu64 " less than minimum %zd " ,
total_bandwidth , llcache - > minimum_bandwidth ) ;
2015-04-30 16:28:18 +03:00
guit - > llcache - > finalise ( ) ;
}
2014-12-03 23:13:10 +03:00
}
}
2014-03-22 18:54:51 +04:00
/**
2014-11-23 18:43:03 +03:00
* Possibly write objects data to backing store .
*
* \ param p The context pointer passed to the callback .
2014-03-22 18:54:51 +04:00
*/
static void llcache_persist ( void * p )
{
nserror ret ;
2014-11-30 02:56:33 +03:00
struct llcache_object * * lst ; /* candidate object list */
int lst_count ; /* number of candidates in list */
int idx ; /* current candidate object index in list */
2014-11-30 03:27:05 +03:00
int next = - 1 ; /* when the next run should be scheduled for */
2014-11-30 02:56:33 +03:00
unsigned long write_limit ; /* max number of bytes to write in this run*/
size_t written ; /* all bytes written for a single object */
unsigned long elapsed ; /* how long writing an object took */
size_t total_written = 0 ; /* total bytes written in this run */
unsigned long total_elapsed = 1 ; /* total ms used to write bytes */
unsigned long total_bandwidth = 0 ; /* total bandwidth */
2014-03-22 18:54:51 +04:00
ret = build_candidate_list ( & lst , & lst_count ) ;
2014-11-26 02:11:41 +03:00
if ( ret ! = NSERROR_OK ) {
2015-05-28 19:06:18 +03:00
LLCACHE_LOG ( " Unable to construct candidate list for persisatnt writeout " ) ;
2014-11-26 02:11:41 +03:00
return ;
}
2014-03-22 18:54:51 +04:00
2014-11-28 22:17:42 +03:00
write_limit = ( llcache - > maximum_bandwidth * llcache - > time_quantum ) / 1000 ;
2014-11-26 02:11:41 +03:00
/* obtained a candidate list, make each object persistant in turn */
for ( idx = 0 ; idx < lst_count ; idx + + ) {
2014-11-30 02:56:33 +03:00
ret = write_backing_store ( lst [ idx ] , & written , & elapsed ) ;
2014-12-01 14:32:33 +03:00
if ( ret ! = NSERROR_OK ) {
continue ;
}
/* sucessfully wrote object to backing store */
total_written + = written ;
total_elapsed + = elapsed ;
total_bandwidth = ( total_written * 1000 ) / total_elapsed ;
2014-12-03 23:13:10 +03:00
2015-06-24 12:31:13 +03:00
LLCACHE_LOG ( " Wrote %zd bytes in %lums bw:%lu %s " ,
2014-12-01 14:32:33 +03:00
written , elapsed , ( written * 1000 ) / elapsed ,
2015-05-28 19:06:18 +03:00
nsurl_access ( lst [ idx ] - > url ) ) ;
2014-11-30 03:27:05 +03:00
2014-12-01 14:32:33 +03:00
/* check to for the time quantum or the size
* ( bandwidth ) for this run being exceeded .
*/
if ( total_elapsed > llcache - > time_quantum ) {
2015-05-28 18:08:46 +03:00
LOG ( " Overran timeslot " ) ;
2014-12-01 14:32:33 +03:00
/* writeout has exhausted the available time.
* Either the writeout is slow or the last
* object was very large .
*/
if ( total_bandwidth < llcache - > minimum_bandwidth ) {
2014-12-03 23:13:10 +03:00
/* Writeout was slow in this time quantum.
* Schedule a check in the future to see if
* overall performance is too slow to be useful .
*/
guit - > browser - > schedule ( llcache - > time_quantum * 100 ,
llcache_persist_slowcheck ,
NULL ) ;
2014-12-01 14:32:33 +03:00
break ;
} else {
2014-11-30 02:56:33 +03:00
if ( total_bandwidth > llcache - > maximum_bandwidth ) {
2014-12-01 14:32:33 +03:00
/* fast writeout of large file
* so calculate delay as if
* write happened only at max
* limit
2014-11-30 02:56:33 +03:00
*/
next = ( ( total_written * llcache - > time_quantum ) / write_limit ) - total_elapsed ;
} else {
2014-12-01 14:32:33 +03:00
next = llcache - > time_quantum ;
2014-11-30 02:56:33 +03:00
}
2014-11-28 22:17:42 +03:00
break ;
}
2014-12-01 14:32:33 +03:00
} else if ( total_written > write_limit ) {
/* The bandwidth limit has been reached. */
if ( total_bandwidth > llcache - > maximum_bandwidth ) {
/* fast writeout of large file so
* calculate delay as if write
* happened only at max limit
*/
next = ( ( total_written * llcache - > time_quantum ) / write_limit ) - total_elapsed ;
} else {
next = llcache - > time_quantum - total_elapsed ;
}
break ;
2014-11-26 02:11:41 +03:00
}
2014-12-01 14:32:33 +03:00
2014-03-22 18:54:51 +04:00
}
2014-11-30 03:27:05 +03:00
free ( lst ) ;
2014-12-01 14:32:33 +03:00
/* Completed list without running out of allowed bytes or time */
2014-11-30 03:27:05 +03:00
if ( idx = = lst_count ) {
2014-12-01 14:32:33 +03:00
/* only reschedule if writing is making any progress at all */
if ( total_written > 0 ) {
next = llcache - > time_quantum - total_elapsed ;
} else {
next = - 1 ;
}
2014-11-30 03:27:05 +03:00
}
2014-11-26 02:11:41 +03:00
2014-12-03 23:13:10 +03:00
llcache - > total_written + = total_written ;
llcache - > total_elapsed + = total_elapsed ;
2015-06-24 12:31:13 +03:00
LLCACHE_LOG ( " writeout size:%zd time:%lu bandwidth:%lubytes/s " ,
2015-05-28 19:06:18 +03:00
total_written , total_elapsed , total_bandwidth ) ;
2014-11-28 22:17:42 +03:00
2015-05-28 19:06:18 +03:00
LLCACHE_LOG ( " Rescheduling writeout in %dms " , next ) ;
2014-11-30 03:27:05 +03:00
guit - > browser - > schedule ( next , llcache_persist , NULL ) ;
2014-03-22 18:54:51 +04:00
}
2010-04-04 16:41:19 +04:00
/**
2012-04-28 17:24:09 +04:00
* Handler for fetch events
2010-04-04 16:41:19 +04:00
*
2014-11-23 18:43:03 +03:00
* \ param msg Fetch event
* \ param p Our private data
2010-03-28 16:56:39 +04:00
*/
2012-04-28 17:24:09 +04:00
static void llcache_fetch_callback ( const fetch_msg * msg , void * p )
2010-03-28 16:56:39 +04:00
{
2012-04-28 17:24:09 +04:00
nserror error = NSERROR_OK ;
llcache_object * object = p ;
llcache_event event ;
2010-03-28 16:56:39 +04:00
2015-05-28 19:06:18 +03:00
LLCACHE_LOG ( " Fetch event %d for %p " , msg - > type , object ) ;
2010-03-29 10:27:37 +04:00
2012-04-28 17:24:09 +04:00
switch ( msg - > type ) {
case FETCH_HEADER :
/* Received a fetch header */
object - > fetch . state = LLCACHE_FETCH_HEADERS ;
2010-09-17 02:46:40 +04:00
2014-02-21 15:31:39 +04:00
error = llcache_fetch_process_header ( object ,
msg - > data . header_or_data . buf ,
2011-11-09 01:51:42 +04:00
msg - > data . header_or_data . len ) ;
2010-09-17 02:46:40 +04:00
break ;
2010-03-28 16:56:39 +04:00
/* 3xx responses */
case FETCH_REDIRECT :
/* Request resulted in a redirect */
2010-09-17 02:04:06 +04:00
/* Release candidate, if any */
if ( object - > candidate ! = NULL ) {
object - > candidate - > candidate_count - - ;
object - > candidate = NULL ;
}
2014-02-21 15:31:39 +04:00
error = llcache_fetch_redirect ( object ,
2011-11-09 01:51:42 +04:00
msg - > data . redirect , & object ) ;
2010-03-28 16:56:39 +04:00
break ;
case FETCH_NOTMODIFIED :
/* Conditional request determined that cached object is fresh */
error = llcache_fetch_notmodified ( object , & object ) ;
break ;
/* Normal 2xx state machine */
case FETCH_DATA :
/* Received some data */
2014-02-21 15:31:39 +04:00
error = llcache_fetch_process_data ( object ,
2011-11-09 01:51:42 +04:00
msg - > data . header_or_data . buf ,
msg - > data . header_or_data . len ) ;
2010-03-28 16:56:39 +04:00
break ;
case FETCH_FINISHED :
/* Finished fetching */
2010-04-12 01:26:20 +04:00
{
uint8_t * temp ;
2010-03-28 16:56:39 +04:00
object - > fetch . state = LLCACHE_FETCH_COMPLETE ;
object - > fetch . fetch = NULL ;
2010-04-12 01:26:20 +04:00
/* Shrink source buffer to required size */
2014-02-21 15:31:39 +04:00
temp = realloc ( object - > source_data ,
2010-04-12 01:26:20 +04:00
object - > source_len ) ;
2010-04-18 01:17:08 +04:00
/* If source_len is 0, then temp may be NULL */
if ( temp ! = NULL | | object - > source_len = = 0 ) {
2010-04-12 01:26:20 +04:00
object - > source_data = temp ;
object - > source_alloc = object - > source_len ;
}
2010-03-28 16:56:39 +04:00
llcache_object_cache_update ( object ) ;
2014-03-22 18:54:51 +04:00
/* record when the fetch finished */
object - > cache . fin_time = time ( NULL ) ;
guit - > browser - > schedule ( 5000 , llcache_persist , NULL ) ;
2010-04-12 01:26:20 +04:00
}
2010-03-28 16:56:39 +04:00
break ;
/* Out-of-band information */
case FETCH_ERROR :
/* An error occurred while fetching */
2010-03-31 01:36:39 +04:00
/* The fetch has has already been cleaned up by the fetcher */
2010-04-15 00:42:03 +04:00
object - > fetch . state = LLCACHE_FETCH_COMPLETE ;
2010-03-28 16:56:39 +04:00
object - > fetch . fetch = NULL ;
2010-04-08 16:20:20 +04:00
2010-09-17 02:04:06 +04:00
/* Release candidate, if any */
if ( object - > candidate ! = NULL ) {
object - > candidate - > candidate_count - - ;
object - > candidate = NULL ;
}
2010-04-08 16:20:20 +04:00
/* Invalidate cache control data */
2010-09-16 23:47:15 +04:00
llcache_invalidate_cache_control_data ( object ) ;
2010-03-28 16:56:39 +04:00
/** \todo Consider using errorcode for something */
event . type = LLCACHE_EVENT_ERROR ;
2011-11-09 01:51:42 +04:00
event . data . msg = msg - > data . error ;
2014-02-21 15:31:39 +04:00
2010-04-14 14:56:44 +04:00
error = llcache_send_event_to_users ( object , & event ) ;
2014-02-21 15:31:39 +04:00
2010-03-28 16:56:39 +04:00
break ;
case FETCH_PROGRESS :
/* Progress update */
event . type = LLCACHE_EVENT_PROGRESS ;
2011-11-09 01:51:42 +04:00
event . data . msg = msg - > data . progress ;
2010-04-14 14:56:44 +04:00
error = llcache_send_event_to_users ( object , & event ) ;
2014-02-21 15:31:39 +04:00
2010-03-28 16:56:39 +04:00
break ;
/* Events requiring action */
case FETCH_AUTH :
/* Need Authentication */
2010-09-17 02:04:06 +04:00
/* Release candidate, if any */
if ( object - > candidate ! = NULL ) {
object - > candidate - > candidate_count - - ;
object - > candidate = NULL ;
}
2011-11-09 01:51:42 +04:00
error = llcache_fetch_auth ( object , msg - > data . auth . realm ) ;
2010-03-28 16:56:39 +04:00
break ;
case FETCH_CERT_ERR :
/* Something went wrong when validating TLS certificates */
2010-09-17 02:04:06 +04:00
/* Release candidate, if any */
if ( object - > candidate ! = NULL ) {
object - > candidate - > candidate_count - - ;
object - > candidate = NULL ;
}
2014-02-21 15:31:39 +04:00
error = llcache_fetch_cert_error ( object ,
msg - > data . cert_err . certs ,
2011-11-09 01:51:42 +04:00
msg - > data . cert_err . num_certs ) ;
2010-03-28 16:56:39 +04:00
break ;
2013-01-05 03:13:23 +04:00
case FETCH_SSL_ERR :
/* TLS connection setup failed */
/* Release candidate, if any */
if ( object - > candidate ! = NULL ) {
object - > candidate - > candidate_count - - ;
object - > candidate = NULL ;
}
error = llcache_fetch_ssl_error ( object ) ;
break ;
2010-03-28 16:56:39 +04:00
}
/* Deal with any errors reported by event handlers */
if ( error ! = NSERROR_OK ) {
2014-05-29 17:00:13 +04:00
if ( error = = NSERROR_NOMEM ) {
/* attempt to purge the cache to free some
* memory . will not help this fetch , but may
* allow the UI to report errors etc .
*/
llcache_clean ( true ) ;
}
2010-03-28 16:56:39 +04:00
if ( object - > fetch . fetch ! = NULL ) {
fetch_abort ( object - > fetch . fetch ) ;
object - > fetch . fetch = NULL ;
2010-04-15 00:42:03 +04:00
/* Invalidate cache control data */
2010-09-16 23:47:15 +04:00
llcache_invalidate_cache_control_data ( object ) ;
2010-04-15 00:42:03 +04:00
object - > fetch . state = LLCACHE_FETCH_COMPLETE ;
2010-03-28 16:56:39 +04:00
}
}
2014-06-03 20:00:23 +04:00
/* There may be users which are not caught up so schedule ourselves */
llcache_users_not_caught_up ( ) ;
2010-03-28 16:56:39 +04:00
}
/**
2012-04-28 17:24:09 +04:00
* Find a user of a low - level cache object
2010-03-28 16:56:39 +04:00
*
2012-04-28 17:24:09 +04:00
* \ param handle External cache handle to search for
* \ return Pointer to corresponding user , or NULL if not found
*/
static llcache_object_user * llcache_object_find_user ( const llcache_handle * handle )
{
llcache_object_user * user ;
assert ( handle - > object ! = NULL ) ;
for ( user = handle - > object - > users ; user ! = NULL ; user = user - > next ) {
if ( user - > handle = = handle )
break ;
}
return user ;
}
/**
* Determine if a low - level cache object resides in a given list
*
* \ param object Object to search for
* \ param list List to search in
* \ return True if object resides in list , false otherwise
*/
static bool llcache_object_in_list ( const llcache_object * object ,
const llcache_object * list )
{
while ( list ! = NULL ) {
if ( list = = object )
break ;
list = list - > next ;
}
return list ! = NULL ;
}
/**
* Notify users of an object ' s current state
*
* \ param object Object to notify users about
2010-03-28 16:56:39 +04:00
* \ return NSERROR_OK on success , appropriate error otherwise
*/
2012-04-28 17:24:09 +04:00
static nserror llcache_object_notify_users ( llcache_object * object )
2010-03-28 16:56:39 +04:00
{
nserror error ;
2012-04-28 17:24:09 +04:00
llcache_object_user * user , * next_user ;
llcache_event event ;
2010-03-28 16:56:39 +04:00
2012-04-28 17:24:09 +04:00
# ifdef LLCACHE_TRACE
bool emitted_notify = false ;
# endif
2010-09-16 23:47:15 +04:00
2012-04-28 17:24:09 +04:00
/**
2014-02-21 15:31:39 +04:00
* State transitions and event emission for users .
2012-04-28 17:24:09 +04:00
* Rows : user state . Cols : object state .
*
2014-11-09 15:50:30 +03:00
* User - Obj INIT HEADERS DATA COMPLETE
2012-04-28 17:24:09 +04:00
* INIT - T T * T *
* HEADERS - - T T *
* DATA - - M T
* COMPLETE - - - -
*
* T = > transition user to object state
* M = > no transition required , but may need to emit event
*
* The transitions marked with an asterisk can be removed by moving
* the user context into the subsequent state and then reevaluating .
*
* Events are issued as follows :
*
* HAD_HEADERS : on transition from HEADERS - > DATA state
* HAD_DATA : in DATA state , whenever there ' s new source data
* DONE : on transition from DATA - > COMPLETE state
*/
2010-04-11 17:58:12 +04:00
2012-04-28 17:24:09 +04:00
for ( user = object - > users ; user ! = NULL ; user = next_user ) {
/* Emit necessary events to bring the user up-to-date */
llcache_handle * handle = user - > handle ;
const llcache_fetch_state objstate = object - > fetch . state ;
2010-04-11 17:58:12 +04:00
2012-04-28 17:24:09 +04:00
/* Flag that this user is the current iteration target
* in case the client attempts to destroy it underneath us */
user - > iterator_target = true ;
2010-03-28 16:56:39 +04:00
2012-04-28 17:24:09 +04:00
/* A note on the computation of next_user:
2014-02-21 15:31:39 +04:00
*
* Within this loop , we may make a number of calls to
2012-04-28 17:24:09 +04:00
* client code . Our contract with clients is that they
* can do whatever they like from within their callback
* handlers . This is so that we limit the pain of
* reentrancy to this module alone .
*
* One of the things a client can do from within its
* callback handler is to remove users from this object ' s
* user list . In the common case , the user they attempt
* to remove is the current iteration target , and we
* already protect against that causing problems here .
2014-02-21 15:31:39 +04:00
* However , no such protection exists if the client
2012-04-28 17:24:09 +04:00
* attempts to remove other users from this object ' s
* user list .
*
* Therefore , we cannot compute next_user up - front
* and expect it to remain valid across calls to
* client code ( as the identity of the next user
* in the list may change underneath us ) . Instead ,
* we must compute next_user at the point where we
* are about to cause another iteration of this loop
* ( i . e . at the very end , and also at the points where
* continue is used )
*/
2010-03-28 16:56:39 +04:00
2012-04-28 17:24:09 +04:00
# ifdef LLCACHE_TRACE
if ( handle - > state ! = objstate ) {
if ( emitted_notify = = false ) {
2015-05-28 18:08:46 +03:00
LOG ( " Notifying users of %p " , object ) ;
2012-04-28 17:24:09 +04:00
emitted_notify = true ;
}
2011-02-24 01:27:55 +03:00
2015-05-28 18:08:46 +03:00
LOG ( " User %p state: %d Object state: %d " , user , handle - > state , objstate ) ;
2011-02-24 01:27:55 +03:00
}
2012-04-28 17:24:09 +04:00
# endif
2010-04-11 18:06:02 +04:00
2012-04-28 17:24:09 +04:00
/* User: INIT, Obj: HEADERS, DATA, COMPLETE => User->HEADERS */
2014-02-21 15:31:39 +04:00
if ( handle - > state = = LLCACHE_FETCH_INIT & &
2012-04-28 17:24:09 +04:00
objstate > LLCACHE_FETCH_INIT ) {
handle - > state = LLCACHE_FETCH_HEADERS ;
}
2011-09-27 15:07:32 +04:00
2012-04-28 17:24:09 +04:00
/* User: HEADERS, Obj: DATA, COMPLETE => User->DATA */
if ( handle - > state = = LLCACHE_FETCH_HEADERS & &
objstate > LLCACHE_FETCH_HEADERS ) {
handle - > state = LLCACHE_FETCH_DATA ;
2010-03-28 16:56:39 +04:00
2012-04-28 17:24:09 +04:00
/* Emit HAD_HEADERS event */
event . type = LLCACHE_EVENT_HAD_HEADERS ;
error = handle - > cb ( handle , & event , handle - > pw ) ;
if ( user - > queued_for_delete ) {
next_user = user - > next ;
llcache_object_remove_user ( object , user ) ;
llcache_object_user_destroy ( user ) ;
if ( error ! = NSERROR_OK )
return error ;
continue ;
} else if ( error = = NSERROR_NEED_DATA ) {
/* User requested replay */
handle - > state = LLCACHE_FETCH_HEADERS ;
2014-02-21 15:31:39 +04:00
/* Continue with the next user -- we'll
2012-04-28 17:24:09 +04:00
* reemit the event next time round */
user - > iterator_target = false ;
next_user = user - > next ;
2014-06-03 20:00:23 +04:00
llcache_users_not_caught_up ( ) ;
2012-04-28 17:24:09 +04:00
continue ;
} else if ( error ! = NSERROR_OK ) {
user - > iterator_target = false ;
return error ;
}
}
/* User: DATA, Obj: DATA, COMPLETE, more source available */
if ( handle - > state = = LLCACHE_FETCH_DATA & &
objstate > = LLCACHE_FETCH_DATA & &
object - > source_len > handle - > bytes ) {
size_t orig_handle_read ;
/* Construct HAD_DATA event */
event . type = LLCACHE_EVENT_HAD_DATA ;
2014-02-21 15:31:39 +04:00
event . data . data . buf =
2012-04-28 17:24:09 +04:00
object - > source_data + handle - > bytes ;
2014-02-21 15:31:39 +04:00
event . data . data . len =
2012-04-28 17:24:09 +04:00
object - > source_len - handle - > bytes ;
/* Update record of last byte emitted */
2014-02-21 15:31:39 +04:00
if ( object - > fetch . flags &
2012-04-28 17:24:09 +04:00
LLCACHE_RETRIEVE_STREAM_DATA ) {
2014-02-21 15:31:39 +04:00
/* Streaming, so reset to zero to
2012-04-28 17:24:09 +04:00
* minimise amount of cached source data .
* Additionally , we don ' t support replay
* when streaming . */
orig_handle_read = 0 ;
handle - > bytes = object - > source_len = 0 ;
} else {
orig_handle_read = handle - > bytes ;
handle - > bytes = object - > source_len ;
}
/* Emit event */
error = handle - > cb ( handle , & event , handle - > pw ) ;
if ( user - > queued_for_delete ) {
next_user = user - > next ;
llcache_object_remove_user ( object , user ) ;
llcache_object_user_destroy ( user ) ;
if ( error ! = NSERROR_OK )
return error ;
continue ;
} else if ( error = = NSERROR_NEED_DATA ) {
/* User requested replay */
handle - > bytes = orig_handle_read ;
2014-02-21 15:31:39 +04:00
/* Continue with the next user -- we'll
2012-04-28 17:24:09 +04:00
* reemit the data next time round */
user - > iterator_target = false ;
next_user = user - > next ;
2014-06-03 20:00:23 +04:00
llcache_users_not_caught_up ( ) ;
2012-04-28 17:24:09 +04:00
continue ;
} else if ( error ! = NSERROR_OK ) {
user - > iterator_target = false ;
return error ;
}
}
/* User: DATA, Obj: COMPLETE => User->COMPLETE */
if ( handle - > state = = LLCACHE_FETCH_DATA & &
objstate > LLCACHE_FETCH_DATA ) {
handle - > state = LLCACHE_FETCH_COMPLETE ;
/* Emit DONE event */
event . type = LLCACHE_EVENT_DONE ;
error = handle - > cb ( handle , & event , handle - > pw ) ;
if ( user - > queued_for_delete ) {
next_user = user - > next ;
llcache_object_remove_user ( object , user ) ;
llcache_object_user_destroy ( user ) ;
2010-03-28 16:56:39 +04:00
2012-04-28 17:24:09 +04:00
if ( error ! = NSERROR_OK )
return error ;
2010-03-28 16:56:39 +04:00
2012-04-28 17:24:09 +04:00
continue ;
} else if ( error = = NSERROR_NEED_DATA ) {
/* User requested replay */
handle - > state = LLCACHE_FETCH_DATA ;
2010-03-28 16:56:39 +04:00
2014-02-21 15:31:39 +04:00
/* Continue with the next user -- we'll
2012-04-28 17:24:09 +04:00
* reemit the event next time round */
user - > iterator_target = false ;
next_user = user - > next ;
2014-06-03 20:00:23 +04:00
llcache_users_not_caught_up ( ) ;
2012-04-28 17:24:09 +04:00
continue ;
} else if ( error ! = NSERROR_OK ) {
user - > iterator_target = false ;
return error ;
}
}
2010-03-28 16:56:39 +04:00
2012-04-28 17:24:09 +04:00
/* No longer the target of an iterator */
user - > iterator_target = false ;
2010-03-28 16:56:39 +04:00
2012-04-28 17:24:09 +04:00
next_user = user - > next ;
2010-03-28 16:56:39 +04:00
}
2012-04-28 17:24:09 +04:00
return NSERROR_OK ;
2010-03-28 16:56:39 +04:00
}
/**
2012-04-28 17:24:09 +04:00
* Make a snapshot of the current state of an llcache_object .
2010-03-28 16:56:39 +04:00
*
2012-04-28 17:24:09 +04:00
* This has the side - effect of the new object being non - cacheable ,
* also not - fetching and not a candidate for any other object .
*
* Also note that this new object has no users and at least one
* should be assigned to it before llcache_clean is entered or it
* will be immediately cleaned up .
*
* \ param object The object to take a snapshot of
* \ param snapshot Pointer to receive snapshot of \ a object
* \ return NSERROR_OK on success , appropriate error otherwise
2010-03-28 16:56:39 +04:00
*/
2014-03-22 18:54:51 +04:00
static nserror
llcache_object_snapshot ( llcache_object * object , llcache_object * * snapshot )
2010-03-28 16:56:39 +04:00
{
2012-04-28 17:24:09 +04:00
llcache_object * newobj ;
nserror error ;
2014-02-21 15:31:39 +04:00
2012-04-28 17:24:09 +04:00
error = llcache_object_new ( object - > url , & newobj ) ;
2014-02-21 15:31:39 +04:00
2012-04-28 17:24:09 +04:00
if ( error ! = NSERROR_OK )
return error ;
2014-02-21 15:31:39 +04:00
2012-04-28 17:24:09 +04:00
newobj - > source_alloc = newobj - > source_len = object - > source_len ;
2014-02-21 15:31:39 +04:00
2012-04-28 17:24:09 +04:00
if ( object - > source_len > 0 ) {
newobj - > source_data = malloc ( newobj - > source_alloc ) ;
if ( newobj - > source_data = = NULL ) {
llcache_object_destroy ( newobj ) ;
return NSERROR_NOMEM ;
2011-02-17 20:50:14 +03:00
}
2014-02-21 15:31:39 +04:00
memcpy ( newobj - > source_data , object - > source_data ,
2012-04-28 17:24:09 +04:00
newobj - > source_len ) ;
}
2014-02-21 15:31:39 +04:00
2012-04-28 17:24:09 +04:00
if ( object - > num_headers > 0 ) {
2014-02-21 15:31:39 +04:00
newobj - > headers = calloc ( sizeof ( llcache_header ) ,
2012-04-28 17:24:09 +04:00
object - > num_headers ) ;
if ( newobj - > headers = = NULL ) {
llcache_object_destroy ( newobj ) ;
return NSERROR_NOMEM ;
}
while ( newobj - > num_headers < object - > num_headers ) {
2014-02-21 15:31:39 +04:00
llcache_header * nh =
2012-04-28 17:24:09 +04:00
& ( newobj - > headers [ newobj - > num_headers ] ) ;
2014-02-21 15:31:39 +04:00
llcache_header * oh =
2012-04-28 17:24:09 +04:00
& ( object - > headers [ newobj - > num_headers ] ) ;
newobj - > num_headers + = 1 ;
nh - > name = strdup ( oh - > name ) ;
nh - > value = strdup ( oh - > value ) ;
if ( nh - > name = = NULL | | nh - > value = = NULL ) {
llcache_object_destroy ( newobj ) ;
return NSERROR_NOMEM ;
}
2011-02-25 00:22:24 +03:00
}
2011-02-17 20:50:14 +03:00
}
2014-02-21 15:31:39 +04:00
2012-04-28 17:24:09 +04:00
newobj - > fetch . state = LLCACHE_FETCH_COMPLETE ;
2014-02-21 15:31:39 +04:00
2012-04-28 17:24:09 +04:00
* snapshot = newobj ;
2014-02-21 15:31:39 +04:00
2010-03-28 16:56:39 +04:00
return NSERROR_OK ;
}
2014-03-22 18:54:51 +04:00
/**
* total ram usage of object
2014-11-23 18:43:03 +03:00
*
* \ param object The object to caclulate the total RAM usage of .
* \ return The total RAM usage in bytes .
2014-03-22 18:54:51 +04:00
*/
static inline uint32_t
total_object_size ( llcache_object * object )
{
uint32_t tot ;
size_t hdrc ;
tot = sizeof ( * object ) ;
tot + = nsurl_length ( object - > url ) ;
if ( object - > source_data ! = NULL ) {
tot + = object - > source_len ;
}
tot + = sizeof ( llcache_header ) * object - > num_headers ;
for ( hdrc = 0 ; hdrc < object - > num_headers ; hdrc + + ) {
if ( object - > headers [ hdrc ] . name ! = NULL ) {
tot + = strlen ( object - > headers [ hdrc ] . name ) ;
}
if ( object - > headers [ hdrc ] . value ! = NULL ) {
tot + = strlen ( object - > headers [ hdrc ] . value ) ;
}
}
return tot ;
}
2012-04-28 17:24:09 +04:00
/******************************************************************************
* Public API *
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2014-05-29 17:00:13 +04:00
/*
2012-04-28 17:24:09 +04:00
* Attempt to clean the cache
2014-03-22 18:54:51 +04:00
*
* The memory cache cleaning discards objects in order of increasing value .
2014-05-29 17:00:13 +04:00
*
* Exported interface documented in llcache . h
2010-03-28 16:56:39 +04:00
*/
2014-05-29 17:00:13 +04:00
void llcache_clean ( bool purge )
2010-03-28 16:56:39 +04:00
{
2012-04-28 17:24:09 +04:00
llcache_object * object , * next ;
uint32_t llcache_size = 0 ;
2013-01-07 22:45:29 +04:00
int remaining_lifetime ;
2014-05-29 17:00:13 +04:00
uint32_t limit ;
2010-03-28 16:56:39 +04:00
2015-05-28 19:06:18 +03:00
LLCACHE_LOG ( " Attempting cache clean " ) ;
2010-03-28 16:56:39 +04:00
2014-05-29 17:00:13 +04:00
/* If the cache is being purged set the size limit to zero. */
if ( purge ) {
limit = 0 ;
} else {
limit = llcache - > limit ;
}
2014-03-22 18:54:51 +04:00
/* Uncacheable objects with no users or fetches */
for ( object = llcache - > uncached_objects ;
object ! = NULL ;
object = next ) {
2012-04-28 17:24:09 +04:00
next = object - > next ;
2010-03-28 16:56:39 +04:00
2012-04-28 17:24:09 +04:00
/* The candidate count of uncacheable objects is always 0 */
2014-02-21 15:31:39 +04:00
if ( ( object - > users = = NULL ) & &
2012-04-28 17:24:09 +04:00
( object - > candidate_count = = 0 ) & &
( object - > fetch . fetch = = NULL ) & &
( object - > fetch . outstanding_query = = false ) ) {
2015-05-28 19:06:18 +03:00
LLCACHE_LOG ( " Discarding uncachable object with no users (%p) %s " ,
object , nsurl_access ( object - > url ) ) ;
2014-02-21 15:43:48 +04:00
2014-02-21 15:31:39 +04:00
llcache_object_remove_from_list ( object ,
2012-04-28 17:24:09 +04:00
& llcache - > uncached_objects ) ;
llcache_object_destroy ( object ) ;
} else {
2014-03-22 18:54:51 +04:00
llcache_size + = total_object_size ( object ) ;
2012-04-28 17:24:09 +04:00
}
}
2010-03-28 16:56:39 +04:00
2014-03-22 18:54:51 +04:00
/* Stale cacheable objects with no users or pending fetches */
for ( object = llcache - > cached_objects ;
object ! = NULL ;
object = next ) {
2012-04-28 17:24:09 +04:00
next = object - > next ;
2010-03-28 16:56:39 +04:00
2013-01-07 22:45:29 +04:00
remaining_lifetime = llcache_object_rfc2616_remaining_lifetime ( & object - > cache ) ;
if ( ( object - > users = = NULL ) & &
2012-04-28 17:24:09 +04:00
( object - > candidate_count = = 0 ) & &
( object - > fetch . fetch = = NULL ) & &
2014-03-22 18:54:51 +04:00
( object - > fetch . outstanding_query = = false ) & &
( remaining_lifetime < = 0 ) ) {
2014-11-23 02:56:13 +03:00
/* object is stale */
2015-05-28 19:06:18 +03:00
LLCACHE_LOG ( " discarding stale cacheable object with no users or pending fetches (%p) %s " , object , nsurl_access ( object - > url ) ) ;
2014-02-21 15:43:48 +04:00
2013-01-07 22:45:29 +04:00
llcache_object_remove_from_list ( object ,
& llcache - > cached_objects ) ;
2014-03-22 18:54:51 +04:00
if ( object - > store_state = = LLCACHE_STATE_DISC ) {
guit - > llcache - > invalidate ( object - > url ) ;
}
2013-01-07 22:45:29 +04:00
llcache_object_destroy ( object ) ;
2014-03-22 18:54:51 +04:00
2012-04-28 17:24:09 +04:00
} else {
2014-03-22 18:54:51 +04:00
/* object has users so account for the storage */
llcache_size + = total_object_size ( object ) ;
2010-03-28 16:56:39 +04:00
}
2012-04-28 17:24:09 +04:00
}
2010-03-28 16:56:39 +04:00
2014-03-22 18:54:51 +04:00
/* if the cache limit is exceeded try to make some objects
* persistant so their RAM can be reclaimed in the next
* step
2012-04-28 17:24:09 +04:00
*/
2014-05-29 17:00:13 +04:00
if ( limit < llcache_size ) {
2014-03-22 18:54:51 +04:00
llcache_persist ( NULL ) ;
}
/* Source data of fresh cacheable objects with no users, no
* pending fetches and pushed to persistant store while the
* cache exceeds the configured size .
*/
for ( object = llcache - > cached_objects ;
2014-05-29 17:00:13 +04:00
( ( limit < llcache_size ) & & ( object ! = NULL ) ) ;
2014-03-22 18:54:51 +04:00
object = next ) {
next = object - > next ;
if ( ( object - > users = = NULL ) & &
( object - > candidate_count = = 0 ) & &
( object - > fetch . fetch = = NULL ) & &
( object - > fetch . outstanding_query = = false ) & &
( object - > store_state = = LLCACHE_STATE_DISC ) ) {
2014-11-23 02:56:13 +03:00
guit - > llcache - > release ( object - > url , BACKING_STORE_NONE ) ;
2014-03-22 18:54:51 +04:00
object - > source_data = NULL ;
2010-03-28 16:56:39 +04:00
2014-03-22 18:54:51 +04:00
llcache_size - = object - > source_len ;
2014-02-21 15:43:48 +04:00
2015-06-24 12:31:13 +03:00
LLCACHE_LOG ( " Freeing source data for %p len:%zd " ,
2014-03-22 18:54:51 +04:00
object ,
2015-05-28 19:06:18 +03:00
object - > source_len ) ;
2014-03-22 18:54:51 +04:00
}
}
2010-03-28 16:56:39 +04:00
2014-03-22 18:54:51 +04:00
/* Fresh cacheable objects with no users, no pending fetches
* and pushed to persistant store while the cache exceeds
2014-11-23 02:56:13 +03:00
* the configured size . Efectively just the llcache object metadata .
2014-03-22 18:54:51 +04:00
*/
for ( object = llcache - > cached_objects ;
2014-05-29 17:00:13 +04:00
( ( limit < llcache_size ) & & ( object ! = NULL ) ) ;
2014-03-22 18:54:51 +04:00
object = next ) {
next = object - > next ;
if ( ( object - > users = = NULL ) & &
( object - > candidate_count = = 0 ) & &
( object - > fetch . fetch = = NULL ) & &
( object - > fetch . outstanding_query = = false ) & &
( object - > store_state = = LLCACHE_STATE_DISC ) & &
( object - > source_data = = NULL ) ) {
2015-06-24 12:31:13 +03:00
LLCACHE_LOG ( " discarding backed object len:%zd age:%d (%p) %s " ,
2014-03-22 18:54:51 +04:00
object - > source_len ,
time ( NULL ) - object - > last_used ,
object ,
2015-05-28 19:06:18 +03:00
nsurl_access ( object - > url ) ) ;
2014-03-22 18:54:51 +04:00
llcache_size - = total_object_size ( object ) ;
llcache_object_remove_from_list ( object ,
2012-04-28 17:24:09 +04:00
& llcache - > cached_objects ) ;
2014-03-22 18:54:51 +04:00
llcache_object_destroy ( object ) ;
}
}
/* Fresh cacheable objects with no users or pending fetches
2014-11-23 02:56:13 +03:00
* while the cache exceeds the configured size . These are the
2014-03-22 18:54:51 +04:00
* most valuble objects as replacing them is a full network
* fetch
*/
for ( object = llcache - > cached_objects ;
2014-05-29 17:00:13 +04:00
( ( limit < llcache_size ) & & ( object ! = NULL ) ) ;
2014-03-22 18:54:51 +04:00
object = next ) {
next = object - > next ;
if ( ( object - > users = = NULL ) & &
( object - > candidate_count = = 0 ) & &
( object - > fetch . fetch = = NULL ) & &
( object - > fetch . outstanding_query = = false ) & &
( object - > store_state = = LLCACHE_STATE_RAM ) ) {
2015-06-24 12:31:13 +03:00
LLCACHE_LOG ( " discarding fresh object len:%zd age:%d (%p) %s " ,
2014-03-22 18:54:51 +04:00
object - > source_len ,
time ( NULL ) - object - > last_used ,
object ,
2015-05-28 19:06:18 +03:00
nsurl_access ( object - > url ) ) ;
2014-03-22 18:54:51 +04:00
llcache_size - = object - > source_len + sizeof ( * object ) ;
llcache_object_remove_from_list ( object ,
& llcache - > cached_objects ) ;
llcache_object_destroy ( object ) ;
2010-03-28 16:56:39 +04:00
}
}
2015-05-28 19:06:18 +03:00
LLCACHE_LOG ( " Size: %u " , llcache_size ) ;
2010-03-28 16:56:39 +04:00
}
2014-11-23 18:43:03 +03:00
/* Exported interface documented in content/llcache.h */
2014-02-21 15:31:39 +04:00
nserror
2014-03-22 18:54:51 +04:00
llcache_initialise ( const struct llcache_parameters * prm )
2010-03-28 16:56:39 +04:00
{
2012-04-28 17:24:09 +04:00
llcache = calloc ( 1 , sizeof ( struct llcache_s ) ) ;
if ( llcache = = NULL ) {
return NSERROR_NOMEM ;
}
2010-03-28 16:56:39 +04:00
2014-03-22 18:54:51 +04:00
llcache - > query_cb = prm - > cb ;
llcache - > query_cb_pw = prm - > cb_ctx ;
llcache - > limit = prm - > limit ;
llcache - > minimum_lifetime = prm - > minimum_lifetime ;
2014-11-28 22:17:42 +03:00
llcache - > minimum_bandwidth = prm - > minimum_bandwidth ;
llcache - > maximum_bandwidth = prm - > maximum_bandwidth ;
llcache - > time_quantum = prm - > time_quantum ;
2014-06-03 20:00:23 +04:00
llcache - > all_caught_up = true ;
2010-03-28 16:56:39 +04:00
2015-05-28 18:08:46 +03:00
LOG ( " llcache initialising with a limit of %d bytes " , llcache - > limit ) ;
2010-03-28 16:56:39 +04:00
2014-03-22 18:54:51 +04:00
/* backing store initialisation */
return guit - > llcache - > initialise ( & prm - > store ) ;
2012-04-28 17:24:09 +04:00
}
2010-03-28 16:56:39 +04:00
2014-11-23 18:43:03 +03:00
/* Exported interface documented in content/llcache.h */
2012-04-28 17:24:09 +04:00
void llcache_finalise ( void )
{
llcache_object * object , * next ;
2015-05-12 12:19:38 +03:00
uint64_t total_bandwidth = 0 ; /* total bandwidth */
2015-01-03 18:12:53 +03:00
2012-04-28 17:24:09 +04:00
/* Clean uncached objects */
for ( object = llcache - > uncached_objects ; object ! = NULL ; object = next ) {
llcache_object_user * user , * next_user ;
2010-03-28 16:56:39 +04:00
2012-04-28 17:24:09 +04:00
next = object - > next ;
2010-03-28 16:56:39 +04:00
2012-04-28 17:24:09 +04:00
for ( user = object - > users ; user ! = NULL ; user = next_user ) {
next_user = user - > next ;
2010-03-28 16:56:39 +04:00
2012-04-28 17:24:09 +04:00
if ( user - > handle ! = NULL )
free ( user - > handle ) ;
free ( user ) ;
2010-03-28 16:56:39 +04:00
}
2012-04-28 17:24:09 +04:00
/* Fetch system has already been destroyed */
object - > fetch . fetch = NULL ;
2010-03-28 16:56:39 +04:00
2012-04-28 17:24:09 +04:00
llcache_object_destroy ( object ) ;
}
2010-03-28 16:56:39 +04:00
2012-04-28 17:24:09 +04:00
/* Clean cached objects */
for ( object = llcache - > cached_objects ; object ! = NULL ; object = next ) {
llcache_object_user * user , * next_user ;
2010-03-28 16:56:39 +04:00
2012-04-28 17:24:09 +04:00
next = object - > next ;
2010-10-28 02:12:00 +04:00
2012-04-28 17:24:09 +04:00
for ( user = object - > users ; user ! = NULL ; user = next_user ) {
next_user = user - > next ;
2010-10-28 02:12:00 +04:00
2012-04-28 17:24:09 +04:00
if ( user - > handle ! = NULL )
free ( user - > handle ) ;
2010-10-28 02:12:00 +04:00
2012-04-28 17:24:09 +04:00
free ( user ) ;
}
2010-10-28 02:12:00 +04:00
2012-04-28 17:24:09 +04:00
/* Fetch system has already been destroyed */
2014-02-21 15:31:39 +04:00
object - > fetch . fetch = NULL ;
2011-02-24 13:14:50 +03:00
2012-04-28 17:24:09 +04:00
llcache_object_destroy ( object ) ;
2010-03-28 16:56:39 +04:00
}
2014-03-22 18:54:51 +04:00
/* backing store finalisation */
guit - > llcache - > finalise ( ) ;
2015-05-08 17:42:49 +03:00
if ( llcache - > total_elapsed > 0 ) {
total_bandwidth = ( llcache - > total_written * 1000 ) /
llcache - > total_elapsed ;
}
2015-05-28 18:08:46 +03:00
LOG ( " Backing store wrote % " PRIu64 " bytes in % " PRIu64 " ms " " (average % " PRIu64 " bytes/second) " , llcache - > total_written , llcache - > total_elapsed , total_bandwidth ) ;
2014-12-03 23:13:10 +03:00
2012-04-28 17:24:09 +04:00
free ( llcache ) ;
llcache = NULL ;
}
2010-03-28 16:56:39 +04:00
2014-06-03 20:00:23 +04:00
/**
* Catch up the cache users with state changes from fetchers .
*
* \ param ignored We ignore this because all our state comes from llcache .
*/
static void llcache_catch_up_all_users ( void * ignored )
2012-04-28 17:24:09 +04:00
{
llcache_object * object ;
2014-02-21 15:31:39 +04:00
2014-06-03 20:00:23 +04:00
/* Assume after this we'll be all caught up. If any user of a handle
* defers then we ' ll end up set not caught up and we ' ll
* reschedule at that point via llcache_users_not_caught_up ( )
*/
llcache - > all_caught_up = true ;
2014-02-21 15:31:39 +04:00
2012-04-28 17:24:09 +04:00
/* Catch new users up with state of objects */
2014-02-21 15:31:39 +04:00
for ( object = llcache - > cached_objects ; object ! = NULL ;
2012-04-28 17:24:09 +04:00
object = object - > next ) {
llcache_object_notify_users ( object ) ;
}
for ( object = llcache - > uncached_objects ; object ! = NULL ;
object = object - > next ) {
llcache_object_notify_users ( object ) ;
}
2014-06-03 20:00:23 +04:00
}
2010-03-28 16:56:39 +04:00
2014-06-03 20:00:23 +04:00
/**
* Ask for : : llcache_catch_up_all_users to be scheduled ASAP to pump the
* user state machines .
*/
2014-11-23 18:43:03 +03:00
static void llcache_users_not_caught_up ( void )
2014-06-03 20:00:23 +04:00
{
if ( llcache - > all_caught_up ) {
llcache - > all_caught_up = false ;
guit - > browser - > schedule ( 0 , llcache_catch_up_all_users , NULL ) ;
}
2010-03-28 16:56:39 +04:00
}
2014-06-03 20:00:23 +04:00
2014-11-23 18:43:03 +03:00
/* Exported interface documented in content/llcache.h */
2012-04-28 17:24:09 +04:00
nserror llcache_handle_retrieve ( nsurl * url , uint32_t flags ,
nsurl * referer , const llcache_post_data * post ,
llcache_handle_callback cb , void * pw ,
llcache_handle * * result )
2010-03-28 16:56:39 +04:00
{
2012-04-28 17:24:09 +04:00
nserror error ;
llcache_object_user * user ;
llcache_object * object ;
/* Can we fetch this URL at all? */
2015-06-24 12:31:13 +03:00
if ( fetch_can_fetch ( url ) = = false ) {
2012-04-28 17:24:09 +04:00
return NSERROR_NO_FETCH_HANDLER ;
2015-06-24 12:31:13 +03:00
}
2012-04-28 17:24:09 +04:00
/* Create a new object user */
error = llcache_object_user_new ( cb , pw , & user ) ;
2015-06-24 12:31:13 +03:00
if ( error ! = NSERROR_OK ) {
2012-04-28 17:24:09 +04:00
return error ;
2015-06-24 12:31:13 +03:00
}
2010-03-28 16:56:39 +04:00
2012-04-28 17:24:09 +04:00
/* Retrieve a suitable object from the cache,
* creating a new one if needed . */
error = llcache_object_retrieve ( url , flags , referer , post , 0 , & object ) ;
if ( error ! = NSERROR_OK ) {
llcache_object_user_destroy ( user ) ;
return error ;
2010-03-28 16:56:39 +04:00
}
2012-04-28 17:24:09 +04:00
/* Add user to object */
llcache_object_add_user ( object , user ) ;
* result = user - > handle ;
2010-03-28 16:56:39 +04:00
2014-06-03 20:00:23 +04:00
/* Users exist which are now not caught up! */
llcache_users_not_caught_up ( ) ;
2010-03-28 16:56:39 +04:00
return NSERROR_OK ;
}
2014-11-23 18:43:03 +03:00
/* Exported interface documented in content/llcache.h */
2012-04-28 17:24:09 +04:00
nserror llcache_handle_change_callback ( llcache_handle * handle ,
llcache_handle_callback cb , void * pw )
2010-03-28 16:56:39 +04:00
{
2012-04-28 17:24:09 +04:00
handle - > cb = cb ;
handle - > pw = pw ;
2010-03-28 16:56:39 +04:00
2012-04-28 17:24:09 +04:00
return NSERROR_OK ;
}
2010-03-28 16:56:39 +04:00
2014-11-23 18:43:03 +03:00
/* Exported interface documented in content/llcache.h */
2012-04-28 17:24:09 +04:00
nserror llcache_handle_release ( llcache_handle * handle )
{
nserror error = NSERROR_OK ;
llcache_object * object = handle - > object ;
llcache_object_user * user = llcache_object_find_user ( handle ) ;
2010-04-17 00:08:27 +04:00
2012-04-28 17:24:09 +04:00
assert ( user ! = NULL ) ;
2010-03-28 16:56:39 +04:00
2012-04-28 17:24:09 +04:00
if ( user - > iterator_target ) {
2014-02-21 15:31:39 +04:00
/* Can't remove / delete user object if it's
2012-04-28 17:24:09 +04:00
* the target of an iterator */
user - > queued_for_delete = true ;
} else {
/* Remove the user from the object and destroy it */
error = llcache_object_remove_user ( object , user ) ;
if ( error = = NSERROR_OK ) {
error = llcache_object_user_destroy ( user ) ;
}
}
2014-02-21 15:31:39 +04:00
return error ;
2012-04-28 17:24:09 +04:00
}
2010-03-28 16:56:39 +04:00
2014-11-23 18:43:03 +03:00
/* Exported interface documented in content/llcache.h */
2012-04-28 17:24:09 +04:00
nserror llcache_handle_clone ( llcache_handle * handle , llcache_handle * * result )
{
nserror error ;
llcache_object_user * newuser ;
2014-02-21 15:31:39 +04:00
2012-04-28 17:24:09 +04:00
error = llcache_object_user_new ( handle - > cb , handle - > pw , & newuser ) ;
if ( error = = NSERROR_OK ) {
llcache_object_add_user ( handle - > object , newuser ) ;
newuser - > handle - > state = handle - > state ;
* result = newuser - > handle ;
}
2014-02-21 15:31:39 +04:00
2012-04-28 17:24:09 +04:00
return error ;
}
2010-03-28 16:56:39 +04:00
2012-04-28 17:24:09 +04:00
/* See llcache.h for documentation */
nserror llcache_handle_abort ( llcache_handle * handle )
{
llcache_object_user * user = llcache_object_find_user ( handle ) ;
llcache_object * object = handle - > object , * newobject ;
nserror error = NSERROR_OK ;
bool all_alone = true ;
2014-02-21 15:31:39 +04:00
2012-04-28 17:24:09 +04:00
/* Determine if we are the only user */
if ( user - > prev ! = NULL )
all_alone = false ;
if ( user - > next ! = NULL )
all_alone = false ;
2014-02-21 15:31:39 +04:00
2012-04-28 17:24:09 +04:00
if ( all_alone = = false ) {
/* We must snapshot this object */
error = llcache_object_snapshot ( object , & newobject ) ;
if ( error ! = NSERROR_OK )
return error ;
2010-03-28 16:56:39 +04:00
2012-04-28 17:24:09 +04:00
/* Move across to the new object */
if ( user - > iterator_target ) {
/* User is current iterator target, clone it */
2014-02-21 15:31:39 +04:00
llcache_object_user * newuser =
2012-04-28 17:24:09 +04:00
calloc ( 1 , sizeof ( llcache_object_user ) ) ;
if ( newuser = = NULL ) {
llcache_object_destroy ( newobject ) ;
return NSERROR_NOMEM ;
}
2010-04-15 00:42:03 +04:00
2012-04-28 17:24:09 +04:00
/* Move handle across to clone */
newuser - > handle = user - > handle ;
user - > handle = NULL ;
2010-04-17 00:00:10 +04:00
2012-04-28 17:24:09 +04:00
/* Mark user as needing deletion */
user - > queued_for_delete = true ;
2011-03-03 03:40:50 +03:00
2012-04-28 17:24:09 +04:00
llcache_object_add_user ( newobject , newuser ) ;
2010-04-17 00:00:10 +04:00
} else {
2012-04-28 17:24:09 +04:00
llcache_object_remove_user ( object , user ) ;
llcache_object_add_user ( newobject , user ) ;
2010-04-17 00:00:10 +04:00
}
2014-02-21 15:31:39 +04:00
2012-04-28 17:24:09 +04:00
/* Add new object to uncached list */
2014-02-21 15:31:39 +04:00
llcache_object_add_to_list ( newobject ,
2012-04-28 17:24:09 +04:00
& llcache - > uncached_objects ) ;
2010-04-17 00:00:10 +04:00
} else {
2012-04-28 17:24:09 +04:00
/* We're the only user, so abort any fetch in progress */
if ( object - > fetch . fetch ! = NULL ) {
fetch_abort ( object - > fetch . fetch ) ;
object - > fetch . fetch = NULL ;
}
2014-02-21 15:31:39 +04:00
2012-04-28 17:24:09 +04:00
object - > fetch . state = LLCACHE_FETCH_COMPLETE ;
2014-02-21 15:31:39 +04:00
2012-04-28 17:24:09 +04:00
/* Invalidate cache control data */
llcache_invalidate_cache_control_data ( object ) ;
2010-03-28 16:56:39 +04:00
}
2014-02-21 15:31:39 +04:00
2010-03-28 16:56:39 +04:00
return error ;
}
2012-04-28 17:24:09 +04:00
/* See llcache.h for documentation */
nserror llcache_handle_force_stream ( llcache_handle * handle )
2010-03-28 16:56:39 +04:00
{
2012-04-28 17:24:09 +04:00
llcache_object_user * user = llcache_object_find_user ( handle ) ;
llcache_object * object = handle - > object ;
2010-03-28 16:56:39 +04:00
2012-04-28 17:24:09 +04:00
/* Cannot stream if there are multiple users */
if ( user - > prev ! = NULL | | user - > next ! = NULL )
return NSERROR_OK ;
2010-03-28 16:56:39 +04:00
2012-04-28 17:24:09 +04:00
/* Forcibly uncache this object */
if ( llcache_object_in_list ( object , llcache - > cached_objects ) ) {
2014-02-21 15:31:39 +04:00
llcache_object_remove_from_list ( object ,
2012-04-28 17:24:09 +04:00
& llcache - > cached_objects ) ;
llcache_object_add_to_list ( object , & llcache - > uncached_objects ) ;
}
2010-04-17 00:08:27 +04:00
2012-04-28 17:24:09 +04:00
object - > fetch . flags | = LLCACHE_RETRIEVE_STREAM_DATA ;
2010-03-28 16:56:39 +04:00
2012-04-28 17:24:09 +04:00
return NSERROR_OK ;
}
2010-03-28 16:56:39 +04:00
2012-04-28 17:24:09 +04:00
/* See llcache.h for documentation */
nserror llcache_handle_invalidate_cache_data ( llcache_handle * handle )
{
2014-02-21 15:31:39 +04:00
if ( handle - > object ! = NULL & & handle - > object - > fetch . fetch = = NULL & &
handle - > object - > cache . no_cache = =
2012-04-28 17:24:09 +04:00
LLCACHE_VALIDATE_FRESH ) {
handle - > object - > cache . no_cache = LLCACHE_VALIDATE_ONCE ;
}
2011-03-03 03:40:50 +03:00
2012-04-28 17:24:09 +04:00
return NSERROR_OK ;
}
2010-03-28 16:56:39 +04:00
2012-04-28 17:24:09 +04:00
/* See llcache.h for documentation */
nsurl * llcache_handle_get_url ( const llcache_handle * handle )
{
return handle - > object ! = NULL ? handle - > object - > url : NULL ;
}
2010-04-15 00:42:03 +04:00
2012-04-28 17:24:09 +04:00
/* See llcache.h for documentation */
const uint8_t * llcache_handle_get_source_data ( const llcache_handle * handle ,
size_t * size )
{
* size = handle - > object ! = NULL ? handle - > object - > source_len : 0 ;
return handle - > object ! = NULL ? handle - > object - > source_data : NULL ;
}
/* See llcache.h for documentation */
2014-02-21 15:31:39 +04:00
const char * llcache_handle_get_header ( const llcache_handle * handle ,
2012-04-28 17:24:09 +04:00
const char * key )
{
const llcache_object * object = handle - > object ;
size_t i ;
if ( object = = NULL )
return NULL ;
/* About as trivial as possible */
for ( i = 0 ; i < object - > num_headers ; i + + ) {
if ( strcasecmp ( key , object - > headers [ i ] . name ) = = 0 )
return object - > headers [ i ] . value ;
2010-03-28 16:56:39 +04:00
}
2012-04-28 17:24:09 +04:00
return NULL ;
}
/* See llcache.h for documentation */
2014-02-21 15:31:39 +04:00
bool llcache_handle_references_same_object ( const llcache_handle * a ,
2012-04-28 17:24:09 +04:00
const llcache_handle * b )
{
return a - > object = = b - > object ;
2010-03-28 16:56:39 +04:00
}