diff options
author | Sergey Poznyakoff <gray@gnu.org.ua> | 2013-07-12 18:05:47 +0300 |
---|---|---|
committer | Sergey Poznyakoff <gray@gnu.org.ua> | 2013-07-12 18:05:47 +0300 |
commit | beec6178d1294a3b339098fe17ba23ef67a4bada (patch) | |
tree | d8b2074bd316cce6a163065b09be7ab37f4af2fd /src | |
parent | 8afb551c895d6870b0d4f427fa8205ae45ad0bf9 (diff) | |
download | vmod-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.h | 5 | ||||
-rw-r--r-- | src/vmod_dbrw.c | 155 |
2 files changed, 152 insertions, 8 deletions
@@ -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; } |