summaryrefslogtreecommitdiff
path: root/content
diff options
context:
space:
mode:
authorJohn Mark Bell <jmb@netsurf-browser.org>2006-02-06 00:10:09 +0000
committerJohn Mark Bell <jmb@netsurf-browser.org>2006-02-06 00:10:09 +0000
commit0f228ada91a9460d1042b1a854fb1a0a32ed3f10 (patch)
treeb8279b6c948d3c5d5b3cb470434b2dc6c85fbca4 /content
parentc176e276e2de15a306872e3d4fa74193178eaadf (diff)
downloadnetsurf-0f228ada91a9460d1042b1a854fb1a0a32ed3f10.tar.gz
netsurf-0f228ada91a9460d1042b1a854fb1a0a32ed3f10.tar.bz2
[project @ 2006-02-06 00:10:09 by jmb]
Implement HTTP caching algorithm; this should avoid stale cache entries being used. svn path=/import/netsurf/; revision=2059
Diffstat (limited to 'content')
-rw-r--r--content/content.c49
-rw-r--r--content/content.h3
-rw-r--r--content/fetch.c172
-rw-r--r--content/fetch.h27
-rw-r--r--content/fetchcache.c272
5 files changed, 468 insertions, 55 deletions
diff --git a/content/content.c b/content/content.c
index 11effac45..9b5d3c8f2 100644
--- a/content/content.c
+++ b/content/content.c
@@ -288,6 +288,11 @@ struct content * content_create(const char *url)
talloc_free(c);
return 0;
}
+ c->cache_data = talloc(c, struct cache_data);
+ if (!c->cache_data) {
+ talloc_free(c);
+ return 0;
+ }
talloc_set_name_const(c, c->url);
c->type = CONTENT_UNKNOWN;
c->mime_type = 0;
@@ -315,6 +320,14 @@ struct content * content_create(const char *url)
c->no_error_pages = false;
c->download = false;
c->error_count = 0;
+ c->cache_data->req_time = 0;
+ c->cache_data->res_time = 0;
+ c->cache_data->date = 0;
+ c->cache_data->expires = 0;
+ c->cache_data->age = INVALID_AGE;
+ c->cache_data->max_age = INVALID_AGE;
+ c->cache_data->no_cache = false;
+ c->cache_data->etag = 0;
c->prev = 0;
c->next = content_list;
@@ -362,6 +375,42 @@ struct content * content_get(const char *url)
/**
+ * Get a READY or DONE content from the memory cache.
+ *
+ * \param url URL of content
+ * \return content if found, or 0
+ *
+ * Searches the list of contents for one corresponding to the given url, and
+ * which is fresh, shareable and either READY or DONE.
+ */
+
+struct content * content_get_ready(const char *url)
+{
+ struct content *c;
+
+ for (c = content_list; c; c = c->next) {
+ if (!c->fresh)
+ /* not fresh */
+ continue;
+ if (c->status != CONTENT_STATUS_READY &&
+ c->status != CONTENT_STATUS_DONE)
+ /* not ready or done */
+ continue;
+ if (c->type != CONTENT_UNKNOWN &&
+ handler_map[c->type].no_share &&
+ c->user_list->next)
+ /* not shareable, and has a user already */
+ continue;
+ if (strcmp(c->url, url))
+ continue;
+ return c;
+ }
+
+ return 0;
+}
+
+
+/**
* Initialise the content for the specified type.
*
* \param c content structure
diff --git a/content/content.h b/content/content.h
index 3e24ccbc0..7dbcdcb08 100644
--- a/content/content.h
+++ b/content/content.h
@@ -134,6 +134,7 @@
struct bitmap;
struct box;
struct browser_window;
+struct cache_data;
struct content;
struct fetch;
struct object_params;
@@ -245,6 +246,7 @@ struct content {
* was fetched using a simple GET, has not expired, and may be
* shared between users. */
bool fresh;
+ struct cache_data *cache_data; /**< Cache control data */
unsigned int size; /**< Estimated size of all data
associated with this content. */
@@ -285,6 +287,7 @@ extern const char *content_status_name[];
content_type content_lookup(const char *mime_type);
struct content * content_create(const char *url);
struct content * content_get(const char *url);
+struct content * content_get_ready(const char *url);
bool content_set_type(struct content *c, content_type type,
const char *mime_type, const char *params[]);
void content_set_status(struct content *c, const char *status_message, ...);
diff --git a/content/fetch.c b/content/fetch.c
index 9fa792f2c..11f23feee 100644
--- a/content/fetch.c
+++ b/content/fetch.c
@@ -25,6 +25,7 @@
#include <string.h>
#include <strings.h>
#include <time.h>
+#include <sys/stat.h>
#ifdef riscos
#include <unixlib/local.h>
#endif
@@ -35,9 +36,7 @@
#ifdef WITH_AUTH
#include "netsurf/desktop/401login.h"
#endif
-#ifdef WITH_POST
#include "netsurf/render/form.h"
-#endif
#define NDEBUG
#include "netsurf/utils/log.h"
#include "netsurf/utils/messages.h"
@@ -68,6 +67,8 @@ struct fetch {
char *realm; /**< HTTP Auth Realm */
char *post_urlenc; /**< Url encoded POST string, or 0. */
struct curl_httppost *post_multipart; /**< Multipart post data, or 0. */
+ struct cache_data cachedata; /**< Cache control data */
+ time_t last_modified; /**< If-Modified-Since time */
struct fetch *queue_prev; /**< Previous fetch for this host. */
struct fetch *queue_next; /**< Next fetch for this host. */
struct fetch *prev; /**< Previous active fetch in ::fetch_list. */
@@ -94,9 +95,8 @@ static size_t fetch_curl_data(void *data, size_t size, size_t nmemb,
static size_t fetch_curl_header(char *data, size_t size, size_t nmemb,
struct fetch *f);
static bool fetch_process_headers(struct fetch *f);
-#ifdef WITH_POST
-static struct curl_httppost *fetch_post_convert(struct form_successful_control *control);
-#endif
+static struct curl_httppost *fetch_post_convert(
+ struct form_successful_control *control);
/**
@@ -148,10 +148,10 @@ void fetch_init(void)
SETOPT(CURLOPT_CAINFO, option_ca_bundle);
if (!option_ssl_verify_certificates) {
- /* disable verification of SSL certificates.
- * security? we've heard of it...
- */
- SETOPT(CURLOPT_SSL_VERIFYPEER, 0L);
+ /* disable verification of SSL certificates.
+ * security? we've heard of it...
+ */
+ SETOPT(CURLOPT_SSL_VERIFYPEER, 0L);
SETOPT(CURLOPT_SSL_VERIFYHOST, 0L);
}
@@ -209,7 +209,8 @@ struct fetch * fetch_start(char *url, char *referer,
void (*callback)(fetch_msg msg, void *p, const char *data,
unsigned long size),
void *p, bool only_2xx, char *post_urlenc,
- struct form_successful_control *post_multipart, bool cookies)
+ struct form_successful_control *post_multipart, bool cookies,
+ char *headers[])
{
char *host;
struct fetch *fetch;
@@ -219,6 +220,7 @@ struct fetch * fetch_start(char *url, char *referer,
struct curl_slist *slist;
url_func_result res;
char *ref1 = 0, *ref2 = 0;
+ int i;
fetch = malloc(sizeof (*fetch));
if (!fetch)
@@ -274,6 +276,15 @@ struct fetch * fetch_start(char *url, char *referer,
fetch->post_urlenc = strdup(post_urlenc);
else if (post_multipart)
fetch->post_multipart = fetch_post_convert(post_multipart);
+ fetch->cachedata.req_time = time(0);
+ fetch->cachedata.res_time = 0;
+ fetch->cachedata.date = 0;
+ fetch->cachedata.expires = 0;
+ fetch->cachedata.age = INVALID_AGE;
+ fetch->cachedata.max_age = INVALID_AGE;
+ fetch->cachedata.no_cache = false;
+ fetch->cachedata.etag = 0;
+ fetch->last_modified = 0;
fetch->queue_prev = 0;
fetch->queue_next = 0;
fetch->prev = 0;
@@ -319,6 +330,16 @@ struct fetch * fetch_start(char *url, char *referer,
s[sizeof s - 1] = 0;
APPEND(fetch->headers, s);
}
+ /* And add any headers specified by the caller */
+ for (i = 0; headers[i]; i++) {
+ if (strncasecmp(headers[i], "If-Modified-Since:", 18) == 0) {
+ char *d = headers[i] + 18;
+ for (; *d && (*d == ' ' || *d == '\t'); d++)
+ /* do nothing */;
+ fetch->last_modified = curl_getdate(d, NULL);
+ }
+ APPEND(fetch->headers, headers[i]);
+ }
/* look for a fetch from the same host */
for (host_fetch = fetch_list;
@@ -499,6 +520,7 @@ void fetch_stop(struct fetch *f)
fetch->curl_handle = f->curl_handle;
f->curl_handle = 0;
+ fetch->cachedata.req_time = time(0);
code = fetch_set_options(fetch);
if (code == CURLE_OK)
/* add to the global curl multi handle */
@@ -554,6 +576,7 @@ void fetch_free(struct fetch *f)
free(f->post_urlenc);
if (f->post_multipart)
curl_formfree(f->post_multipart);
+ free(f->cachedata.etag);
free(f);
}
@@ -611,6 +634,7 @@ void fetch_done(CURL *curl_handle, CURLcode result)
void (*callback)(fetch_msg msg, void *p, const char *data,
unsigned long size);
CURLcode code;
+ struct cache_data cachedata;
/* find the structure associated with this fetch */
code = curl_easy_getinfo(curl_handle, CURLINFO_PRIVATE, &f);
@@ -635,14 +659,22 @@ void fetch_done(CURL *curl_handle, CURLcode result)
else
error = true;
+ /* If finished, acquire cache info to pass to callback */
+ if (finished) {
+ memcpy(&cachedata, &f->cachedata, sizeof(struct cache_data));
+ f->cachedata.etag = 0;
+ }
+
/* clean up fetch and start any queued fetch for this host */
fetch_stop(f);
/* postponed until after stop so that queue fetches are started */
if (abort)
; /* fetch was aborted: no callback */
- else if (finished)
- callback(FETCH_FINISHED, p, 0, 0);
+ else if (finished) {
+ callback(FETCH_FINISHED, p, (const char *)&cachedata, 0);
+ free(cachedata.etag);
+ }
else if (error)
callback(FETCH_ERROR, p, fetch_error_buffer, 0);
}
@@ -716,6 +748,13 @@ size_t fetch_curl_header(char *data, size_t size, size_t nmemb,
{
int i;
size *= nmemb;
+
+#define SKIP_ST(o) for (i = (o); i < (int) size && (data[i] == ' ' || data[i] == '\t'); i++)
+
+ /* Set fetch response time if not already set */
+ if (f->cachedata.res_time == 0)
+ f->cachedata.res_time = time(0);
+
if (12 < size && strncasecmp(data, "Location:", 9) == 0) {
/* extract Location header */
free(f->location);
@@ -724,8 +763,7 @@ size_t fetch_curl_header(char *data, size_t size, size_t nmemb,
LOG(("malloc failed"));
return size;
}
- for (i = 9; i < (int)size && (data[i] == ' ' || data[i] == '\t'); i++)
- /* */;
+ SKIP_ST(9);
strncpy(f->location, data + i, size - i);
f->location[size - i] = '\0';
for (i = size - i - 1; i >= 0 &&
@@ -736,12 +774,11 @@ size_t fetch_curl_header(char *data, size_t size, size_t nmemb,
f->location[i] = '\0';
} else if (15 < size && strncasecmp(data, "Content-Length:", 15) == 0) {
/* extract Content-Length header */
- for (i = 15; i < (int)size && (data[i] == ' ' || data[i] == '\t'); i++)
- /* */;
+ SKIP_ST(15);
if (i < (int)size && '0' <= data[i] && data[i] <= '9')
f->content_length = atol(data + i);
#ifdef WITH_AUTH
- } else if (16 < size && strncasecmp(data, "WWW-Authenticate", 16) == 0) {
+ } else if (17 < size && strncasecmp(data, "WWW-Authenticate:", 17) == 0) {
/* extract the first Realm from WWW-Authenticate header */
free(f->realm);
f->realm = malloc(size);
@@ -749,8 +786,7 @@ size_t fetch_curl_header(char *data, size_t size, size_t nmemb,
LOG(("malloc failed"));
return size;
}
- for (i = 16; i < (int)size && data[i] != '='; i++)
- /* */;
+ SKIP_ST(17);
while (i < (int)size && data[++i] == '"')
/* */;
strncpy(f->realm, data + i, size - i);
@@ -763,8 +799,70 @@ size_t fetch_curl_header(char *data, size_t size, size_t nmemb,
f->realm[i] == '\n'); --i)
f->realm[i] = '\0';
#endif
+ } else if (5 < size && strncasecmp(data, "Date:", 5) == 0) {
+ /* extract Date header */
+ SKIP_ST(5);
+ if (i < (int) size)
+ f->cachedata.date = curl_getdate(&data[i], NULL);
+ } else if (4 < size && strncasecmp(data, "Age:", 4) == 0) {
+ /* extract Age header */
+ SKIP_ST(4);
+ if (i < (int) size && '0' <= data[i] && data[i] <= '9')
+ f->cachedata.age = atoi(data + i);
+ } else if (8 < size && strncasecmp(data, "Expires:", 8) == 0) {
+ /* extract Expires header */
+ SKIP_ST(8);
+ if (i < (int) size)
+ f->cachedata.expires = curl_getdate(&data[i], NULL);
+ } else if (14 < size && strncasecmp(data, "Cache-Control:", 14) == 0) {
+ /* extract and parse Cache-Control header */
+ int comma;
+ SKIP_ST(14);
+
+ while (i < (int) size) {
+ for (comma = i; comma < (int) size; comma++)
+ if (data[comma] == ',')
+ break;
+
+ SKIP_ST(i);
+
+ if (8 < comma - i && (strncasecmp(data + i, "no-cache", 8) == 0 || strncasecmp(data + i, "no-store", 8) == 0))
+ /* When we get a disk cache we should
+ * distinguish between these two */
+ f->cachedata.no_cache = true;
+ else if (7 < comma - i && strncasecmp(data + i, "max-age", 7) == 0) {
+ for (; i < comma; i++)
+ if (data[i] == '=')
+ break;
+ SKIP_ST(i+1);
+ if (i < comma)
+ f->cachedata.max_age =
+ atoi(data + i);
+ }
+
+ i = comma + 1;
+ }
+ } else if (5 < size && strncasecmp(data, "ETag:", 5) == 0) {
+ /* extract ETag header */
+ free(f->cachedata.etag);
+ f->cachedata.etag = malloc(size);
+ if (!f->cachedata.etag) {
+ LOG(("malloc failed"));
+ return size;
+ }
+ SKIP_ST(5);
+ strncpy(f->cachedata.etag, data + i, size - i);
+ f->cachedata.etag[size - i] = '\0';
+ for (i = size - i - 1; i >= 0 &&
+ (f->cachedata.etag[i] == ' ' ||
+ f->cachedata.etag[i] == '\t' ||
+ f->cachedata.etag[i] == '\r' ||
+ f->cachedata.etag[i] == '\n'); --i)
+ f->cachedata.etag[i] = '\0';
}
+
return size;
+#undef SKIP_ST
}
@@ -779,13 +877,47 @@ bool fetch_process_headers(struct fetch *f)
long http_code;
const char *type;
CURLcode code;
+ struct stat s;
f->had_headers = true;
+ /* Set fetch response time if not already set */
+ if (f->cachedata.res_time == 0)
+ f->cachedata.res_time = time(0);
+
code = curl_easy_getinfo(f->curl_handle, CURLINFO_HTTP_CODE, &http_code);
assert(code == CURLE_OK);
LOG(("HTTP status code %li", http_code));
+ if (f->last_modified) {
+ /* Fake up HTTP cache control for local files */
+ char *url_path = 0;
+
+ if (strncmp(f->url, "file:///", 8) == 0) {
+ url_path = curl_unescape(f->url + 7,
+ (int) strlen(f->url) - 7);
+ }
+ else if (strncmp(f->url, "file:/", 6) == 0) {
+ url_path = curl_unescape(f->url + 5,
+ (int) strlen(f->url) - 5);
+ }
+
+ if (url_path && stat(url_path, &s) == 0) {
+ if (f->last_modified > s.st_mtime) {
+ f->callback(FETCH_NOTMODIFIED, f->p,
+ (const char *)&f->cachedata, 0);
+ return true;
+ }
+ }
+ }
+
+ if (http_code == 304 && !f->post_urlenc && !f->post_multipart) {
+ /* Not Modified && GET request */
+ f->callback(FETCH_NOTMODIFIED, f->p,
+ (const char *)&f->cachedata, 0);
+ return true;
+ }
+
/* handle HTTP redirects (3xx response codes) */
if (300 <= http_code && http_code < 400 && f->location != 0) {
LOG(("FETCH_REDIRECT, '%s'", f->location));
@@ -840,7 +972,6 @@ bool fetch_process_headers(struct fetch *f)
* Convert a list of struct ::form_successful_control to a list of
* struct curl_httppost for libcurl.
*/
-#ifdef WITH_POST
struct curl_httppost *fetch_post_convert(struct form_successful_control *control)
{
struct curl_httppost *post = 0, *last = 0;
@@ -901,7 +1032,6 @@ struct curl_httppost *fetch_post_convert(struct form_successful_control *control
return post;
}
-#endif
/**
diff --git a/content/fetch.h b/content/fetch.h
index 38de5d090..b65e150ff 100644
--- a/content/fetch.h
+++ b/content/fetch.h
@@ -23,6 +23,7 @@ typedef enum {
FETCH_FINISHED,
FETCH_ERROR,
FETCH_REDIRECT,
+ FETCH_NOTMODIFIED,
#ifdef WITH_AUTH
FETCH_AUTH
#endif
@@ -30,9 +31,19 @@ typedef enum {
struct content;
struct fetch;
-#ifdef WITH_POST
struct form_successful_control;
-#endif
+
+struct cache_data {
+ time_t req_time; /**< Time of request */
+ time_t res_time; /**< Time of response */
+ time_t date; /**< Date: response header */
+ time_t expires; /**< Expires: response header */
+#define INVALID_AGE -1
+ int age; /**< Age: response header */
+ int max_age; /**< Max-age Cache-control parameter */
+ bool no_cache; /**< no-cache Cache-control parameter */
+ char *etag; /**< Etag: response header */
+};
extern bool fetch_active;
extern CURLM *fetch_curl_multi;
@@ -41,15 +52,9 @@ void fetch_init(void);
struct fetch * fetch_start(char *url, char *referer,
void (*callback)(fetch_msg msg, void *p, const char *data,
unsigned long size),
- void *p, bool only_2xx
-#ifdef WITH_POST
- , char *post_urlenc,
- struct form_successful_control *post_multipart
-#endif
-#ifdef WITH_COOKIES
- ,bool cookies
-#endif
- );
+ void *p, bool only_2xx, char *post_urlenc,
+ struct form_successful_control *post_multipart,
+ bool cookies, char *headers[]);
void fetch_abort(struct fetch *f);
void fetch_poll(void);
void fetch_quit(void);
diff --git a/content/fetchcache.c b/content/fetchcache.c
index a7e68ee97..6e3f359f0 100644
--- a/content/fetchcache.c
+++ b/content/fetchcache.c
@@ -18,6 +18,7 @@
#include <string.h>
#include <sys/types.h>
#include <regex.h>
+#include <time.h>
#include "netsurf/utils/config.h"
#include "netsurf/content/content.h"
#include "netsurf/content/fetchcache.h"
@@ -25,6 +26,7 @@
#include "netsurf/content/url_store.h"
#include "netsurf/utils/log.h"
#include "netsurf/utils/messages.h"
+#include "netsurf/utils/talloc.h"
#include "netsurf/utils/url.h"
#include "netsurf/utils/utils.h"
@@ -35,6 +37,9 @@ static void fetchcache_callback(fetch_msg msg, void *p, const char *data,
unsigned long size);
static char *fetchcache_parse_type(const char *s, char **params[]);
static void fetchcache_error_page(struct content *c, const char *error);
+static void fetchcache_cache_update(struct content *c,
+ const struct cache_data *data);
+static void fetchcache_notmodified(struct content *c, const char *data);
/**
@@ -74,7 +79,9 @@ struct content * fetchcache(const char *url,
{
struct content *c;
char *url1;
- char *hash;
+ char *hash, *query;
+ char *etag = 0;
+ time_t date = 0;
if ((url1 = strdup(url)) == NULL)
return NULL;
@@ -83,15 +90,48 @@ struct content * fetchcache(const char *url,
if ((hash = strchr(url1, '#')) != NULL)
*hash = 0;
+ /* look for query; we don't cache URLs with a query segment */
+ query = strchr(url1, '?');
+
LOG(("url %s", url1));
- if (!post_urlenc && !post_multipart && !download) {
+ if (!post_urlenc && !post_multipart && !download && !query) {
if ((c = content_get(url1)) != NULL) {
- free(url1);
- if (!content_add_user(c, callback, p1, p2))
- return NULL;
- else
- return c;
+ struct cache_data *cd = c->cache_data;
+ int current_age, freshness_lifetime;
+
+ /* Calculate staleness of cached content 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 +
+ time(0) - cd->res_time;
+ freshness_lifetime =
+ (cd->max_age != INVALID_AGE) ? cd->max_age :
+ (cd->expires != 0) ? cd->expires - cd->date :
+ 0;
+
+ if (freshness_lifetime > current_age ||
+ cd->date == 0) {
+ /* Ok, either a fresh content or we're
+ * currently fetching the selected content
+ * (therefore it must be fresh) */
+ free(url1);
+ if (!content_add_user(c, callback, p1, p2))
+ return NULL;
+ else
+ return c;
+ }
+
+ /* Ok. We have a cache entry, but it appears stale.
+ * Therefore, validate it. */
+ /** \todo perhaps it'd be better to use the
+ * contents of the Last-Modified header here
+ * instead */
+ date = c->cache_data->date;
+ etag = c->cache_data->etag;
}
}
@@ -99,11 +139,21 @@ struct content * fetchcache(const char *url,
free(url1);
if (!c)
return NULL;
+
+ /* Fill in cache validation fields (if present) */
+ if (date)
+ c->cache_data->date = date;
+ if (etag) {
+ c->cache_data->etag = talloc_strdup(c, etag);
+ if (!c->cache_data->etag)
+ return NULL;
+ }
+
if (!content_add_user(c, callback, p1, p2)) {
return NULL;
}
- if (!post_urlenc && !post_multipart && !download)
+ if (!post_urlenc && !post_multipart && !download && !query)
c->fresh = true;
c->width = width;
@@ -152,12 +202,59 @@ void fetchcache_go(struct content *content, char *referer,
/* fetching, but not yet received any response:
* no action required */
- } else if (content->status == CONTENT_STATUS_TYPE_UNKNOWN) {
- /* brand new content: start fetch */
+ } else if (content->status == CONTENT_STATUS_TYPE_UNKNOWN) {
+ /* brand new content: start fetch */
+ char **headers;
+ int i = 0;
+ char *etag = content->cache_data->etag;
+ time_t date = content->cache_data->date;
+ content->cache_data->etag = 0;
+ content->cache_data->date = 0;
+ headers = malloc(3 * sizeof(char *));
+ if (!headers) {
+ talloc_free(etag);
+ msg_data.error = messages_get("NoMemory");
+ callback(CONTENT_MSG_ERROR, content, p1, p2,
+ msg_data);
+ return;
+ }
+ if (etag) {
+ headers[i] = malloc(15 + strlen(etag) + 1);
+ if (!headers[i]) {
+ free(headers);
+ talloc_free(etag);
+ msg_data.error = messages_get("NoMemory");
+ callback(CONTENT_MSG_ERROR, content, p1, p2,
+ msg_data);
+ return;
+ }
+ sprintf(headers[i++], "If-None-Match: %s", etag);
+ talloc_free(etag);
+ }
+ if (date) {
+ headers[i] = malloc(19 + 29 + 1);
+ if (!headers[i]) {
+ while (--i >= 0) {
+ free(headers[i]);
+ }
+ free(headers);
+ msg_data.error = messages_get("NoMemory");
+ callback(CONTENT_MSG_ERROR, content, p1, p2,
+ msg_data);
+ return;
+ }
+ sprintf(headers[i++], "If-Modified-Since: %s",
+ rfc1123_date(date));
+ }
+ headers[i] = 0;
content->fetch = fetch_start(content->url, referer,
fetchcache_callback, content,
content->no_error_pages,
- post_urlenc, post_multipart, cookies);
+ post_urlenc, post_multipart, cookies,
+ headers);
+ for (i = 0; headers[i]; i++)
+ free(headers[i]);
+ free(headers);
if (!content->fetch) {
LOG(("warning: fetch_start failed"));
snprintf(error_message, sizeof error_message,
@@ -259,24 +356,15 @@ void fetchcache_callback(fetch_msg msg, void *p, const char *data,
break;
case FETCH_DATA:
-/* if (c->total_size)
- content_set_status(c,
- messages_get("RecPercent"),
- human_friendly_bytesize(c->source_size + size),
- human_friendly_bytesize(c->total_size),
- (unsigned int) ((c->source_size + size) * 100 / c->total_size));
- else
- content_set_status(c,
- messages_get("Received"),
- human_friendly_bytesize(c->source_size + size));
- content_broadcast(c, CONTENT_MSG_STATUS, msg_data);
-*/ if (!content_process_data(c, data, size)) {
+ if (!content_process_data(c, data, size)) {
fetch_abort(c->fetch);
c->fetch = 0;
}
break;
case FETCH_FINISHED:
+ fetchcache_cache_update(c,
+ (const struct cache_data *)data);
c->fetch = 0;
content_set_status(c, messages_get("Converting"),
c->source_size);
@@ -327,6 +415,11 @@ void fetchcache_callback(fetch_msg msg, void *p, const char *data,
content_broadcast(c, CONTENT_MSG_ERROR, msg_data);
}
break;
+
+ case FETCH_NOTMODIFIED:
+ fetchcache_notmodified(c, data);
+ break;
+
#ifdef WITH_AUTH
case FETCH_AUTH:
/* data -> string containing the Realm */
@@ -456,6 +549,139 @@ void fetchcache_error_page(struct content *c, const char *error)
}
+/**
+ * Update a content's cache info
+ *
+ * \param The content
+ * \param Cache data
+ */
+
+void fetchcache_cache_update(struct content *c,
+ const struct cache_data *data)
+{
+ assert(c && data);
+
+ c->cache_data->req_time = data->req_time;
+ c->cache_data->res_time = data->res_time;
+
+ if (data->date != 0)
+ c->cache_data->date = data->date;
+ else
+ c->cache_data->date = time(0);
+
+ if (data->expires != 0)
+ c->cache_data->expires = data->expires;
+
+ if (data->age != INVALID_AGE)
+ c->cache_data->age = data->age;
+
+ if (data->max_age != INVALID_AGE)
+ c->cache_data->max_age = data->max_age;
+
+ if (data->no_cache)
+ c->fresh = false;
+
+ if (data->etag) {
+ talloc_free(c->cache_data->etag);
+ c->cache_data->etag = talloc_strdup(c, data->etag);
+ }
+}
+
+
+/**
+ * Not modified callback handler
+ */
+
+void fetchcache_notmodified(struct content *c, const char *data)
+{
+ struct content *fb;
+ union content_msg_data msg_data;
+
+ assert(c && data);
+ assert(c->status == CONTENT_STATUS_TYPE_UNKNOWN);
+
+ /* Look for cached content */
+ fb = content_get_ready(c->url);
+
+ if (fb) {
+ /* Found it */
+ intptr_t p1, p2;
+ void (*callback)(content_msg msg,
+ struct content *c, intptr_t p1,
+ intptr_t p2,
+ union content_msg_data data);
+
+ /* Now notify all users that we're changing content */
+ while (c->user_list->next) {
+ p1 = c->user_list->next->p1;
+ p2 = c->user_list->next->p2;
+ callback = c->user_list->next->callback;
+
+ if (!content_add_user(fb, callback, p1, p2)) {
+ c->type = CONTENT_UNKNOWN;
+ c->status = CONTENT_STATUS_ERROR;
+ msg_data.error = messages_get("NoMemory");
+ content_broadcast(c, CONTENT_MSG_ERROR,
+ msg_data);
+ return;
+ }
+
+ content_remove_user(c, callback, p1, p2);
+ callback(CONTENT_MSG_NEWPTR, fb, p1, p2, msg_data);
+
+ /* and catch user up with fallback's state */
+ if (fb->status == CONTENT_STATUS_LOADING) {
+ callback(CONTENT_MSG_LOADING,
+ fb, p1, p2, msg_data);
+ } else if (fb->status == CONTENT_STATUS_READY) {
+ callback(CONTENT_MSG_LOADING,
+ fb, p1, p2, msg_data);
+ if (content_find_user(fb, callback, p1, p2))
+ callback(CONTENT_MSG_READY,
+ fb, p1, p2, msg_data);
+ } else if (fb->status == CONTENT_STATUS_DONE) {
+ callback(CONTENT_MSG_LOADING,
+ fb, p1, p2, msg_data);
+ if (content_find_user(fb, callback, p1, p2))
+ callback(CONTENT_MSG_READY,
+ fb, p1, p2, msg_data);
+ if (content_find_user(fb, callback, p1, p2))
+ callback(CONTENT_MSG_DONE,
+ fb, p1, p2, msg_data);
+ } else if (fb->status == CONTENT_STATUS_ERROR) {
+ /* shouldn't usually occur */
+ msg_data.error = messages_get("MiscError");
+ callback(CONTENT_MSG_ERROR, fb, p1, p2,
+ msg_data);
+ }
+ }
+
+ /* mark content invalid */
+ c->fetch = 0;
+ c->status = CONTENT_STATUS_ERROR;
+
+ /* and update fallback's cache control data */
+ fetchcache_cache_update(fb,
+ (const struct cache_data *)data);
+ }
+ else {
+ /* No cached content, so unconditionally refetch */
+ struct content_user *u;
+
+ fetch_abort(c->fetch);
+ c->fetch = 0;
+
+ c->cache_data->date = 0;
+ talloc_free(c->cache_data->etag);
+ c->cache_data->etag = 0;
+
+ for (u = c->user_list->next; u; u = u->next) {
+ fetchcache_go(c, 0, u->callback, u->p1, u->p2,
+ c->width, c->height, 0, 0, false);
+ }
+ }
+}
+
#ifdef TEST
#include <unistd.h>