diff options
author | Sergey Poznyakoff <gray@gnu.org.ua> | 2015-02-13 12:07:09 +0200 |
---|---|---|
committer | Sergey Poznyakoff <gray@gnu.org.ua> | 2015-02-13 12:12:30 +0200 |
commit | e6754a1b721358b5588f67bdb7eef6ab662c0ee2 (patch) | |
tree | 804d7d5fccd71ab7bef6ecdd0b8afd46519e4b41 | |
parent | f06fcc848fa5a5b15d170bf5cc34986ed6164621 (diff) | |
download | vmod-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.c | 278 | ||||
-rw-r--r-- | src/variable.vcc | 2 | ||||
-rw-r--r-- | tests/Makefile.am | 2 | ||||
-rw-r--r-- | tests/queryset00.at | 39 | ||||
-rw-r--r-- | tests/queryset01.at | 39 | ||||
-rw-r--r-- | tests/testsuite.at | 5 |
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 |