Project

General

Profile

lighttpd-1.4.x-svn-mod_mem_cache.patch

Updated Patchfile (works with 1.4.x from SVN rev. 2639 - lunetics, 2009-10-19 22:23

View differences:

./src/http-header-glue.c 2008-03-14 09:50:18.000000000 +0800
229 229
}
230 230

  
231 231

  
232
int http_response_handle_cachable(server *srv, connection *con, buffer *mtime) {
232
int http_response_handle_cachable(server *srv, connection *con, buffer *mtime, buffer *etag) {
233 233
	/*
234 234
	 * 14.26 If-None-Match
235 235
	 *    [...]
......
242 242

  
243 243
	/* last-modified handling */
244 244
	if (con->request.http_if_none_match) {
245
		if (etag_is_equal(con->physical.etag, con->request.http_if_none_match)) {
245
		if (etag_is_equal(etag, con->request.http_if_none_match)) {
246 246
			if (con->request.http_method == HTTP_METHOD_GET ||
247 247
			    con->request.http_method == HTTP_METHOD_HEAD) {
248 248

  
./src/Makefile.am 2008-03-14 09:35:42.000000000 +0800
126 126
mod_scgi_la_LDFLAGS = -module -export-dynamic -avoid-version -no-undefined
127 127
mod_scgi_la_LIBADD = $(common_libadd)
128 128

  
129
lib_LTLIBRARIES += mod_mem_cache.la
130
mod_mem_cache_la_SOURCES = mod_mem_cache.c
131
mod_mem_cache_la_LDFLAGS = -module -export-dynamic -avoid-version -no-undefined
132
mod_mem_cache_la_LIBADD = $(common_libadd)
133

  
129 134
lib_LTLIBRARIES += mod_staticfile.la
130 135
mod_staticfile_la_SOURCES = mod_staticfile.c
131 136
mod_staticfile_la_LDFLAGS = -module -export-dynamic -avoid-version -no-undefined
./src/mod_mem_cache.c 2008-03-14 09:52:09.000000000 +0800
1
/* 
2
Copyright (c) 2006, 2008 QUE Hongyu
3

  
4
Redistribution and use in source and binary forms, with or without
5
modification, are permitted provided that the following conditions
6
are met:
7
1. Redistributions of source code must retain the above copyright
8
   notice, this list of conditions and the following disclaimer.
9
2. Redistributions in binary form must reproduce the above copyright
10
   notice, this list of conditions and the following disclaimer in the
11
   documentation and/or other materials provided with the distribution.
12

  
13
THIS SOFTWARE IS PROVIDED BY AUTHOR AND CONTRIBUTORS ``AS IS'' AND
14
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
15
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
16
ARE DISCLAIMED.  IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
17
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
18
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
19
OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
20
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
21
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
22
OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
23
SUCH DAMAGE.
24

  
25
*/
26

  
27
#include <ctype.h>
28
#include <stdlib.h>
29
#include <stdio.h>
30
#include <string.h>
31
#include <fcntl.h>
32
#include <errno.h>
33
#include <assert.h>
34
#include <unistd.h>
35

  
36
#include "base.h"
37
#include "log.h"
38
#include "buffer.h"
39

  
40
#include "plugin.h"
41

  
42
#include "stat_cache.h"
43
#include "etag.h"
44
#include "response.h"
45
#include "status_counter.h"
46

  
47

  
48
#define LIGHTTPD_V14 1
49

  
50
#ifdef LIGHTTPD_V14
51
#include "splaytree.h"
52
#endif
53

  
54
#define CONFIG_MEM_CACHE_ENABLE "mem-cache.enable"
55
#define CONFIG_MEM_CACHE_MAX_MEMORY "mem-cache.max-memory"
56
#define CONFIG_MEM_CACHE_MAX_FILE_SIZE "mem-cache.max-file-size"
57
#define CONFIG_MEM_CACHE_LRU_REMOVE_COUNT "mem-cache.lru-remove-count"
58
#define CONFIG_MEM_CACHE_EXPIRE_TIME "mem-cache.expire-time"
59
#define CONFIG_MEM_CACHE_FILE_TYPES "mem-cache.filetypes"
60
#define CONFIG_MEM_CACHE_SLRU_THRESOLD "mem-cache.slru-thresold"
61

  
62
typedef struct {
63
	/* number of cache items removed by lru when memory is full */
64
	unsigned short lru_remove_count;
65
	unsigned short enable;
66
	short thresold;
67
	off_t maxmemory; /* maxium total used memory in MB */
68
	off_t maxfilesize; /* maxium file size will put into memory */
69
	unsigned int expires;
70
	array  *filetypes;
71
} plugin_config;
72

  
73
#define MEM_CACHE_NUM 524288 /* 2^19 */
74
#define LRUDEBUG 0
75

  
76
static int lruheader, lruend;
77
static unsigned long reqcount, reqhit, cachenumber;
78

  
79
#ifdef LIGHTTPD_V15
80
typedef struct tree_node {
81
    struct tree_node * left, * right;
82
    int key;
83
    int size;   /* maintained to be the number of nodes rooted here */
84

  
85
    void *data;
86
} splay_tree;
87

  
88
#define splaytree_size(x) (((x)==NULL) ? 0 : ((x)->size))
89

  
90
#endif
91
/* This macro returns the size of a node.  Unlike "x->size",     */
92
/* it works even if x=NULL.  The test could be avoided by using  */
93
/* a special version of NULL which was a real node with size 0.  */
94

  
95
/* use hash idea as danga's memcached */
96
struct cache_entry{
97
	short inuse;
98
	/* cache data */
99
	buffer *content;
100

  
101
	/* pointer for next when hash collided */
102
	struct cache_entry *scnext;
103

  
104
	/* lru info */
105
	unsigned int prev;
106
	unsigned int next;
107

  
108
	/* cache store time */
109
	time_t ct;
110
	/* file name */
111
	buffer *path;
112
	/* buffer to print at Last-Modified: header */
113
	buffer *mtime;
114
	/* content-type */
115
	buffer *content_type;
116
	/* etag */
117
	buffer *etag;
118
}; 
119

  
120
static struct cache_entry *memcache;
121

  
122
static float usedmemory = 0;
123

  
124
/* probation lru splaytree */
125
splay_tree *plru;
126
/* structure to store probation lru info */
127
struct probation {
128
	time_t startts;
129
	int count;
130
};
131

  
132
typedef struct {
133
	PLUGIN_DATA;
134
	
135
	plugin_config **config_storage;
136
	
137
	plugin_config conf; 
138
} plugin_data;
139

  
140
#ifdef LIGHTTPD_V15
141

  
142
#define compare(i,j) ((i)-(j))
143
/* This is the comparison.*/
144
/* Returns <0 if i<j, =0 if i=j, and >0 if i>j */
145

  
146
#define node_size splaytree_size
147

  
148
/* Splay using the key i (which may or may not be in the tree.)
149
 * The starting root is t, and the tree used is defined by rat
150
 * size fields are maintained */
151
static splay_tree * splaytree_splay (splay_tree *t, int i) {
152
	splay_tree N, *l, *r, *y;
153
	int comp, root_size, l_size, r_size;
154

  
155
	if (t == NULL) return t;
156
	N.left = N.right = NULL;
157
	l = r = &N;
158
	root_size = node_size(t);
159
	l_size = r_size = 0;
160

  
161
	for (;;) {
162
		comp = compare(i, t->key);
163
		if (comp < 0) {
164
			if (t->left == NULL) break;
165
			if (compare(i, t->left->key) < 0) {
166
				y = t->left;	/* rotate right */
167
				t->left = y->right;
168
				y->right = t;
169
				t->size = node_size(t->left) + node_size(t->right) + 1;
170
				t = y;
171
				if (t->left == NULL) break;
172
			}
173
			r->left = t;	/* link right */
174
			r = t;
175
			t = t->left;
176
			r_size += 1+node_size(r->right);
177
		} else if (comp > 0) {
178
			if (t->right == NULL) break;
179
			if (compare(i, t->right->key) > 0) {
180
				y = t->right;	/* rotate left */
181
				t->right = y->left;
182
				y->left = t;
183
				t->size = node_size(t->left) + node_size(t->right) + 1;
184
				t = y;
185
				if (t->right == NULL) break;
186
			}
187
			l->right = t;	/* link left */
188
			l = t;
189
			t = t->right;
190
			l_size += 1+node_size(l->left);
191
		} else {
192
			break;
193
		}
194
	}
195
	l_size += node_size(t->left);  /* Now l_size and r_size are the sizes of */
196
	r_size += node_size(t->right); /* the left and right trees we just built.*/
197
	t->size = l_size + r_size + 1;
198

  
199
	l->right = r->left = NULL;
200

  
201
	/* The following two loops correct the size fields of the right path
202
	 * from the left child of the root and the right path from the left
203
	 * child of the root.
204
	 */
205
	for (y = N.right; y != NULL; y = y->right) {
206
		y->size = l_size;
207
		l_size -= 1+node_size(y->left);
208
	}
209
	for (y = N.left; y != NULL; y = y->left) {
210
		y->size = r_size;
211
		r_size -= 1+node_size(y->right);
212
	}
213

  
214
	l->right = t->left;	/* assemble */
215
	r->left = t->right;
216
	t->left = N.right;
217
	t->right = N.left;
218

  
219
	return t;
220
}
221

  
222
static splay_tree * splaytree_insert(splay_tree * t, int i, void *data) {
223
	/* Insert key i into the tree t, if it is not already there.
224
	 * Return a pointer to the resulting tree.
225
	 */
226
	splay_tree * new;
227

  
228
	if (t != NULL) {
229
		t = splaytree_splay(t, i);
230
		if (compare(i, t->key)==0) {
231
			return t;  /* it's already there */
232
		}
233
	}
234
	new = (splay_tree *) malloc (sizeof (splay_tree));
235
	assert(new);
236
	if (t == NULL) {
237
		new->left = new->right = NULL;
238
	} else if (compare(i, t->key) < 0) {
239
		new->left = t->left;
240
		new->right = t;
241
		t->left = NULL;
242
		t->size = 1+node_size(t->right);
243
	} else {
244
		new->right = t->right;
245
		new->left = t;
246
		t->right = NULL;
247
		t->size = 1+node_size(t->left);
248
	}
249
	new->key = i;
250
	new->data = data;
251
	new->size = 1 + node_size(new->left) + node_size(new->right);
252
	return new;
253
}
254

  
255
static splay_tree * splaytree_delete(splay_tree *t, int i) {
256
	/* Deletes i from the tree if it's there.
257
	 * Return a pointer to the resulting tree.
258
	 */
259
	splay_tree * x;
260
	int tsize;
261

  
262
	if (t==NULL) return NULL;
263
	tsize = t->size;
264
	t = splaytree_splay(t, i);
265
	if (compare(i, t->key) == 0) {/* found it */
266
		if (t->left == NULL) {
267
			x = t->right;
268
		} else {
269
			x = splaytree_splay(t->left, i);
270
			x->right = t->right;
271
		}
272
		free(t);
273
		if (x != NULL) {
274
			x->size = tsize-1;
275
		}
276
		return x;
277
	} else {
278
		return t;						 /* It wasn't there */
279
	}
280
}
281

  
282

  
283
#endif
284

  
285
/* init cache_entry table */
286
static struct cache_entry *cache_entry_init(void) {
287
	struct cache_entry *c;
288
	c = (struct cache_entry *) malloc(sizeof(struct cache_entry)*(MEM_CACHE_NUM+1));
289
	assert(c);
290
	memset(c, 0, sizeof(struct cache_entry)*(MEM_CACHE_NUM+1));
291
	return c;
292
}
293

  
294
/* free cache_entry */
295
static void cache_entry_free(struct cache_entry *cache) {
296
	if (cache == NULL) return;
297
	cachenumber --;
298
	if (cache->content) usedmemory -= cache->content->size;
299
	buffer_free(cache->content);
300
	buffer_free(cache->content_type);
301
	buffer_free(cache->etag);
302
	buffer_free(cache->path);
303
	buffer_free(cache->mtime);
304
	memset(cache, 0, sizeof(struct cache_entry));
305
}
306

  
307
/* reset cache_entry to initial state */
308
static void cache_entry_reset(struct cache_entry *cache) {
309
	if (cache == NULL) return;
310
	if (cache->content == NULL) cache->content = buffer_init();
311
	if (cache->content_type == NULL) cache->content_type = buffer_init();
312
	if (cache->etag == NULL) cache->etag = buffer_init();
313
	if (cache->path == NULL) cache->path = buffer_init();
314
	if (cache->mtime == NULL) cache->mtime = buffer_init();
315
}
316

  
317
/* init the plugin data */
318
INIT_FUNC(mod_mem_cache_init) {
319
	plugin_data *p;
320
	
321
#ifdef LIGHTTPD_V15
322
	UNUSED(srv);
323
#endif
324
	p = calloc(1, sizeof(*p));
325
	memcache = cache_entry_init();
326
	lruheader = lruend = cachenumber = 0;
327
	reqcount = reqhit = 1;
328
	usedmemory = 0;
329
	plru = NULL;
330
	
331
	return p;
332
}
333

  
334
void free_cache_entry_chain(struct cache_entry *p) {
335
	struct cache_entry *c1, *c2;
336

  
337
	c1 = p;
338
	while(c1) {
339
		c2 = c1->scnext;
340
		cache_entry_free(c1);
341
		if (c1 != p) free(c1);
342
		c1 = c2;
343
	}
344

  
345
}
346

  
347
/* detroy the plugin data */
348
FREE_FUNC(mod_mem_cache_free) {
349
	plugin_data *p = p_d;
350
	size_t i;
351
	
352
	UNUSED(srv);
353

  
354
	if (!p) return HANDLER_GO_ON;
355
	
356
	if (p->config_storage) {
357
		for (i = 0; i < srv->config_context->used; i++) {
358
			plugin_config *s = p->config_storage[i];
359
			
360
			if (!s) continue;
361
			array_free(s->filetypes);
362
			free(s);
363
		}
364
		free(p->config_storage);
365
	}
366
	
367
	free(p);
368
	for (i = 0; i<= MEM_CACHE_NUM; i++) {
369
		free_cache_entry_chain(memcache+i);
370
	}
371
	free(memcache);
372

  
373
	return HANDLER_GO_ON;
374
}
375

  
376
/* handle plugin config and check values */
377

  
378
SETDEFAULTS_FUNC(mod_mem_cache_set_defaults) {
379
	plugin_data *p = p_d;
380
	size_t i = 0;
381
	
382
	config_values_t cv[] = { 
383
		{ CONFIG_MEM_CACHE_MAX_MEMORY, NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION },       /* 0 */
384
		{ CONFIG_MEM_CACHE_MAX_FILE_SIZE, NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION },       /* 1 */
385
		{ CONFIG_MEM_CACHE_LRU_REMOVE_COUNT, NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION },       /* 2 */
386
		{ CONFIG_MEM_CACHE_ENABLE, NULL, T_CONFIG_BOOLEAN, T_CONFIG_SCOPE_CONNECTION },       /* 3 */
387
		{ CONFIG_MEM_CACHE_EXPIRE_TIME, NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION },       /* 4 */
388
		{ CONFIG_MEM_CACHE_FILE_TYPES, NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION },       /* 5 */
389
		{ CONFIG_MEM_CACHE_SLRU_THRESOLD, NULL, T_CONFIG_SHORT, T_CONFIG_SCOPE_CONNECTION },       /* 6 */
390
		{ NULL,                         NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET }
391
	};
392
	
393
	if (!p) return HANDLER_ERROR;
394
	
395
	p->config_storage = calloc(1, srv->config_context->used * sizeof(specific_config *));
396
	
397
	for (i = 0; i < srv->config_context->used; i++) {
398
		plugin_config *s;
399
		
400
		s = calloc(1, sizeof(plugin_config));
401
		s->maxmemory = 256; /* 256M default */
402
		s->maxfilesize = 512; /* maxium 512k */
403
		s->lru_remove_count = 200; /* default 200 */
404
		s->enable = 1; /* default to cache content into memory */
405
		s->expires = 0; /* default to check stat at every request */
406
		s->filetypes = array_init();
407
		s->thresold = 0; /* 0 just like normal LRU algorithm */
408
		
409
		cv[0].destination = &(s->maxmemory);
410
		cv[1].destination = &(s->maxfilesize);
411
		cv[2].destination = &(s->lru_remove_count);
412
		cv[3].destination = &(s->enable);
413
		cv[4].destination = &(s->expires);
414
		cv[5].destination = s->filetypes;
415
		cv[6].destination = &(s->thresold);
416
		
417
		p->config_storage[i] = s;
418
	
419
		if (0 != config_insert_values_global(srv, ((data_config *)srv->config_context->data[i])->value, cv)) {
420
			return HANDLER_ERROR;
421
		}
422
		s->expires *= 60;
423

  
424
		if (s->thresold < 0) s->thresold = 0;
425
		if (s->thresold > 0)
426
#ifdef LIGHTTPD_V14
427
			status_counter_set(srv, CONST_STR_LEN("mem-cache.slru-thresold"), s->thresold);
428
#else
429
			status_counter_set(CONST_STR_LEN("mem-cache.slru-thresold"), s->thresold);
430
#endif
431
	}
432
	
433
	return HANDLER_GO_ON;
434
}
435

  
436
/* the famous DJB hash function for strings from stat_cache.c*/
437
static uint32_t hashme(buffer *str) {
438
	uint32_t hash = 5381;
439
	const char *s;
440
	for (s = str->ptr; *s; s++) {
441
		hash = ((hash << 5) + hash) + *s;
442
	}
443

  
444
	hash &= ~(1 << 31); /* strip the highest bit */
445

  
446
	return hash;
447
}
448

  
449
#define PATCH_OPTION(x) \
450
	p->conf.x = s->x
451

  
452
static int mod_mem_cache_patch_connection(server *srv, connection *con, plugin_data *p) {
453
	size_t i, j;
454
	plugin_config *s = p->config_storage[0];
455
	
456
	PATCH_OPTION(maxmemory);
457
	PATCH_OPTION(maxfilesize);
458
	PATCH_OPTION(lru_remove_count);
459
	PATCH_OPTION(enable);
460
	PATCH_OPTION(expires);
461
	PATCH_OPTION(filetypes);
462
	PATCH_OPTION(thresold);
463
	
464
	/* skip the first, the global context */
465
	for (i = 1; i < srv->config_context->used; i++) {
466
		data_config *dc = (data_config *)srv->config_context->data[i];
467
		s = p->config_storage[i];
468
		
469
		/* condition didn't match */
470
		if (!config_check_cond(srv, con, dc)) continue;
471
		
472
		/* merge config */
473
		for (j = 0; j < dc->value->used; j++) {
474
			data_unset *du = dc->value->data[j];
475
			
476
			if (buffer_is_equal_string(du->key, CONST_STR_LEN(CONFIG_MEM_CACHE_ENABLE))) {
477
				PATCH_OPTION(enable);
478
			} else if (buffer_is_equal_string(du->key, CONST_STR_LEN(CONFIG_MEM_CACHE_MAX_FILE_SIZE))) {
479
				PATCH_OPTION(maxfilesize);
480
			} else if (buffer_is_equal_string(du->key, CONST_STR_LEN(CONFIG_MEM_CACHE_MAX_MEMORY))) {
481
				PATCH_OPTION(maxmemory);
482
			} else if (buffer_is_equal_string(du->key, CONST_STR_LEN(CONFIG_MEM_CACHE_FILE_TYPES))) {
483
				PATCH_OPTION(filetypes);
484
			} else if (buffer_is_equal_string(du->key, CONST_STR_LEN(CONFIG_MEM_CACHE_EXPIRE_TIME))) {
485
				PATCH_OPTION(expires);
486
			} else if (buffer_is_equal_string(du->key, CONST_STR_LEN(CONFIG_MEM_CACHE_LRU_REMOVE_COUNT))) {
487
				PATCH_OPTION(lru_remove_count);
488
			} else if (buffer_is_equal_string(du->key, CONST_STR_LEN(CONFIG_MEM_CACHE_SLRU_THRESOLD))) {
489
				PATCH_OPTION(thresold);
490
			}
491
		}
492
	}
493
	
494
	return 0;
495
}
496

  
497
#if LRUDEBUG
498
static void print_static_lru(server *srv) {
499
	int d1;
500
	struct cache_entry *g, *g2;
501

  
502
	if (lruheader == 0 || lruend == 0) return;
503
	d1 = lruheader;
504
	TRACE("total lru number = %d, total memory used = %ld", cachenumber, (long) usedmemory);
505
	while(d1) {
506
		g = memcache + d1;
507
		if(g->content) 
508
			TRACE("list %d: data_length %d, block size %d, path %s", d1, g->content->used, g->content->size, g->path->ptr);
509
		else
510
			TRACE("list %d: path %s", d1, g->path->ptr);
511
		g2 = g->scnext;
512
		while(g2) {
513
			if(g2->content) 
514
				TRACE("chain %d: data_length %d, block size %d, path %s", d1, g2->content->used, g2->content->size, g2->path->ptr);
515
			else
516
				TRACE("chain %d: path %s", d1, g2->path->ptr);
517
			g2 = g2->scnext;
518
		}
519
		d1 = memcache[d1].next;
520
	}
521
}
522
#endif
523

  
524
/* free all cache-entry and init cache_entry */
525
static void free_all_cache_entry(server *srv) {
526
	int j;
527

  
528
	UNUSED(srv);
529
	for (j = 0; j <= MEM_CACHE_NUM; j++) {
530
		free_cache_entry_chain(memcache+j);
531
	}
532

  
533
	memset(memcache, 0, sizeof(struct cache_entry)*(MEM_CACHE_NUM+1));
534
	lruheader = lruend = cachenumber = 0;
535
	usedmemory = 0;
536
#ifdef LIGHTTPD_V14
537
	log_error_write(srv, __FILE__, __LINE__, "s", "free all state_cache data due to data inconsistence");
538
	status_counter_set(srv, CONST_STR_LEN("mem-cache.usedmemory"), usedmemory);
539
	status_counter_set(srv, CONST_STR_LEN("mem-cache.cachenumber"), cachenumber);
540
#else
541
	TRACE("%s", "free all state_cache data due to data inconsistence");
542
	status_counter_set(CONST_STR_LEN("mem-cache.memory-inuse(MB)"), ((long)usedmemory)>>20);
543
	status_counter_set(CONST_STR_LEN("mem-cache.cached-items"), cachenumber);
544
#endif
545
}
546

  
547
static void free_cache_entry_by_lru(server *srv, const int num) {
548
	int i, d1;
549

  
550
	if (lruheader == 0 || lruend == 0) return;
551
	d1 = lruheader;
552
#if LRUDEBUG
553
	log_error_write(srv, __FILE__, __LINE__, "sdsd",
554
			"memory size before lru remove:", usedmemory, "cachenumber", cachenumber);
555
#endif
556
	for(i = 0; i < num; i++, d1=lruheader) {
557
		lruheader = memcache[d1].next;
558
		if (memcache[d1].inuse) {
559
			memcache[d1].next = memcache[d1].prev = 0;
560
			free_cache_entry_chain(memcache+d1);
561
			memcache[d1].inuse = 0;
562
			memset(memcache+d1, 0, sizeof(struct cache_entry));
563
		} else { 
564
			/* wrong lru data, free them all! */
565
			free_all_cache_entry(srv);
566
			break;
567
		}
568
		if (lruheader == 0) { lruheader = lruend = cachenumber = usedmemory = 0; break; }
569
	}
570
#ifdef LIGHTTPD_V14
571
	status_counter_set(srv, CONST_STR_LEN("mem-cache.usedmemory"), usedmemory);
572
	status_counter_set(srv, CONST_STR_LEN("mem-cache.cachenumber"), cachenumber);
573
#else
574
	status_counter_set(CONST_STR_LEN("mem-cache.memory-inuse(MB)"), ((long)usedmemory)>>20);
575
	status_counter_set(CONST_STR_LEN("mem-cache.cached-items"), cachenumber);
576
#endif
577
#if LRUDEBUG
578
	log_error_write(srv, __FILE__, __LINE__, "sdsdsds",
579
			"memory size:", usedmemory, "after remove:", i, "items", cachenumber, "remained");
580
#endif
581
}
582

  
583
/* update LRU lists */
584
static void update_lru(server *srv, int i) {
585
	int d1, d2;
586

  
587
	if (i == 0 || memcache[i].inuse == 0) return;
588
	if (lruheader == 0 || lruend == 0) { 
589
		/* first item */
590
		memcache[i].prev = memcache[i].next = 0;
591
		lruheader = lruend = i;
592
	} else if (i != lruend && i != lruheader){ 
593
		/* re-order lru list */
594
		d1 = memcache[i].prev;
595
		d2 = memcache[i].next;
596
		if (d1 == 0 && d2 == 0) { 
597
			/* new item */
598
			memcache[i].prev = lruend;
599
			memcache[i].next = 0;
600
			memcache[lruend].next = i;
601
			lruend = i;
602
		} else if (d1 == 0 || d2 == 0) {
603
			/* wrong lru , free all cached data and reset lru */
604
			free_all_cache_entry(srv);
605
		} else {
606
			memcache[d1].next = d2;
607
			memcache[d2].prev = d1;
608
			/* append to end of list */
609
			memcache[lruend].next = i;
610
			memcache[i].next = 0;
611
			memcache[i].prev = lruend;
612
			lruend = i;
613
		}
614
	} else if (i == lruend) { 
615
		/* end of lru, no change */
616
	} else if (i == lruheader) { 
617
		/* move header to the end*/
618
		lruheader = memcache[i].next;
619
		memcache[lruheader].prev = 0;
620
		memcache[i].prev = lruend;
621
		memcache[i].next = 0;
622
		memcache[lruend].next = i;
623
		lruend = i;
624
	}
625
}
626

  
627
/* read file content into buffer dst 
628
 * return 1 if failed
629
 */
630
static int readfile_into_buffer(server *srv, connection *con, int filesize, buffer *dst) {
631
	int ifd;
632

  
633
	UNUSED(srv);
634

  
635
	if (dst == NULL) return 1;
636
	if (dst->size <= (size_t) filesize) return 1;
637
	if (-1 == (ifd = open(con->physical.path->ptr, O_RDONLY | O_BINARY))) {
638
#ifdef LIGHTTPD_V14
639
		log_error_write(srv, __FILE__, __LINE__, "sbss", "opening plain-file", 
640
				con->physical.path, "failed", strerror(errno));
641
#else
642
		TRACE("fail to open %s: %s", con->physical.path->ptr, strerror(errno));
643
#endif
644
		return 1;
645
	}
646

  
647
	if (filesize == read(ifd, dst->ptr, filesize)) { 
648
		dst->ptr[filesize] = '\0';
649
		dst->used = filesize + 1;
650
		close(ifd); 
651
		return 0; 
652
	} else { 
653
#ifdef LIGHTTPD_V14
654
		log_error_write(srv, __FILE__, __LINE__, "sds", "fail to read all of ", 
655
				filesize, "bytes into memory");
656
#else
657
		TRACE("fail to read %d bytes of %s into memory", filesize, con->physical.path->ptr);
658
#endif
659
		close(ifd); 
660
		return 1; 
661
	}
662
}
663

  
664
/* if HIT + not expire, set status = 1 and return ptr
665
 * else if HIT but expired, set status = 0 and return ptr
666
 * else if not HIT, set status = 0 and return NULL
667
 */
668
static struct cache_entry *check_mem_cache(server *srv, connection *con, plugin_data *p, int *status, const uint32_t i) {
669
	struct cache_entry *c;
670
	int success = 0;
671

  
672
	c = memcache+i;
673
	*status = 0;
674
	
675
	while (c) {
676
		if (c->path && buffer_is_equal(c->path, con->physical.path)) {
677
			success = 1;
678
			break;
679
		}
680
		c = c->scnext;
681
	}
682

  
683
	if (success) {
684
		if (c->inuse && p->conf.expires 
685
			&& (srv->cur_ts - c->ct)  <= (time_t )p->conf.expires)
686
			*status = 1;
687
		return c;
688
	}
689

  
690
	return NULL;
691
}
692

  
693
static struct cache_entry *get_mem_cache_entry(const uint32_t hash) {
694
	uint32_t i;
695
	struct cache_entry *c1, *c2;
696

  
697
	i = (hash & (MEM_CACHE_NUM-1))+1;
698
	c1 = c2 = memcache+i;
699
	
700
	/* try to find unused item first */
701
	while(c1 && c1->inuse) {
702
		c2 = c1;
703
		c1 = c1->scnext;
704
	}
705
	if (c1) return c1; /* use the first unused item */
706
	/* we need allocate new cache_entry */
707
	c1 = (struct cache_entry *)malloc(sizeof(struct cache_entry));
708
	if (c1 == NULL) return NULL;
709
	memset(c1, 0, sizeof(struct cache_entry));
710
	/* put new cache_entry into hash table */
711
	c2->scnext = c1;
712
	return c1;
713
}
714

  
715
/* return 0 when probation->count > p->conf.thresold in 24 hours or p->conf.thresold == 0
716
 * otherwise return 1
717
 */
718
static int check_probation_lru(server *srv, plugin_data *p, int hash) {
719
	struct probation *pr;
720
	int status = 1;
721

  
722
	if (p->conf.thresold == 0) return 0;
723
	plru = splaytree_splay(plru, hash);
724
	if (plru == NULL || plru->key != hash) { /* first splaytree node or new node*/
725
		pr = (struct probation *) malloc(sizeof(struct probation));
726
		if (pr == NULL) { /* out of memory */
727
			return 1;
728
		}
729
		pr->count = 1;
730
		pr->startts = srv->cur_ts;
731
		plru = splaytree_insert(plru, hash, (void *) pr);
732
	} else { /* matched */
733
		pr = (struct probation *) plru->data;
734
		if ((srv->cur_ts - pr->startts) > 86400) {
735
			/* keep track of last 24 hours only */
736
			pr->count = 0;
737
			pr->startts = srv->cur_ts;
738
		}
739
		pr->count ++;
740
		if (pr->count > p->conf.thresold) {
741
			free(pr);
742
			plru = splaytree_delete(plru, hash);
743
			status = 0;
744
		}
745
	}
746
	return status;
747
}
748

  
749
handler_t mod_mem_cache_uri_handler(server *srv, connection *con, void *p_d) {
750
	plugin_data *p = p_d;
751
	uint32_t hash;
752
	int i = 0, success = 0;
753
	size_t m;
754
	stat_cache_entry *sce = NULL;
755
	buffer *mtime;
756
	data_string *ds;
757
	struct cache_entry *cache;
758
	
759
	/* someone else has done a decision for us */
760
	if (con->http_status != 0) return HANDLER_GO_ON;
761
	if (con->uri.path->used == 0) return HANDLER_GO_ON;
762
	if (con->physical.path->used == 0) return HANDLER_GO_ON;
763
	
764
	/* someone else has handled this request */
765
	if (con->mode != DIRECT) return HANDLER_GO_ON;
766
#ifdef LIGHTTPD_V14
767
	if (con->file_finished) return HANDLER_GO_ON;
768
#else
769
	if (con->send->is_closed) return HANDLER_GO_ON;
770
#endif
771

  
772
	/* we only handle GET, POST and HEAD */
773
	switch(con->request.http_method) {
774
	case HTTP_METHOD_GET:
775
	case HTTP_METHOD_POST:
776
	case HTTP_METHOD_HEAD:
777
		break;
778
	default:
779
		return HANDLER_GO_ON;
780
	}
781
	
782
	if (con->conf.range_requests && NULL != array_get_element(con->request.headers, "Range"))
783
		return HANDLER_GO_ON;
784

  
785
	mod_mem_cache_patch_connection(srv, con, p);
786
	
787
	if (p->conf.enable == 0|| p->conf.maxfilesize == 0) return HANDLER_GO_ON;
788

  
789
	if (con->conf.log_request_handling) {
790
 		log_error_write(srv, __FILE__, __LINE__, "s", "-- mod_mem_cache_uri_handler called");
791
	}
792

  
793
	hash = hashme(con->physical.path);
794
	i = (hash & (MEM_CACHE_NUM-1))+1;
795
	cache = check_mem_cache(srv, con, p, &success, i);
796
	reqcount ++;
797

  
798
	if (success == 0 || cache == NULL) {
799
		/* going to put content into cache */
800
		if (HANDLER_ERROR == stat_cache_get_entry(srv, con, con->physical.path, &sce)) {
801
			return HANDLER_GO_ON;
802
		}
803
		/* we only handline regular files */
804
#ifdef HAVE_LSTAT
805
		if ((sce->is_symlink == 1) && !con->conf.follow_symlink) {
806
			con->http_status = 403;
807
			buffer_reset(con->physical.path);
808
			return HANDLER_FINISHED;
809
		}
810
#endif
811

  
812
		if (!S_ISREG(sce->st.st_mode)) {
813
			return HANDLER_GO_ON;
814
		}
815
		/* check filetypes */
816
		for (m = 0; m < p->conf.filetypes->used; m++) {
817
			ds = (data_string *)p->conf.filetypes->data[m];
818
			if (!ds) return HANDLER_GO_ON;
819
			if (sce->content_type->used &&
820
			    strncmp(ds->value->ptr, sce->content_type->ptr, ds->value->used-1)==0)
821
				break;
822
		}
823
		if (m && m == p->conf.filetypes->used)
824
			return HANDLER_GO_ON;
825
		if (sce->st.st_size == 0 || ((sce->st.st_size >> 10) > p->conf.maxfilesize)) 
826
			return HANDLER_GO_ON;
827

  
828
		if (cache == NULL) {
829
			/* check probation lru now */
830
			if (check_probation_lru(srv, p, hash))
831
				return HANDLER_GO_ON;
832
			cache = get_mem_cache_entry(hash);
833
			if (cache == NULL) {
834
				/* may be out of memory, just return GO_ON */
835
				return HANDLER_GO_ON;
836
			}
837
		}
838
		etag_mutate(con->physical.etag, sce->etag);
839

  
840
		if (cache->inuse == 0 || buffer_is_equal(con->physical.etag, cache->etag) == 0) {
841
			/* initialze cache's buffer if needed */
842
			cache_entry_reset(cache);
843
			if (cache->content->size <= sce->st.st_size) {
844
				usedmemory -= cache->content->size;
845
				buffer_prepare_copy(cache->content, sce->st.st_size);
846
				usedmemory += cache->content->size;
847
			}
848
			if (readfile_into_buffer(srv, con, sce->st.st_size, cache->content)) {
849
				return HANDLER_GO_ON;
850
			}
851
			/* increase cachenumber if needed */
852
			if (cache->inuse == 0) cachenumber ++;
853
			cache->inuse = 1;
854

  
855

  
856
			if (sce->content_type->used == 0) {
857
				buffer_copy_string_len(cache->content_type, CONST_STR_LEN("application/octet-stream"));
858
			} else {
859
				buffer_copy_string_buffer(cache->content_type, sce->content_type);
860
			}
861
			buffer_copy_string_buffer(cache->etag, con->physical.etag);
862
			buffer_copy_string_buffer(cache->path, con->physical.path);
863
			mtime = strftime_cache_get(srv, sce->st.st_mtime);
864
			buffer_copy_string_buffer(cache->mtime, mtime);
865
			cache->ct = srv->cur_ts;
866
#ifdef LIGHTTPD_V14
867
			status_counter_set(srv, CONST_STR_LEN("mem-cache.usedmemory"), usedmemory);
868
			status_counter_set(srv, CONST_STR_LEN("mem-cache.cachenumber"), cachenumber);
869
#else
870
			status_counter_set(CONST_STR_LEN("mem-cache.memory-inuse(MB)"), ((long)usedmemory)>>20);
871
			status_counter_set(CONST_STR_LEN("mem-cache.cached-items"), cachenumber);
872
#endif
873
		} else  {
874
			cache->ct = srv->cur_ts;
875
			reqhit ++;
876
//			response_header_overwrite(srv, con, CONST_STR_LEN("X-Mem-Cache"), CONST_STR_LEN("by memcache"));
877
		}
878
	} else {
879
		reqhit ++;
880
//		response_header_overwrite(srv, con, CONST_STR_LEN("X-Mem-Cache"), CONST_STR_LEN("by memcache"));
881
	}
882

  
883
	if (NULL == array_get_element(con->response.headers, "Content-Type")) {
884
		response_header_overwrite(srv, con, CONST_STR_LEN("Content-Type"), CONST_BUF_LEN(cache->content_type));
885
	}
886
	
887
	if (NULL == array_get_element(con->response.headers, "ETag")) {
888
		response_header_overwrite(srv, con, CONST_STR_LEN("ETag"), CONST_BUF_LEN(cache->etag));
889
	}
890

  
891
	/* prepare header */
892
	if (NULL == (ds = (data_string *)array_get_element(con->response.headers, "Last-Modified"))) {
893
		mtime = cache->mtime;
894
		response_header_overwrite(srv, con, CONST_STR_LEN("Last-Modified"), CONST_BUF_LEN(mtime));
895
	} else mtime = ds->value;
896

  
897
#ifdef LIGHTTPD_V14
898
	status_counter_set(srv, CONST_STR_LEN("mem-cache.hitpercent"), reqhit*100/reqcount);
899
#else
900
	status_counter_set(CONST_STR_LEN("mem-cache.hitrate(%)"), (int) (((float)reqhit/(float)reqcount)*100));
901
#endif
902
	if (HANDLER_FINISHED == http_response_handle_cachable(srv, con, mtime, cache->etag))
903
		return HANDLER_FINISHED;
904

  
905
#ifdef LIGHTTPD_V14
906
	chunkqueue_append_buffer(con->write_queue, cache->content);
907
#else
908
	chunkqueue_append_buffer(con->send, cache->content);
909
#endif
910
	buffer_reset(con->physical.path);
911
	update_lru(srv, i);
912
	if ((((long)usedmemory) >> 20) > p->conf.maxmemory) {
913
		/* free least used items */
914
		free_cache_entry_by_lru(srv, p->conf.lru_remove_count); 
915
	}
916
#ifdef LIGHTTPD_V14
917
	con->file_finished = 1;
918
#else
919
	con->send->is_closed = 1;
920
#endif
921
	
922
	return HANDLER_FINISHED;
923
}
924

  
925
/* this function is called at dlopen() time and inits the callbacks */
926

  
927
int mod_mem_cache_plugin_init(plugin *p) {
928
	p->version     = LIGHTTPD_VERSION_ID;
929
	p->name        = buffer_init_string("mem_cache");
930
	
931
	p->init        = mod_mem_cache_init;
932
	/*p->handle_physical = mod_mem_cache_uri_handler; */
933
	p->handle_subrequest_start = mod_mem_cache_uri_handler;
934
	p->set_defaults  = mod_mem_cache_set_defaults;
935
	p->cleanup     = mod_mem_cache_free;
936
	
937
	p->data        = NULL;
938
	
939
	return 0;
940
}
./src/mod_staticfile.c 2008-03-14 09:51:08.000000000 +0800
472 472
			mtime = ds->value;
473 473
		}
474 474

  
475
		if (HANDLER_FINISHED == http_response_handle_cachable(srv, con, mtime)) {
475
		if (HANDLER_FINISHED == http_response_handle_cachable(srv, con, mtime, con->physical.etag)) {
476 476
			return HANDLER_FINISHED;
477 477
		}
478 478
	}
./src/response.h 2008-03-14 09:49:42.000000000 +0800
13 13

  
14 14
handler_t http_response_prepare(server *srv, connection *con);
15 15
int http_response_redirect_to_directory(server *srv, connection *con);
16
int http_response_handle_cachable(server *srv, connection *con, buffer * mtime);
16
int http_response_handle_cachable(server *srv, connection *con, buffer * mtime, buffer *etag);
17 17

  
18 18
buffer * strftime_cache_get(server *srv, time_t last_mod);
19 19
#endif
./src/mod_cml_lua.c 2008-08-10 02:35:13.000000000 +0200
425 425
				tbuf.ptr = NULL;
426 426
			}
427 427

  
428
			if (HANDLER_FINISHED == http_response_handle_cachable(srv, con, &tbuf)) {
428
			if (HANDLER_FINISHED == http_response_handle_cachable(srv, con, &tbuf, con->physical.etag)) {
429 429
				/* ok, the client already has our content,
430 430
				 * no need to send it again */
src/mod_compress.c (Arbeitskopie)
771 771

  
772 772
					/* try matching original etag of uncompressed version */
773 773
					etag_mutate(con->physical.etag, sce->etag);
774
					if (HANDLER_FINISHED == http_response_handle_cachable(srv, con, mtime)) {
774
					if (HANDLER_FINISHED == http_response_handle_cachable(srv, con, mtime,con->physical.etag)) {
775 775
						response_header_overwrite(srv, con, CONST_STR_LEN("Content-Type"), CONST_BUF_LEN(sce->content_type));
776 776
						response_header_overwrite(srv, con, CONST_STR_LEN("Last-Modified"), CONST_BUF_LEN(mtime));
777 777
						response_header_overwrite(srv, con, CONST_STR_LEN("ETag"), CONST_BUF_LEN(con->physical.etag));
......
796 796
					buffer_append_string(srv->tmp_buf, compression_name);
797 797
					etag_mutate(con->physical.etag, srv->tmp_buf);
798 798

  
799
					if (HANDLER_FINISHED == http_response_handle_cachable(srv, con, mtime)) {
799
					if (HANDLER_FINISHED == http_response_handle_cachable(srv, con, mtime, con->physical.etag)) {
800 800
						response_header_overwrite(srv, con, CONST_STR_LEN("Content-Encoding"), compression_name, strlen(compression_name));
801 801
						response_header_overwrite(srv, con, CONST_STR_LEN("Content-Type"), CONST_BUF_LEN(sce->content_type));
802 802
						response_header_overwrite(srv, con, CONST_STR_LEN("Last-Modified"), CONST_BUF_LEN(mtime));