aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorSergey Poznyakoff <gray@gnu.org.ua>2015-02-13 12:07:09 +0200
committerSergey Poznyakoff <gray@gnu.org.ua>2015-02-13 12:12:30 +0200
commite6754a1b721358b5588f67bdb7eef6ab662c0ee2 (patch)
tree804d7d5fccd71ab7bef6ecdd0b8afd46519e4b41
parentf06fcc848fa5a5b15d170bf5cc34986ed6164621 (diff)
downloadvmod-variable-e6754a1b721358b5588f67bdb7eef6ab662c0ee2.tar.gz
vmod-variable-e6754a1b721358b5588f67bdb7eef6ab662c0ee2.tar.bz2
Implement queryset function
* src/variable.c (vardef_new,vardef_find,setval): New functions. (vmod_regset): Use these to handle vardefs. (hex2ul,xdecode,define_param): New functions. (vmod_queryset): New API call. * src/variable.vcc: New proto. * tests/Makefile.am (TESTSUITE_AT): Add new testcases. * tests/testsuite.at: Likewise. * tests/queryset00.at: New file. * tests/queryset01.at: New file.
-rw-r--r--src/variable.c278
-rw-r--r--src/variable.vcc2
-rw-r--r--tests/Makefile.am2
-rw-r--r--tests/queryset00.at39
-rw-r--r--tests/queryset01.at39
-rw-r--r--tests/testsuite.at5
6 files changed, 305 insertions, 60 deletions
diff --git a/src/variable.c b/src/variable.c
index 667cfc0..5deda1b 100644
--- a/src/variable.c
+++ b/src/variable.c
@@ -504,6 +504,27 @@ struct vardef {
char *repl;
};
+
+static struct vardef *
+vardef_new(enum variable_type type,
+ char const *nameptr, size_t namelen,
+ char const *replptr, size_t repllen)
+{
+ struct vardef *def = malloc(sizeof(def[0]) + namelen + repllen + 2);
+ def->next = NULL;
+ def->type = type;
+ def->name = (char*)(def + 1);
+ memcpy(def->name, nameptr, namelen);
+ def->name[namelen] = 0;
+ if (replptr) {
+ def->repl = def->name + namelen + 1;
+ memcpy(def->repl, replptr, repllen);
+ def->repl[repllen] = 0;
+ } else
+ def->repl = NULL;
+ return def;
+}
+
static void
vardef_free(struct vardef *v)
{
@@ -514,6 +535,15 @@ vardef_free(struct vardef *v)
}
}
+static struct vardef *
+vardef_find(struct vardef *v, const char *name, size_t len)
+{
+ for (; v; v = v->next)
+ if (strlen(v->name) == len && memcmp(v->name, name, len) == 0)
+ break;
+ return v;
+}
+
static enum variable_type
str2type(const char *s, size_t len)
{
@@ -616,8 +646,56 @@ bref_expand(const char *str, const char *input, pcre *re,
return rbase;
}
+static void
+setval(union value *val, const char *s, enum variable_type type, char **err)
+{
+ char *p;
+ long lval;
+
+ *err = NULL;
+ switch (type) {
+ case variable_string:
+ val->s = strdup(s);
+ AN(val->s);
+ break;
+ case variable_int:
+ errno = 0;
+ lval = strtol(s, &p, 10);
+ if (*p) {
+ *err = "not an integer";
+ val->i = 0;
+ } else if (errno) {
+ *err = strerror(errno);
+ val->i = 0;
+ } else if (lval < INT_MIN || lval > INT_MAX) {
+ *err = "value out of range";
+ val->i = 0;
+ } else
+ val->i = lval;
+ break;
+
+ case variable_real:
+ errno = 0;
+ val->r = strtod(s, &p);
+ if (*p) {
+ *err = "not a valid number";
+ val->r = 0;
+ } else if (errno) {
+ *err = strerror(errno);
+ val->r = 0;
+ }
+ break;
+ case variable_duration:
+ val->d = str2duration(s);
+ break;
+ default:
+ abort();
+ }
+}
+
VCL_VOID
-vmod_regset(VARIABLE_CTX ctx, VCL_STRING vars, VCL_STRING rxs, VCL_STRING input)
+vmod_regset(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;
@@ -627,7 +705,6 @@ vmod_regset(VARIABLE_CTX ctx, VCL_STRING vars, VCL_STRING rxs, VCL_STRING input)
const char *error_ptr;
int error_offset;
int cflags = 0;
- long lval;
union value value;
char *p;
int ovsize;
@@ -645,15 +722,13 @@ vmod_regset(VARIABLE_CTX ctx, VCL_STRING vars, VCL_STRING rxs, VCL_STRING input)
while (*v) {
char const *nameptr = v;
size_t n = strcspn(v, ":=,");
- size_t namelen;
+ size_t namelen = n;
char const *replptr;
size_t repllen;
char rbuf[3];
enum variable_type type;
int delim;
- namelen = n;
-
v += n;
delim = *v ? *v++ : 0;
@@ -679,21 +754,13 @@ vmod_regset(VARIABLE_CTX ctx, VCL_STRING vars, VCL_STRING rxs, VCL_STRING input)
repllen = 2;
}
- def = malloc(sizeof(def[0]) + namelen + repllen + 2);
- def->next = NULL;
+ def = vardef_new(type, nameptr, namelen, replptr, repllen);
if (tail)
tail->next = def;
else
head = def;
tail = def;
++count;
- 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);
@@ -731,51 +798,13 @@ vmod_regset(VARIABLE_CTX ctx, VCL_STRING vars, VCL_STRING rxs, VCL_STRING input)
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.regset: %s(%s)#%d: not an integer", rxs, input, i);
- value.i = 0;
- } else if (errno) {
- log_error("variable.regset: %s(%s)#%d: %s",
- rxs, input, i, strerror(errno));
- value.i = 0;
- } else if (lval < INT_MIN || lval > INT_MAX) {
- log_error("variable.regset: %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.regset: %s(%s)#%d: not a valid number", rxs, input, i);
- value.r = 0;
- } else if (errno) {
- log_error("variable.regset: %s(%s)#%d: %s",
- rxs, input, i, strerror(errno));
- value.r = 0;
- }
- free(s);
- break;
-
- case variable_duration:
- value.d = str2duration(s);
- free(s);
- break;
- default:
- abort();
- }
+ char *err;
+ setval(&value, s, def->type, &err);
+ if (err)
+ log_error("variable.regset: %s(%s)#%d: %s",
+ rxs, input, i, err);
defvar(vt, def->name, def->type, &value);
+ free(s);
}
pcre_free(re);
@@ -783,3 +812,136 @@ vmod_regset(VARIABLE_CTX ctx, VCL_STRING vars, VCL_STRING rxs, VCL_STRING input)
vardef_free(head);
}
+static unsigned long
+hex2ul(char hex)
+{
+ if (hex >= '0' && hex <= '9')
+ return hex - '0';
+
+ if (hex >= 'a' && hex <= 'z')
+ return hex - 'a' + 10;
+
+ if (hex >= 'A' && hex <= 'Z')
+ return hex - 'A' + 10;
+
+ return -1;
+}
+
+/* From RFC 1738, section 2.2 */
+static void
+xdecode(char *s)
+{
+ char *d;
+
+ d = strchr(s, '%');
+ if (!d)
+ return;
+
+ for (s = d; *s; ) {
+ if (*s == '%' && hex2ul(s[1]) != -1 && hex2ul(s[2]) != -1) {
+ *d++ = hex2ul(s[1]) << 4 + hex2ul(s[2]);
+ s += 3;
+ } else
+ *d++ = *s++;
+ }
+
+ *d = 0;
+}
+
+static void
+define_param(struct symtab *vt, struct vardef *def,
+ char const *valptr, size_t vallen)
+{
+ char *s;
+ char *err;
+ union value value;
+
+ s = malloc(vallen+1);
+ AN(s);
+ memcpy(s, valptr, vallen);
+ s[vallen] = 0;
+ xdecode(s);
+
+ setval(&value, s, def->type, &err);
+ if (err)
+ log_error("variable.queryset: %s", err);
+ defvar(vt, def->name, def->type, &value);
+ free(s);
+}
+
+VCL_VOID
+vmod_queryset(VARIABLE_CTX ctx, VCL_STRING vars, VCL_STRING query)
+{
+ struct symtab *vt = get_symtab(ctx);
+ struct vardef *head = NULL, *tail = NULL, *def;
+ size_t count = 0;
+ size_t n;
+ const char *v = vars;
+
+ while (*v) {
+ char const *nameptr = v;
+ size_t n = strcspn(v, ":,");
+ size_t namelen = n;
+ enum variable_type type;
+ int delim;
+
+ 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;
+
+ def = vardef_new(type, nameptr, namelen, NULL, 0);
+ if (tail)
+ tail->next = def;
+ else
+ head = def;
+ tail = def;
+ ++count;
+ }
+
+ if (!query || !*query) {
+ for (def = head; def; def = def->next)
+ symtab_remove(vt, def->name);
+ vardef_free(head);
+ return;
+ }
+
+ v = query;
+ while (*v) {
+ char const *paramptr = v;
+ size_t n = strcspn(v, "=&");
+ size_t paramlen = n;
+ char const *valptr = NULL;
+ size_t vallen = 0;
+ int delim;
+
+ v += n;
+ delim = *v ? *v++ : 0;
+
+ if (delim == '=') {
+ n = strcspn(v, "&");
+ valptr = v;
+ vallen = n;
+ v += n;
+ delim = *v ? *v++ : 0;
+ }
+
+ if (head) {
+ def = vardef_find(head, paramptr, paramlen);
+ if (def)
+ define_param(vt, def, valptr, vallen);
+ } else {
+ def = vardef_new(variable_string, paramptr, paramlen,
+ NULL, 0);
+ define_param(vt, def, valptr, vallen);
+ free(def);
+ }
+ }
+ vardef_free(head);
+}
diff --git a/src/variable.vcc b/src/variable.vcc
index 7fca518..f0106e9 100644
--- a/src/variable.vcc
+++ b/src/variable.vcc
@@ -34,4 +34,4 @@ $Function INT defined(STRING)
$Function STRING type_of(STRING)
$Function VOID undef(STRING)
$Function VOID regset(STRING, STRING, STRING)
-
+$Function VOID queryset(STRING, STRING)
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 99769ae..4c17467 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -47,6 +47,8 @@ TESTSUITE_AT = \
duration.at\
gstring.at\
int.at\
+ queryset00.at\
+ queryset01.at\
real.at\
regset.at\
string.at\
diff --git a/tests/queryset00.at b/tests/queryset00.at
new file mode 100644
index 0000000..f56e19a
--- /dev/null
+++ b/tests/queryset00.at
@@ -0,0 +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(all variables)
+AT_KEYWORDS(queryset queryset00)
+
+AT_VARNISHTEST([
+sub vcl_recv {
+ variable.queryset("", regsub(req.url, ".*\?(.+)", "\1"));
+}
+sub vcl_deliver {
+ set resp.http.X-X = variable.get("x");
+ set resp.http.X-Y = variable.get("y");
+ set resp.http.X-Foo = variable.get("foo");
+}
+],[
+txreq -url /?x=4&y=bar&foo=quz%40example.com
+rxresp
+expect resp.http.X-X == "4"
+expect resp.http.X-Y == "bar"
+expect resp.http.X-Foo == "quz@example.com"
+])
+AT_CLEANUP
+
+
+ \ No newline at end of file
diff --git a/tests/queryset01.at b/tests/queryset01.at
new file mode 100644
index 0000000..12939c0
--- /dev/null
+++ b/tests/queryset01.at
@@ -0,0 +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(selected variables)
+AT_KEYWORDS(queryset queryset01)
+
+AT_VARNISHTEST([
+sub vcl_recv {
+ variable.queryset("x:i,foo", regsub(req.url, ".*\?(.+)", "\1"));
+}
+sub vcl_deliver {
+ set resp.http.X-X = variable.get_int("x");
+ set resp.http.X-Y = variable.defined("y");
+ set resp.http.X-Foo = variable.get("foo");
+}
+],[
+txreq -url /?x=4&y=test&foo=quz
+rxresp
+expect resp.http.X-X == "4"
+expect resp.http.X-Y == "0"
+expect resp.http.X-Foo == "quz"
+])
+AT_CLEANUP
+
+
+ \ No newline at end of file
diff --git a/tests/testsuite.at b/tests/testsuite.at
index d0cc846..077db01 100644
--- a/tests/testsuite.at
+++ b/tests/testsuite.at
@@ -54,7 +54,10 @@ m4_include([clear.at])
m4_include([defined.at])
m4_include([type_of.at])
m4_include([undef.at])
-m4_include([regset.at])
m4_include([gstring.at])
+m4_include([regset.at])
+AT_BANNER([queryset])
+m4_include([queryset00.at])
+m4_include([queryset01.at])
# End of testsuite.at

Return to:

Send suggestions and report system problems to the System administrator.