File:  [ELWIX - Embedded LightWeight unIX -] / embedaddon / lighttpd / src / mod_expire.c
Revision 1.1.1.2 (vendor branch): download - view: text, annotated - select for diffs - revision graph
Sun Jun 15 20:20:06 2014 UTC (10 years ago) by misho
Branches: lighttpd, MAIN
CVS tags: v1_4_35p0, v1_4_35, HEAD
lighttpd 1.4.35

    1: #include "base.h"
    2: #include "log.h"
    3: #include "buffer.h"
    4: #include "response.h"
    5: 
    6: #include "plugin.h"
    7: #include "stat_cache.h"
    8: 
    9: #include <ctype.h>
   10: #include <stdlib.h>
   11: #include <string.h>
   12: #include <time.h>
   13: 
   14: /**
   15:  * this is a expire module for a lighttpd
   16:  *
   17:  * set 'Expires:' HTTP Headers on demand
   18:  */
   19: 
   20: 
   21: 
   22: /* plugin config for all request/connections */
   23: 
   24: typedef struct {
   25: 	array *expire_url;
   26: } plugin_config;
   27: 
   28: typedef struct {
   29: 	PLUGIN_DATA;
   30: 
   31: 	buffer *expire_tstmp;
   32: 
   33: 	plugin_config **config_storage;
   34: 
   35: 	plugin_config conf;
   36: } plugin_data;
   37: 
   38: /* init the plugin data */
   39: INIT_FUNC(mod_expire_init) {
   40: 	plugin_data *p;
   41: 
   42: 	p = calloc(1, sizeof(*p));
   43: 
   44: 	p->expire_tstmp = buffer_init();
   45: 
   46: 	buffer_prepare_copy(p->expire_tstmp, 255);
   47: 
   48: 	return p;
   49: }
   50: 
   51: /* detroy the plugin data */
   52: FREE_FUNC(mod_expire_free) {
   53: 	plugin_data *p = p_d;
   54: 
   55: 	UNUSED(srv);
   56: 
   57: 	if (!p) return HANDLER_GO_ON;
   58: 
   59: 	buffer_free(p->expire_tstmp);
   60: 
   61: 	if (p->config_storage) {
   62: 		size_t i;
   63: 		for (i = 0; i < srv->config_context->used; i++) {
   64: 			plugin_config *s = p->config_storage[i];
   65: 			if (!s) continue;
   66: 
   67: 			array_free(s->expire_url);
   68: 			free(s);
   69: 		}
   70: 		free(p->config_storage);
   71: 	}
   72: 
   73: 	free(p);
   74: 
   75: 	return HANDLER_GO_ON;
   76: }
   77: 
   78: static int mod_expire_get_offset(server *srv, plugin_data *p, buffer *expire, time_t *offset) {
   79: 	char *ts;
   80: 	int type = -1;
   81: 	time_t retts = 0;
   82: 
   83: 	UNUSED(p);
   84: 
   85: 	/*
   86: 	 * parse
   87: 	 *
   88: 	 * '(access|now|modification) [plus] {<num> <type>}*'
   89: 	 *
   90: 	 * e.g. 'access 1 years'
   91: 	 */
   92: 
   93: 	if (expire->used == 0) {
   94: 		log_error_write(srv, __FILE__, __LINE__, "s",
   95: 				"empty:");
   96: 		return -1;
   97: 	}
   98: 
   99: 	ts = expire->ptr;
  100: 
  101: 	if (0 == strncmp(ts, "access ", 7)) {
  102: 		type  = 0;
  103: 		ts   += 7;
  104: 	} else if (0 == strncmp(ts, "now ", 4)) {
  105: 		type  = 0;
  106: 		ts   += 4;
  107: 	} else if (0 == strncmp(ts, "modification ", 13)) {
  108: 		type  = 1;
  109: 		ts   += 13;
  110: 	} else {
  111: 		/* invalid type-prefix */
  112: 		log_error_write(srv, __FILE__, __LINE__, "ss",
  113: 				"invalid <base>:", ts);
  114: 		return -1;
  115: 	}
  116: 
  117: 	if (0 == strncmp(ts, "plus ", 5)) {
  118: 		/* skip the optional plus */
  119: 		ts   += 5;
  120: 	}
  121: 
  122: 	/* the rest is just <number> (years|months|weeks|days|hours|minutes|seconds) */
  123: 	while (1) {
  124: 		char *space, *err;
  125: 		int num;
  126: 
  127: 		if (NULL == (space = strchr(ts, ' '))) {
  128: 			log_error_write(srv, __FILE__, __LINE__, "ss",
  129: 					"missing space after <num>:", ts);
  130: 			return -1;
  131: 		}
  132: 
  133: 		num = strtol(ts, &err, 10);
  134: 		if (*err != ' ') {
  135: 			log_error_write(srv, __FILE__, __LINE__, "ss",
  136: 					"missing <type> after <num>:", ts);
  137: 			return -1;
  138: 		}
  139: 
  140: 		ts = space + 1;
  141: 
  142: 		if (NULL != (space = strchr(ts, ' '))) {
  143: 			int slen;
  144: 			/* */
  145: 
  146: 			slen = space - ts;
  147: 
  148: 			if (slen == 5 &&
  149: 			    0 == strncmp(ts, "years", slen)) {
  150: 				num *= 60 * 60 * 24 * 30 * 12;
  151: 			} else if (slen == 6 &&
  152: 				   0 == strncmp(ts, "months", slen)) {
  153: 				num *= 60 * 60 * 24 * 30;
  154: 			} else if (slen == 5 &&
  155: 				   0 == strncmp(ts, "weeks", slen)) {
  156: 				num *= 60 * 60 * 24 * 7;
  157: 			} else if (slen == 4 &&
  158: 				   0 == strncmp(ts, "days", slen)) {
  159: 				num *= 60 * 60 * 24;
  160: 			} else if (slen == 5 &&
  161: 				   0 == strncmp(ts, "hours", slen)) {
  162: 				num *= 60 * 60;
  163: 			} else if (slen == 7 &&
  164: 				   0 == strncmp(ts, "minutes", slen)) {
  165: 				num *= 60;
  166: 			} else if (slen == 7 &&
  167: 				   0 == strncmp(ts, "seconds", slen)) {
  168: 				num *= 1;
  169: 			} else {
  170: 				log_error_write(srv, __FILE__, __LINE__, "ss",
  171: 						"unknown type:", ts);
  172: 				return -1;
  173: 			}
  174: 
  175: 			retts += num;
  176: 
  177: 			ts = space + 1;
  178: 		} else {
  179: 			if (0 == strcmp(ts, "years")) {
  180: 				num *= 60 * 60 * 24 * 30 * 12;
  181: 			} else if (0 == strcmp(ts, "months")) {
  182: 				num *= 60 * 60 * 24 * 30;
  183: 			} else if (0 == strcmp(ts, "weeks")) {
  184: 				num *= 60 * 60 * 24 * 7;
  185: 			} else if (0 == strcmp(ts, "days")) {
  186: 				num *= 60 * 60 * 24;
  187: 			} else if (0 == strcmp(ts, "hours")) {
  188: 				num *= 60 * 60;
  189: 			} else if (0 == strcmp(ts, "minutes")) {
  190: 				num *= 60;
  191: 			} else if (0 == strcmp(ts, "seconds")) {
  192: 				num *= 1;
  193: 			} else {
  194: 				log_error_write(srv, __FILE__, __LINE__, "ss",
  195: 						"unknown type:", ts);
  196: 				return -1;
  197: 			}
  198: 
  199: 			retts += num;
  200: 
  201: 			break;
  202: 		}
  203: 	}
  204: 
  205: 	if (offset != NULL) *offset = retts;
  206: 
  207: 	return type;
  208: }
  209: 
  210: 
  211: /* handle plugin config and check values */
  212: 
  213: SETDEFAULTS_FUNC(mod_expire_set_defaults) {
  214: 	plugin_data *p = p_d;
  215: 	size_t i = 0, k;
  216: 
  217: 	config_values_t cv[] = {
  218: 		{ "expire.url",                 NULL, T_CONFIG_ARRAY, T_CONFIG_SCOPE_CONNECTION },       /* 0 */
  219: 		{ NULL,                         NULL, T_CONFIG_UNSET, T_CONFIG_SCOPE_UNSET }
  220: 	};
  221: 
  222: 	if (!p) return HANDLER_ERROR;
  223: 
  224: 	p->config_storage = calloc(1, srv->config_context->used * sizeof(plugin_config *));
  225: 
  226: 	for (i = 0; i < srv->config_context->used; i++) {
  227: 		plugin_config *s;
  228: 
  229: 		s = calloc(1, sizeof(plugin_config));
  230: 		s->expire_url    = array_init();
  231: 
  232: 		cv[0].destination = s->expire_url;
  233: 
  234: 		p->config_storage[i] = s;
  235: 
  236: 		if (0 != config_insert_values_global(srv, ((data_config *)srv->config_context->data[i])->value, cv)) {
  237: 			return HANDLER_ERROR;
  238: 		}
  239: 
  240: 		for (k = 0; k < s->expire_url->used; k++) {
  241: 			data_string *ds = (data_string *)s->expire_url->data[k];
  242: 
  243: 			/* parse lines */
  244: 			if (-1 == mod_expire_get_offset(srv, p, ds->value, NULL)) {
  245: 				log_error_write(srv, __FILE__, __LINE__, "sb",
  246: 						"parsing expire.url failed:", ds->value);
  247: 				return HANDLER_ERROR;
  248: 			}
  249: 		}
  250: 	}
  251: 
  252: 
  253: 	return HANDLER_GO_ON;
  254: }
  255: 
  256: #define PATCH(x) \
  257: 	p->conf.x = s->x;
  258: static int mod_expire_patch_connection(server *srv, connection *con, plugin_data *p) {
  259: 	size_t i, j;
  260: 	plugin_config *s = p->config_storage[0];
  261: 
  262: 	PATCH(expire_url);
  263: 
  264: 	/* skip the first, the global context */
  265: 	for (i = 1; i < srv->config_context->used; i++) {
  266: 		data_config *dc = (data_config *)srv->config_context->data[i];
  267: 		s = p->config_storage[i];
  268: 
  269: 		/* condition didn't match */
  270: 		if (!config_check_cond(srv, con, dc)) continue;
  271: 
  272: 		/* merge config */
  273: 		for (j = 0; j < dc->value->used; j++) {
  274: 			data_unset *du = dc->value->data[j];
  275: 
  276: 			if (buffer_is_equal_string(du->key, CONST_STR_LEN("expire.url"))) {
  277: 				PATCH(expire_url);
  278: 			}
  279: 		}
  280: 	}
  281: 
  282: 	return 0;
  283: }
  284: #undef PATCH
  285: 
  286: URIHANDLER_FUNC(mod_expire_path_handler) {
  287: 	plugin_data *p = p_d;
  288: 	int s_len;
  289: 	size_t k;
  290: 
  291: 	if (con->uri.path->used == 0) return HANDLER_GO_ON;
  292: 
  293: 	mod_expire_patch_connection(srv, con, p);
  294: 
  295: 	s_len = con->uri.path->used - 1;
  296: 
  297: 	for (k = 0; k < p->conf.expire_url->used; k++) {
  298: 		data_string *ds = (data_string *)p->conf.expire_url->data[k];
  299: 		int ct_len = ds->key->used - 1;
  300: 
  301: 		if (ct_len > s_len) continue;
  302: 		if (ds->key->used == 0) continue;
  303: 
  304: 		if (0 == strncmp(con->uri.path->ptr, ds->key->ptr, ct_len)) {
  305: 			time_t ts, expires;
  306: 			size_t len;
  307: 			stat_cache_entry *sce = NULL;
  308: 
  309: 			/* if stat fails => sce == NULL, ignore return value */
  310: 			(void) stat_cache_get_entry(srv, con, con->physical.path, &sce);
  311: 
  312: 			switch(mod_expire_get_offset(srv, p, ds->value, &ts)) {
  313: 			case 0:
  314: 				/* access */
  315: 				expires = (ts + srv->cur_ts);
  316: 				break;
  317: 			case 1:
  318: 				/* modification */
  319: 
  320: 				/* can't set modification based expire header if
  321: 				 * mtime is not available
  322: 				 */
  323: 				if (NULL == sce) return HANDLER_GO_ON;
  324: 
  325: 				expires = (ts + sce->st.st_mtime);
  326: 				break;
  327: 			default:
  328: 				/* -1 is handled at parse-time */
  329: 				return HANDLER_ERROR;
  330: 			}
  331: 
  332: 			/* expires should be at least srv->cur_ts */
  333: 			if (expires < srv->cur_ts) expires = srv->cur_ts;
  334: 
  335: 			if (0 == (len = strftime(p->expire_tstmp->ptr, p->expire_tstmp->size - 1,
  336: 					   "%a, %d %b %Y %H:%M:%S GMT", gmtime(&(expires))))) {
  337: 				/* could not set expire header, out of mem */
  338: 
  339: 				return HANDLER_GO_ON;
  340: 			}
  341: 
  342: 			p->expire_tstmp->used = len + 1;
  343: 
  344: 			/* HTTP/1.0 */
  345: 			response_header_overwrite(srv, con, CONST_STR_LEN("Expires"), CONST_BUF_LEN(p->expire_tstmp));
  346: 
  347: 			/* HTTP/1.1 */
  348: 			buffer_copy_string_len(p->expire_tstmp, CONST_STR_LEN("max-age="));
  349: 			buffer_append_long(p->expire_tstmp, expires - srv->cur_ts); /* as expires >= srv->cur_ts the difference is >= 0 */
  350: 
  351: 			response_header_append(srv, con, CONST_STR_LEN("Cache-Control"), CONST_BUF_LEN(p->expire_tstmp));
  352: 
  353: 			return HANDLER_GO_ON;
  354: 		}
  355: 	}
  356: 
  357: 	/* not found */
  358: 	return HANDLER_GO_ON;
  359: }
  360: 
  361: /* this function is called at dlopen() time and inits the callbacks */
  362: 
  363: int mod_expire_plugin_init(plugin *p);
  364: int mod_expire_plugin_init(plugin *p) {
  365: 	p->version     = LIGHTTPD_VERSION_ID;
  366: 	p->name        = buffer_init_string("expire");
  367: 
  368: 	p->init        = mod_expire_init;
  369: 	p->handle_subrequest_start = mod_expire_path_handler;
  370: 	p->set_defaults  = mod_expire_set_defaults;
  371: 	p->cleanup     = mod_expire_free;
  372: 
  373: 	p->data        = NULL;
  374: 
  375: 	return 0;
  376: }

FreeBSD-CVSweb <freebsd-cvsweb@FreeBSD.org>