aboutsummaryrefslogtreecommitdiff
path: root/src
diff options
context:
space:
mode:
authorSergey Poznyakoff <gray@gnu.org.ua>2013-07-12 18:05:47 +0300
committerSergey Poznyakoff <gray@gnu.org.ua>2013-07-12 18:05:47 +0300
commitbeec6178d1294a3b339098fe17ba23ef67a4bada (patch)
treed8b2074bd316cce6a163065b09be7ab37f4af2fd /src
parent8afb551c895d6870b0d4f427fa8205ae45ad0bf9 (diff)
downloadvmod-dbrw-beec6178d1294a3b339098fe17ba23ef67a4bada.tar.gz
vmod-dbrw-beec6178d1294a3b339098fe17ba23ef67a4bada.tar.bz2
Optional flags modify the regexp handling, return status and query disposition.
The flags can be supplied globally, using the "flags=X" parameter to drw.config, or for each record individually, by returning X in the fourth column of a tuple. In both cases X is a comma-separated list of: NC or nocase - force case-insensitive regexps case - force case-sensitive regexps (the default) QSA or qsappend - treat destination field as URL; append any query string from the original request URL to it QSD or qsdiscard - treat destination field as URL; discard any query string attached to the incoming URI R=NNN or redirect=NNN - on success, set the X-VMOD-DBRW-Status header to NNN, which must be a valid HTTP status code. * src/dbrw.h [HTTP_STATUS_LEN]: New define. <dbrw_config> (qdisp, regflags, status): New members. * src/vmod_dbrw.c (QDISP_NONE, QDISP_APPEND) (QDISP_APPEND): New constants. (dbrw_init): Fill allocated memory with zeroes. (is_http_status,parse_flags): New static functions. (vmod_config): Re-initialize conf on failure. Initialize new members of struct dbrw_config. (expand_backref): Additional argument 'qry' supplies the query part to be appended to the resulting string. (findmatch): Accept four fields per tuple, the optional fourth one supplying flags.
Diffstat (limited to 'src')
-rw-r--r--src/dbrw.h5
-rw-r--r--src/vmod_dbrw.c155
2 files changed, 152 insertions, 8 deletions
diff --git a/src/dbrw.h b/src/dbrw.h
index beebc5e..0721247 100644
--- a/src/dbrw.h
+++ b/src/dbrw.h
@@ -30,11 +30,16 @@ enum {
state_disabled
};
+#define HTTP_STATUS_LEN 3
+
struct dbrw_config {
int debug_level;
struct dbrw_backend *backend;
char **param;
char *query;
+ int qdisp;
+ int regflags;
+ char status[HTTP_STATUS_LEN+1];
};
struct dbrw_connection {
diff --git a/src/vmod_dbrw.c b/src/vmod_dbrw.c
index 16bbb57..efe31ce 100644
--- a/src/vmod_dbrw.c
+++ b/src/vmod_dbrw.c
@@ -9,6 +9,12 @@
static pthread_once_t thread_once = PTHREAD_ONCE_INIT;
static pthread_key_t thread_key;
+enum {
+ QDISP_NONE,
+ QDISP_APPEND,
+ QDISP_DISCARD
+};
+
void
dbrw_debug(const char *fmt, ...)
{
@@ -44,7 +50,7 @@ make_key()
int
dbrw_init(struct vmod_priv *priv, const struct VCL_conf *vclconf)
{
- struct dbrw_config *conf = malloc(sizeof(*conf));
+ struct dbrw_config *conf = calloc(1, sizeof(*conf));
if (!conf) {
dbrw_error("not enough memory");
abort();
@@ -101,6 +107,72 @@ find_backend(const char *name)
return NULL;
}
+static int
+is_http_status(const char *arg)
+{
+ if (strlen(arg) != HTTP_STATUS_LEN)
+ return 0;
+ return '1' <= arg[0] && arg[0] <= '5' &&
+ '0' <= arg[1] && arg[1] <= '9' &&
+ '0' <= arg[2] && arg[2] <= '9';
+}
+
+static int
+parse_flags(const char *arg, int *qdisp, int *flags, char status[])
+{
+ struct wordsplit ws;
+ int rc = 0;
+ int i;
+
+ if (!arg)
+ return 0;
+ ws.ws_delim = ",";
+ if (wordsplit(arg, &ws,
+ WRDSF_NOVAR | WRDSF_NOCMD | WRDSF_QUOTE |
+ WRDSF_CESCAPES |
+ WRDSF_DELIM)) {
+ dbrw_error("cannot split string `%s': %s",
+ arg, wordsplit_strerror (&ws));
+ return 1;
+ }
+
+ for (i = 0; i < ws.ws_wordc; i++) {
+ if (strcmp(ws.ws_wordv[i], "nocase") == 0 ||
+ strcmp(ws.ws_wordv[i], "NC") == 0)
+ *flags |= REG_ICASE;
+ else if (strcmp(ws.ws_wordv[i], "case") == 0)
+ *flags &= ~REG_ICASE;
+ else if (strcmp(ws.ws_wordv[i], "qsappend") == 0 ||
+ strcmp(ws.ws_wordv[i], "QSA") == 0)
+ *qdisp = QDISP_APPEND;
+ else if (strcmp(ws.ws_wordv[i], "qsdiscard") == 0 ||
+ strcmp(ws.ws_wordv[i], "QSD") == 0)
+ *qdisp = QDISP_DISCARD;
+ else if (strncmp(ws.ws_wordv[i], "redirect=", 9) == 0) {
+ if (!is_http_status(ws.ws_wordv[i] + 9)) {
+ dbrw_error("invalid status code: %s",
+ ws.ws_wordv[i] + 9);
+ rc = 1;
+ } else
+ strncpy(status, ws.ws_wordv[i] + 9,
+ HTTP_STATUS_LEN);
+ } else if (strncmp(ws.ws_wordv[i], "R=", 2) == 0) {
+ if (!is_http_status(ws.ws_wordv[i] + 2)) {
+ dbrw_error("invalid status code: %s",
+ ws.ws_wordv[i] + 2);
+ rc = 1;
+ } else
+ strncpy(status, ws.ws_wordv[i] + 2,
+ HTTP_STATUS_LEN);
+ } else {
+ dbrw_error("unrecognized flag: %s", ws.ws_wordv[i]);
+ rc = 1;
+ }
+ }
+
+ wordsplit_free(&ws);
+ return rc;
+}
/* Configure the module.
@@ -152,17 +224,30 @@ vmod_config(struct sess *sp, struct vmod_priv *priv, const char *bkname,
dbrw_error("unsupported backend: %s", bkname);
argv_free(conf->param);
free(conf->query);
- param = NULL;
+ conf->query = NULL;
+ conf->param = NULL;
return;
}
s = findparam(conf->param, "debug");
conf->debug_level = atoi(s);
+
+ conf->qdisp = QDISP_NONE;
+ conf->regflags = REG_EXTENDED;
+ conf->status[0] = 0;
+
+ s = findparam(conf->param, "flags");
+ if (s && parse_flags(s, &conf->qdisp, &conf->regflags, &conf->status)) {
+ argv_free(conf->param);
+ free(conf->query);
+ conf->query = NULL;
+ conf->param = NULL;
+ }
}
static char *
expand_backref(struct sess *sp, const char *str, const char *val,
- size_t matchcount, regmatch_t *matches)
+ size_t matchcount, regmatch_t *matches, char *qry)
{
unsigned u;
char *b, *e, *p;
@@ -214,9 +299,24 @@ expand_backref(struct sess *sp, const char *str, const char *val,
WS_Release(sp->wrk->ws, 0);
return NULL;
}
- *p++ = 0;
+
+ *p = 0;
u--;
+ if (qry) {
+ size_t len = strlen(qry) + 1;
+ if (len >= u) {
+ WS_Release(sp->wrk->ws, 0);
+ return NULL;
+ }
+ if (strchr(b, '?'))
+ *p++ = '&';
+ else
+ *p++ = '?';
+ memcpy(p, qry, len);
+ u -= len;
+ }
+
if (u)
WS_Release(sp->wrk->ws, u);
@@ -248,10 +348,11 @@ findmatch(struct sess *sp, struct dbrw_connection *conn, char **param)
if (nf < 3)
return WS_Dup(sp->ws, sql_get_column(conn, 0, 0));
- /* Three fields:
+ /* Three or four fields:
result
pattern
value
+ [flags]
*/
ws.ws_env = (const char **)param;
res = NULL;
@@ -261,6 +362,10 @@ findmatch(struct sess *sp, struct dbrw_connection *conn, char **param)
regex_t re;
int rc;
size_t matchcount; /* Number of used entries in matches */
+ int rflags = conn->conf->regflags;
+ char status[HTTP_STATUS_LEN+1];
+ int qdisp = conn->conf->qdisp;
+ char *qry = NULL;
if (ISEMPTY(pat)) {
res = WS_Dup(sp->ws, sql_get_column(conn, i, 0));
@@ -279,7 +384,32 @@ findmatch(struct sess *sp, struct dbrw_connection *conn, char **param)
debug(conn->conf, 1, ("expanded to \"%s\" ~ \"%s\"",
pat, val));
- rc = regcomp(&re, pat, REG_EXTENDED);
+ strcpy(status, conn->conf->status);
+ if (nf == 4) {
+ if (parse_flags(sql_get_column(conn, i, 3),
+ &qdisp, &rflags, &status))
+ continue;
+ }
+
+ switch (qdisp) {
+ case QDISP_NONE:
+ break;
+ case QDISP_APPEND:
+ qry = strchr(val, '?');
+ if (qry)
+ *qry++ = 0;
+ break;
+ case QDISP_DISCARD:
+ qry = strchr(val, '?');
+ if (qry) {
+ debug(conn->conf, 1,
+ ("discarding query part \"%s\"", qry));
+ *qry = 0;
+ qry = NULL;
+ }
+ }
+
+ rc = regcomp(&re, pat, rflags);
if (rc) {
char errbuf[512];
@@ -307,10 +437,19 @@ findmatch(struct sess *sp, struct dbrw_connection *conn, char **param)
if (rc == 0)
res = expand_backref(sp, val,
sql_get_column(conn, i, 0),
- matchcount, matches);
+ matchcount, matches, qry);
+
regfree(&re);
if (rc == 0) {
debug(conn->conf, 1, ("match"));
+ if (status[0]) {
+ debug(conn->conf, 1,
+ ("setting status %s", status));
+ VRT_SetHdr(sp, HDR_REQ,
+ "\023X-VMOD-DBRW-Status:",
+ status,
+ vrt_magic_string_end);
+ }
break;
}
}
@@ -416,7 +555,7 @@ vmod_rewrite(struct sess *sp, struct vmod_priv *priv, const char *arg)
sql_free_result(cp);
- debug(conf, 1, ("vmod_rewrite: res=%s", res ? res : NULL));
+ debug(conf, 1, ("vmod_rewrite: res=%s", res ? res : "(NULL)"));
return res;
}

Return to:

Send suggestions and report system problems to the System administrator.