diff options
author | Sergey Poznyakoff <gray@gnu.org.ua> | 2015-02-12 16:30:53 +0200 |
---|---|---|
committer | Sergey Poznyakoff <gray@gnu.org.ua> | 2015-02-12 16:30:53 +0200 |
commit | 133e104d9efac43c0cf243207c2c5eaf0343bced (patch) | |
tree | 64399bcc3d6b33de2fb911d291ba90656b028fc1 | |
parent | c36a2210ed6cd53c5c65418fa9c20ac22dec8c59 (diff) | |
download | vmod-variable-133e104d9efac43c0cf243207c2c5eaf0343bced.tar.gz vmod-variable-133e104d9efac43c0cf243207c2c5eaf0343bced.tar.bz2 |
Improve batchset
The new syntax allows for supplying replacement patterns as in
variable.batchset("x:duration=\2s", ...
* configure.ac: Remove unneeded check
* src/variable.c (vardef): New member repl.
(bref_expand): New function.
(vmod_batchset): Change handling of vars string.
* tests/batchset.at: Update.
-rw-r--r-- | configure.ac | 18 | ||||
-rw-r--r-- | src/variable.c | 139 | ||||
-rw-r--r-- | tests/batchset.at | 6 |
3 files changed, 101 insertions, 62 deletions
diff --git a/configure.ac b/configure.ac index 2fa32f6..f21fcfb 100644 --- a/configure.ac +++ b/configure.ac @@ -87,61 +87,43 @@ AM_CONDITIONAL([VARNISH4],[test $VARNISHVERSION -eq 4]) AC_CHECK_FILE([$VARNISHSRC/bin/varnishtest/varnishtest], [], [AC_MSG_FAILURE([cannot find "$VARNISHSRC/bin/varnishtest/varnishtest". Please build your varnish source directory])] ) # vmod installation dir AC_ARG_VAR([VMODDIR], [vmod installation directory]) AC_ARG_WITH([vmoddir], AC_HELP_STRING([--with-vmoddir=DIR], [install modules to DIR]), [case "$withval" in /*) VMODDIR=$withval;; no) unset VMODDIR;; *) AC_MSG_ERROR([argument to --with-vmoddir must be absolute pathname]) esac],[VMODDIR=`pkg-config --variable=vmoddir varnishapi` if test -z "$VMODDIR"; then AC_MSG_FAILURE([cannot determine vmod installation directory]) fi]) if test -z "$VMODDIR"; then VMODDIR='$(libdir)/varnish/mods' fi -########### -# Check for db.h and libdb -# -AC_CHECK_HEADER([db.h],,[AC_MSG_ERROR([Required header file db.h not found])]) - -AC_CHECK_LIB(db, db_create, - [LIBS="$LIBS -ldb" - AC_RUN_IFELSE( - [AC_LANG_PROGRAM([#include "db.h"], - [int v_major, v_minor, v_patch; - db_version(&v_major, &v_minor, &v_patch); - return !(v_major == DB_VERSION_MAJOR - && v_minor == DB_VERSION_MINOR - && v_patch == DB_VERSION_PATCH); - ])], - [], - [AC_MSG_ERROR([header file db.h is not the same version as libdb])])]) - AC_CONFIG_COMMANDS([status],[ delim="-------------------------------------------------------------------" echo "" echo $delim echo "Building for Varnish version $VARNISHVERSION" echo $delim ], [VARNISHVERSION=$VARNISHVERSION]) AC_CONFIG_TESTDIR(tests) AC_CONFIG_FILES([tests/Makefile tests/atlocal]) AM_MISSING_PROG([AUTOM4TE], [autom4te]) AC_CONFIG_FILES([ Makefile src/Makefile ]) AC_OUTPUT diff --git a/src/variable.c b/src/variable.c index 6403c1a..9442b6b 100644 --- a/src/variable.c +++ b/src/variable.c @@ -479,49 +479,50 @@ vmod_type_of(VARIABLE_CTX ctx, VCL_STRING name) return typestr[var ? var->type : variable_unset]; } VCL_VOID vmod_undef(VARIABLE_CTX ctx, VCL_STRING name) { symtab_remove(get_symtab(ctx), name); } void log_error(const char *fmt, ...) { va_list ap; va_start(ap, fmt); vsyslog(LOG_DAEMON|LOG_NOTICE, fmt, ap); va_end(ap); } #define S(s) ((s) ? (s) : "NULL") struct vardef { struct vardef *next; enum variable_type type; - char name[1]; + char *name; + char *repl; }; static void vardef_free(struct vardef *v) { while (v) { struct vardef *n = v->next; free(v); v = n; } } static enum variable_type str2type(const char *s, size_t len) { int i; for (i = 0; i < sizeof(typestr)/sizeof(typestr[0]); i++) { if (strlen(typestr[i]) < len) continue; if (strncasecmp(typestr[i], s, len) == 0) return i; } return variable_unset; @@ -549,185 +550,239 @@ str2duration(const char *str) p++; } else r *= 60.; break; case 'h': r *= 60.*60.; break; case 'd': r *= 60.*60.*24.; break; case 'w': r *= 60.*60.*24.*7.; break; case 'y': r *= 60.*60.*24.*365.; break; default: return 0.0; } while (*p && isspace(*p)) p++; return *p ? 0 : r; } +char * +bref_expand(const char *str, const char *input, pcre *re, + int ovsize, int *ovector) +{ + size_t rlen = 0; + char *rbase = NULL; + char *rptr; + int nm = 2*ovsize/3; + const char *p; + + for (p = str; *p; ) { + if (*p == '\\' && strchr("123456789", p[1])) { + int n = 2*(p[1] - '0'); + if (n < nm) { + rlen += ovector[n+1] - ovector[n]; + p += 2; + continue; + } + } + ++p; + ++rlen; + } + rbase = malloc(rlen + 1); + AN(rbase); + p = str; + rptr = rbase; + while (*p) { + if (*p == '\\' && strchr("123456789", p[1])) { + int n = 2*(p[1] - '0'); + if (n < nm) { + memcpy(rptr, input + ovector[n], + ovector[n+1] - ovector[n]); + rptr += ovector[n+1] - ovector[n]; + p += 2; + continue; + } + } + + *rptr++ = *p++; + } + *rptr = 0; + + return rbase; +} + VCL_VOID vmod_batchset(VARIABLE_CTX ctx, VCL_STRING vars, VCL_STRING rxs, VCL_STRING input) { struct symtab *vt = get_symtab(ctx); struct vardef *head = NULL, *tail = NULL, *def; size_t count = 0; size_t n; const char *v = vars; const char *error_ptr; int error_offset; int cflags = 0; long lval; union value value; char *p; int ovsize; int *ovector; int i; int rc; pcre *re; if (!vars || !rxs || !input) { log_error("variable.batchset: bad arguments: vars=%s, rxs=%s, input=%s", S(vars), S(rxs), S(input)); return; } while (*v) { - size_t n = strcspn(v, "="); + char const *nameptr = v; + size_t n = strcspn(v, ":=,"); + size_t namelen; + char const *replptr; + size_t repllen; + char rbuf[3]; + enum variable_type type; + int delim; + + namelen = n; - if (!v[n]) { - log_error("variable.batchset: vars argument invalid near %s", - v); - vardef_free(head); - return; + v += n; + delim = *v ? *v++ : 0; + + if (delim == ':') { + n = strcspn(v, "=,"); + type = str2type(v, n); + v += n; + delim = *v ? *v++ : 0; + } else + type = variable_string; + + if (delim == '=') { + n = strcspn(v, ","); + replptr = v; + repllen = n; + v += n; + delim = *v ? *v++ : 0; + } else { + rbuf[0] = '\\'; + rbuf[1] = count + '1'; + rbuf[2] = 0; + replptr = rbuf; + repllen = 2; } - def = malloc(sizeof(def[0]) + n); + + def = malloc(sizeof(def[0]) + namelen + repllen + 2); def->next = NULL; - memcpy(def->name, v, n); - def->name[n] = 0; if (tail) tail->next = def; else head = def; tail = def; ++count; - - v += n + 1; - if (!*v) { - log_error("variable.batchset: no type for %s", def->name); - vardef_free(head); - return; - } - - n = strcspn(v, ","); - def->type = str2type(v, n); - if (def->type == variable_unset) { - log_error("variable.batchset: invalid type for %s", - def->name); - vardef_free(head); - return; - } - - v += n; - if (*v) - ++v; + def->type = type; + def->name = (char*)(def + 1); + memcpy(def->name, nameptr, namelen); + def->name[namelen] = 0; + def->repl = def->name + namelen + 1; + memcpy(def->repl, replptr, repllen); + def->repl[repllen] = 0; } re = pcre_compile(rxs, cflags, &error_ptr, &error_offset, NULL); if (!re) { log_error("variable.batchset: %s: compilation failed near %s: %s", - rxs, rxs + error_offset, error_ptr); + rxs, rxs + error_offset, error_ptr); vardef_free(head); return; } rc = pcre_fullinfo(re, NULL, PCRE_INFO_CAPTURECOUNT, &n); if (rc) { log_error("pcre_fullinfo() failed: %d", rc); vardef_free(head); return; } if (n < count) { log_error("variable.batchset: %s: too few subexpressions to satisfy %s", rxs, vars); vardef_free(head); return; } ovsize = (count + 1) * 3; ovector = calloc(ovsize, sizeof(*ovector)); rc = pcre_exec(re, 0, input, strlen(input), 0, 0, ovector, ovsize); if (rc <= 0) { if (rc == 0) log_error("matched, but too many substrings"); else /*FIXME*/; vardef_free(head); return; } - for (def = head, i = 1; def; def = def->next, i++) { - const char *s; - - rc = pcre_get_substring(input, ovector, ovsize, i, &s); - if (rc < 0) { - log_error("variable.batchset: %s(%s) can't get subexpr #%d: %d", - rxs, input, i, rc); - vardef_free(head); - return; - } + for (def = head; def; def = def->next, i++) { + char *s = bref_expand(def->repl, input, re, ovsize, ovector); switch (def->type) { case variable_string: value.s = (char*)s; break; case variable_int: errno = 0; lval = strtol(s, &p, 10); if (*p) { log_error("variable.batchset: %s(%s)#%d: not an integer", rxs, input, i); value.i = 0; } else if (errno) { log_error("variable.batchset: %s(%s)#%d: %s", rxs, input, i, strerror(errno)); value.i = 0; } else if (lval < INT_MIN || lval > INT_MAX) { log_error("variable.batchset: %s(%s)#%d: value out of range", rxs, input, i); value.i = 0; } else value.i = lval; + free(s); break; case variable_real: errno = 0; value.r = strtod(s, &p); if (*p) { log_error("variable.batchset: %s(%s)#%d: not a valid number", rxs, input, i); value.r = 0; } else if (errno) { log_error("variable.batchset: %s(%s)#%d: %s", rxs, input, i, strerror(errno)); value.r = 0; } + free(s); break; case variable_duration: value.d = str2duration(s); - break; + free(s); + break; default: abort(); } defvar(vt, def->name, def->type, &value); } pcre_free(re); free(ovector); vardef_free(head); } + diff --git a/tests/batchset.at b/tests/batchset.at index be07cae..506fe78 100644 --- a/tests/batchset.at +++ b/tests/batchset.at @@ -1,37 +1,39 @@ # This file is part of vmod-variable -*- autotest -*- # Copyright (C) 2015 Sergey Poznyakoff # # Vmod-variable 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; either version 3, or (at your option) # any later version. # # Vmod-variable is distributed in the hope that it will be useful, # but WITHOUT ANY WARRANTY; without even the implied warranty of # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the # GNU General Public License for more details. # # You should have received a copy of the GNU General Public License # along with vmod-variable. If not, see <http://www.gnu.org/licenses/>. AT_SETUP(batchset) AT_KEYWORDS(batchset) AT_VARNISHTEST([ sub vcl_recv { - variable.batchset("y=i,x=s", "^/(\d+)/(.+)", req.url); + variable.batchset("y:i,x,time:d=\3s", "^/(\d+)/(.+)/(\d+)", req.url); } sub vcl_deliver { set resp.http.X-X = variable.get("x"); set resp.http.X-Y = variable.get_int("y"); + set resp.http.X-Time = variable.get_duration("time"); } ],[ -txreq -url /10/test +txreq -url /10/test/60 rxresp expect resp.http.X-X == "test" expect resp.http.X-Y == "10" +expect resp.http.X-Time == "60.000" ]) AT_CLEANUP
\ No newline at end of file |