aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--NEWS19
-rw-r--r--configure.ac4
-rw-r--r--doc/vmod-dbrw.324
-rw-r--r--doc/vmod-dbrw.texi38
-rw-r--r--src/dbrw.h1
-rw-r--r--src/vmod_dbrw.c110
-rw-r--r--tests/Makefile.am1
-rw-r--r--tests/atlocal.in9
-rw-r--r--tests/initdb.at4
-rw-r--r--tests/rewrite07.at40
-rw-r--r--tests/testsuite.at1
11 files changed, 198 insertions, 53 deletions
diff --git a/NEWS b/NEWS
index 0300ec3..516ca69 100644
--- a/NEWS
+++ b/NEWS
@@ -1,17 +1,32 @@
-vmod-dbrw -- history of user-visible changes. 2018-12-10
+vmod-dbrw -- history of user-visible changes. 2019-02-13
See the end of file for copying conditions.
Please send vmod-dbrw bug reports to <gray@gnu.org>
+Version 2.4.90 (git)
+
+* req.http.X-VMOD-DBRW-Error
+
+This header is set to 1 by dbrw.rewrite to indicate that an error
+occurred during the rewrite.
+
+* New flags: regex and eq
+
+One of this flags can appear in the fourth column of the returned data
+set. The 'eq' flag instructs dbrw.rewrite to use exact string match,
+instead of regular expressions. The 'regex' flag instructs it to use
+regular expression matching. It is the default.
+
+
Version 2.4, 2018-12-10
* Support for Varnish version 6.0.2
Version 2.3, 2018-12-08
* SQL idle timeout
For MySQL backend, the default connection idle timeout is set equal to
the value of the MySQL variable 'wait_timeout'. For Postgres, default
idle timeout is not yet implemented.
@@ -69,25 +84,25 @@ Varnish header files and libraries.
Version 2.0, 2014-11-13
Support for Varnish 4.0.
Version 1.0, 2013-07-20
Initial release
=========================================================================
Copyright information:
-Copyright (C) 2013-2018 Sergey Poznyakoff
+Copyright (C) 2013-2019 Sergey Poznyakoff
Permission is granted to anyone to make or distribute verbatim copies
of this document as received, in any medium, provided that the
copyright notice and this permission notice are preserved,
thus giving the recipient permission to redistribute in turn.
Permission is granted to distribute modified versions
of this document, or of portions of it,
under the above conditions, provided also that they
carry prominent notices stating who last changed them.
Local variables:
diff --git a/configure.ac b/configure.ac
index 62c3afe..fd618d3 100644
--- a/configure.ac
+++ b/configure.ac
@@ -1,29 +1,29 @@
# This file is part of vmod-dbrw -*- autoconf -*-
-# Copyright (C) 2013-2018 Sergey Poznyakoff
+# Copyright (C) 2013-2019 Sergey Poznyakoff
#
# Vmod-dbrw 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-dbrw 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-dbrw. If not, see <http://www.gnu.org/licenses/>.
AC_PREREQ(2.69)
-AC_INIT([vmod-dbrw], 2.4, [gray@gnu.org])
+AC_INIT([vmod-dbrw], 2.4.90, [gray@gnu.org])
AC_CONFIG_AUX_DIR([build-aux])
AC_CONFIG_MACRO_DIR([m4])
AC_CONFIG_SRCDIR(src/vmod_dbrw.vcc)
AM_CONFIG_HEADER(config.h)
AC_SUBST([AC_VMOD_BASENAME],[dbrw])
AC_CANONICAL_SYSTEM
AC_LANG(C)
AM_INIT_AUTOMAKE([gnu tar-ustar silent-rules])
AM_SILENT_RULES([yes])
diff --git a/doc/vmod-dbrw.3 b/doc/vmod-dbrw.3
index 2abef65..9bec008 100644
--- a/doc/vmod-dbrw.3
+++ b/doc/vmod-dbrw.3
@@ -4,25 +4,25 @@
.\" Vmod-dbrw 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-dbrw 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-dbrw. If not, see <http://www.gnu.org/licenses/>.
-.TH VMOD-DBRW 3 "December 8, 2018" "VMOD-DBRW" "User Reference"
+.TH VMOD-DBRW 3 "February 13, 2019" "VMOD-DBRW" "User Reference"
.SH NAME
vmod-dbrw \- Database-driven rewrite rules for Varnish Cache
.SH SYNOPSIS
.B import dbrw;
.PP
.BI "VOID dbrw.config(STRING " dbtype ", STRING " params ", STRING " query ");"
.PP
.BI "STRING dbrw.rewrite(STRING " args ");"
.SH DESCRIPTION
.B Vmod-dbrw
is a Varnish Cache module implementing database-driven rewrite procedures.
Its intended use is for web sites that need an exceedingly big number
@@ -181,41 +181,61 @@ of \fB$\fIDIGIT\fR (where \fIDIGIT\fR stands for a decimal digit from
compatibility with the traditional usage, the \fB\\\fIDIGIT\fR
notation is also allowed. The resulting value is returned.
.PP
Optional \fBflags\fR column is a comma-separated list of flags that
modify regular expression handling. The following flags are defined:
.TP
.BR NC " or " nocase
Treat \fBregexp\fR as case-insensitive.
.TP
.B case
Treat \fBregexp\fR as case-sensitive (default).
.TP
+.B eq
+Use exact string matching.
+.TP
.BR QSA " or " qsappend
Treat the resulting value as URL; append any query string from the
original \fBvalue\fR to it.
.TP
.BR QSD " or " qsdiscard
Treat the resulting value as URL; discard any query string attached to
the original \fBvalue\fR.
.TP
\fBredirect=\fICODE\fR or \fBR=\fICODE\fR
On success, set the \fBX\-VMOD\-DBRW\-Status\fR header to \fICODE\fR,
-which must be a valid HTTP status code.
+which must be a valid HTTP status code.
+.TP
+.B regex
+Use regular expression matching. This is the default. This flag is
+provided for completeness sake, as a counterpart of
+.BR eq .
.PP
If \fBregexp\fR or \fBvalue\fR is NULL, the tuple is handled as
described in
.BR "Strict matches" .
.PP
If \fBflags\fR is NULL, it is ignored.
+.SH HTTP HEADERS
+Upon return,
+.B dbrw.return
+may set one of the following headers in
+.BR resp.http :
+.TP
+.B X\-VMOD\-DBRW\-Status
+If the \fBredirect\fR flag was used, this header contains the HTTP
+response code to be used instead of the default.
+.TP
+.B X\-VMOD\-DBRW\-Error
+This header is set to \fB1\fR if an error occurred during the rewrite.
.SH EXAMPLES
The examples in this section assume \fBMySQL\fR databases. Any
details not related to \fBvmod-dbrw\fR are omitted.
.SS Redirects
.PP
This example shows how to implement apache-style permanent redirects
in Varnish.
.PP
The following table is used to keep the redirection data:
.PP
.EX
CREATE TABLE redirects (
diff --git a/doc/vmod-dbrw.texi b/doc/vmod-dbrw.texi
index 734dadb..6a987ed 100644
--- a/doc/vmod-dbrw.texi
+++ b/doc/vmod-dbrw.texi
@@ -123,25 +123,32 @@ assignment having the form @code{@var{name}=@var{value}}. When called,
@code{rewrite} expands the SQL query registered with the prior call to
@code{config} by replacing each @code{$@var{name}}
construct (a @dfn{variable reference}) with the corresponding
@var{value} from its argument. Similarly to the shell syntax, the
variable reference can also be written as @code{$@{@var{name}@}}.
This latter form can be used in contexts where the variable reference is
immediately followed by a letter, digit or underscore, to prevent it
from being counted as a part of the name. Special syntax is available
for substituting default values and invoking built-in functions during
the expansion of the query. @xref{Expansions}, for a detailed
description of these.
+@anchor{X-VMOD-DBRW-Error}
+@vindex X-VMOD-DBRW-Error
Having undergone expansions, the query is sent to the database server.
+If the query returns no records or if an error occured, @code{rewrite}
+returns empty string. In case of error, it also sets the HTTP header
+@samp{X-VMOD-DBRW-Error: 1}. It can be used in VLC code to provide a
+special handling for such failures.
+
The returned set of records (if non-empty) is processed depending on the
number of fields it contains.
@cindex result interpretation
@anchor{result interpretation}
@cindex strict matching
@anchor{strict matching}
If the returned set has one or two columns, only the first tuple is
used and @code{rewrite} returns the value of its first column.
@cindex regex matching
@anchor{regex matching}
@@ -157,41 +164,46 @@ the resulting string is matched with the @var{regexp} column, which is
treated as an extended POSIX regular expression. If the value matches
the expression, the @var{result} column is expanded by replacing
@dfn{backreferences}: each occurrence of @code{$@var{digit}} (where
@var{digit} stands for a decimal digit from @samp{0} through @samp{9})
is replaced by the contents of the @var{digit}s parenthesized
subexpression in @var{regexp}. For compatibility with the traditional
usage, the @code{\@var{digit}} notation is also allowed. The
resulting value is then returned to the caller.
@cindex flags
@anchor{flags}
Optional @var{flags} column is a comma-separated list of flags that
-modify regular expression handling:
+control the matching algorithm.
@table @samp
@kindex NC
@kindex nocase
@cindex regular expression, case-insensitive
@cindex case-insensitive regular expression
@item NC
@itemx nocase
Treat @var{regexp} as case-insensitive regular expression.
@kindex case
@cindex regular expression, case-sensitive
@cindex case-sensitive regular expression
@item case
Treat @var{regexp} as case-sensitive (default).
+@kindex eq
+@cindex exact matching
+@item eq
+Use exact string matching.
+
@kindex QSA
@kindex qsappend
@cindex query string handling
@cindex append query string
@item QSA
@itemx qsappend
Treat the resulting value as URL; append any query string from the
original @var{value} to it.
@kindex QSD
@kindex qsdiscard
@cindex query string handling
@@ -200,24 +212,30 @@ original @var{value} to it.
@itemx qsdiscard
Treat the resulting value as URL; discard any query string attached to
the original @var{value}.
@anchor{X-VMOD-DBRW-Status}
@vindex X-VMOD-DBRW-Status
@cindex redirection code
@cindex status code
@item redirect=@var{code}
@item R=@var{code}
On success, set the @samp{X-VMOD-DBRW-Status} header to @var{code},
which must be a valid HTTP status code.
+
+@kindex regex
+@cindex regular expression matching
+@item regex
+Use regular expression matching. This is the default. This flag is
+provided for completeness sake, as a counterpart of @samp{eq}.
@end table
If @var{regexp} or @var{value} is NULL, strict matching is assumed
(@pxref{strict matching}).
If @var{flags} is NULL, it is ignored.
@node Configuration
@chapter Configuration
@deftypefn {function} void config (string @var{dbtype}, string @var{params}, @
string @var{query})
@@ -692,24 +710,42 @@ sub vcl_synth @{
std.integer(req.http.X-VMOD-DBRW-Status, 301);
@}
return (deliver);
@}
@}
@end group
@end example
The @code{X-VMOD-DBRW-Status} header, if set, contains the status code to be
returned to the client (@pxref{X-VMOD-DBRW-Status}). Notice the use
of the @command{vmod_std} module to cast it to integer.
+If an error occured during the rewrite, it is recommended to not
+cache the response. This way the next request will call rewrite again
+and eventually complete the rewriting. This can be achieved using the
+following @code{vcl_backend_response} fragment:
+
+@example
+@group
+sub vcl_backend_response
+@{
+ if (bereq.http.X-VMOD-DBRW-Error == "1") @{
+ set beresp.uncacheable = true;
+ return (deliver);
+ @}
+@}
+@end group
+@end example
+
+
@node Reporting Bugs
@chapter How to Report a Bug
Email bug reports to @email{gray@@gnu.org}.
As the purpose of bug reporting is to improve software, please be
sure to include a detailed information when reporting a bug. The minimum
information needed is:
@itemize @bullet
@item Module version you use.
@item A description of the bug.
diff --git a/src/dbrw.h b/src/dbrw.h
index bd0aa2a..9a8adf6 100644
--- a/src/dbrw.h
+++ b/src/dbrw.h
@@ -62,24 +62,25 @@ enum {
#define HTTP_STATUS_LEN 3
struct dbrw_config {
unsigned magic;
#define DBRW_CONFIG_MAGIC 0x67636667
int debug_level;
struct dbrw_backend *backend;
char **param;
char *param_str;
char *query;
int qdisp;
int regflags;
+ int match_type;
char status[HTTP_STATUS_LEN+1];
int idle_timeout;
VTAILQ_ENTRY(dbrw_config) list;
};
struct dbrw_connection {
unsigned magic;
#define DBRW_CONNECTION_MAGIC 0x6773716c
int state; /* Connection state */
int busy:1;
struct dbrw_config *conf; /* Pointer to the configuration data */
regmatch_t *matches; /* Match map */
diff --git a/src/vmod_dbrw.c b/src/vmod_dbrw.c
index 63d4ea1..beeb9af 100644
--- a/src/vmod_dbrw.c
+++ b/src/vmod_dbrw.c
@@ -1,14 +1,14 @@
/* This file is part of vmod-dbrw
- Copyright (C) 2013-2018 Sergey Poznyakoff
+ Copyright (C) 2013-2019 Sergey Poznyakoff
Vmod-dbrw 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-dbrw 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
@@ -16,24 +16,29 @@
*/
#include "dbrw.h"
#include <stdarg.h>
#include "wordsplit.h"
#include "pthread.h"
enum {
QDISP_NONE,
QDISP_APPEND,
QDISP_DISCARD
};
+enum {
+ MATCH_REGEX,
+ MATCH_EQ
+};
+
void
dbrw_debug(const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
vsyslog(LOG_DAEMON|LOG_DEBUG, fmt, ap);
va_end(ap);
}
void
dbrw_error(const char *fmt, ...)
{
@@ -145,25 +150,25 @@ argv_free(char **argv)
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[])
+parse_flags(const char *arg, int *qdisp, int *flags, int *match, 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)) {
@@ -195,24 +200,28 @@ parse_flags(const char *arg, int *qdisp, int *flags, char status[])
status[HTTP_STATUS_LEN] = 0;
}
} 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);
status[HTTP_STATUS_LEN] = 0;
}
+ } else if (strcmp(ws.ws_wordv[i], "regex") == 0) {
+ *match = MATCH_REGEX;
+ } else if (strcmp(ws.ws_wordv[i], "eq") == 0) {
+ *match = MATCH_EQ;
} else {
dbrw_error("unrecognized flag: %s", ws.ws_wordv[i]);
rc = 1;
}
}
wordsplit_free(&ws);
return rc;
}
/* Configure the module.
BACKEND - "mysql" or "pgsql"
@@ -268,31 +277,33 @@ vmod_config(VRT_CTX, struct vmod_priv *priv,
conf->backend = backend;
if ((s = findparam(conf->param, "debug")) != NULL)
conf->debug_level = atoi(s);
if ((s = findparam(conf->param, "timeout")) != NULL)
conf->idle_timeout = atoi(s);
else
conf->idle_timeout = -2;
conf->qdisp = QDISP_NONE;
conf->regflags = REG_EXTENDED;
+ conf->match_type = MATCH_REGEX;
conf->status[0] = 0;
s = findparam(conf->param, "flags");
if (s)
parse_flags(s,
&conf->qdisp,
&conf->regflags,
+ &conf->match_type,
conf->status);
AZ(pthread_mutex_lock(&config_pool_mtx));
VTAILQ_INSERT_HEAD(&config_pool, conf, list);
pthread_mutex_unlock(&config_pool_mtx);
}
priv->priv = conf;
}
static char *
expand_backref(VRT_CTX, const char *str, const char *val,
size_t matchcount, regmatch_t *matches, char *qry)
@@ -404,99 +415,107 @@ findmatch(VRT_CTX, struct dbrw_connection *conn, char **param)
*/
ws.ws_env = (const char **)param;
res = NULL;
for (i = 0; i < nt; i++) {
const char *pat = sql_get_column(conn, i, 1);
const char *val = sql_get_column(conn, i, 2);
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;
+ int match_type = conn->conf->match_type;
char *qry = NULL;
if (ISEMPTY(pat)) {
res = WS_Copy(WSPTR(ctx), sql_get_column(conn, i, 0), -1);
break;
}
debug(conn->conf, 1, ("considering \"%s\" ~ \"%s\"", pat, val));
if (wordsplit(val, &ws, wsflags)) {
dbrw_error("cannot expand string `%s': %s",
val, wordsplit_strerror(&ws));
continue;
}
wsflags |= WRDSF_REUSE;
val = ws.ws_wordv[0];
debug(conn->conf, 1, ("expanded to \"%s\" ~ \"%s\"",
pat, val));
strcpy(status, conn->conf->status);
if (nf == 4) {
if (parse_flags(sql_get_column(conn, i, 3),
- &qdisp, &rflags, status))
+ &qdisp, &rflags, &match_type, 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];
-
- regerror(rc, &re, errbuf, sizeof(errbuf));
- dbrw_error("cannot compile regexp \"%s\": %s",
- pat, errbuf);
- continue;
- }
- matchcount = re.re_nsub + 1;
- if (conn->matchsize < matchcount) {
- void *p = realloc(conn->matches,
- sizeof(conn->matches[0]) * matchcount);
- if (!p) {
- dbrw_error("not enough memory");
- regfree(&re);
+ if (match_type == MATCH_EQ) {
+ rc = ((rflags & REG_ICASE)
+ ? strcasecmp : strcmp) (pat, val);
+ } else {
+ rc = regcomp(&re, pat, rflags);
+ if (rc) {
+ char errbuf[512];
+
+ regerror(rc, &re, errbuf, sizeof(errbuf));
+ dbrw_error("cannot compile regexp \"%s\": %s",
+ pat, errbuf);
continue;
}
- conn->matches = p;
- conn->matchsize = matchcount;
- }
- rc = regexec(&re, val, matchcount, conn->matches, 0);
+ matchcount = re.re_nsub + 1;
+ if (conn->matchsize < matchcount) {
+ void *p = realloc(conn->matches,
+ sizeof(conn->matches[0]) * matchcount);
+ if (!p) {
+ dbrw_error("not enough memory");
+ regfree(&re);
+ continue;
+ }
+ conn->matches = p;
+ conn->matchsize = matchcount;
+ }
+
+ rc = regexec(&re, val, matchcount, conn->matches, 0);
- if (rc == 0)
- res = expand_backref(ctx, val,
- sql_get_column(conn, i, 0),
- matchcount, conn->matches, qry);
+ if (rc == 0)
+ res = expand_backref(ctx, val,
+ sql_get_column(conn, i, 0),
+ matchcount,
+ conn->matches, qry);
- regfree(&re);
+ regfree(&re);
+ }
+
if (rc == 0) {
debug(conn->conf, 1, ("match"));
if (status[0]) {
debug(conn->conf, 1,
("setting status %s", status));
dbrw_sethdr(ctx, HDR_REQ,
"\023X-VMOD-DBRW-Status:",
status);
}
break;
}
}
@@ -597,99 +616,104 @@ query_command_expand(char **ret, const char *cmd, size_t len, char **argv,
{
struct expcom *ec;
static char diagmsg[] = "unknown command: ";
for (ec = expcomtab; ec->com; ec++) {
if (strcmp(ec->com, argv[0]) == 0)
return ec->exp(clos, argv, ret);
}
return expand_error(ret, argv[0], "unknown command");
}
-static char *
-do_rewrite(VRT_CTX, struct dbrw_connection *cp, VCL_STRING arg)
+static int
+do_rewrite(VRT_CTX, struct dbrw_connection *cp, VCL_STRING arg, char **result)
{
struct wordsplit ws, wsenv;
int i, rc;
- char *res;
int wsflags;
+
+ *result = NULL;
if (sql_connect(cp) || cp->state != state_connected)
- return NULL;
+ return -1;
debug(cp->conf, 2, ("vmod_rewrite: splitting arg"));
wsenv.ws_delim = ";";
if (wordsplit(arg, &wsenv, WRDSF_NOVAR|WRDSF_NOCMD|WRDSF_DELIM)) {
dbrw_error("cannot split string `%s': %s",
arg, wordsplit_strerror(&wsenv));
- return NULL;
+ return -1;
}
if (cp->conf->backend->sql_escape) {
debug(cp->conf, 2, ("escaping variables"));
for (i = 0; i < wsenv.ws_wordc; i++) {
char *p = sql_escape(cp, wsenv.ws_wordv[i]);
if (!p) {
dbrw_error("cannot expand argument");
wordsplit_free(&wsenv);
- return NULL;
+ return -1;
}
free(wsenv.ws_wordv[i]);
wsenv.ws_wordv[i] = p;
debug(cp->conf, 3, ("%d: %s",i,p));
}
}
debug(cp->conf, 2, ("expanding query {\"%s\"}", cp->conf->query));
ws.ws_env = (const char **)wsenv.ws_wordv;
ws.ws_command = query_command_expand;
ws.ws_closure = cp;
wsflags = WRDSF_NOSPLIT | WRDSF_CLOSURE | WRDSF_ENV | WRDSF_UNDEF;
if (cp->conf->debug_level == 100) {
ws.ws_debug = dbrw_debug;
wsflags |= WRDSF_DEBUG | WRDSF_SHOWDBG;
}
rc = wordsplit(cp->conf->query, &ws, wsflags);
if (rc) {
dbrw_error("cannot expand query `%s': %s",
cp->conf->query, wordsplit_strerror(&ws));
wordsplit_free(&wsenv);
- return NULL;
+ return -1;
}
rc = sql_query(cp, ws.ws_wordv[0]);
wordsplit_free(&ws);
if (rc) {
- debug(cp->conf, 1, ("vmod_rewrite: query failed"));
+ dbrw_error("vmod_rewrite: query failed");
wordsplit_free(&wsenv);
- return NULL;
+ return -1;
}
- res = findmatch(ctx, cp, wsenv.ws_wordv);
+ *result = findmatch(ctx, cp, wsenv.ws_wordv);
wordsplit_free(&wsenv);
sql_free_result(cp);
- return res;
+ return 0;
}
VCL_STRING
vmod_rewrite(VRT_CTX, struct vmod_priv *priv, VCL_STRING arg)
{
struct dbrw_config *conf = priv->priv;
struct dbrw_connection *cp;
char *res = NULL;
-
+ int rc;
+
AN(conf);
debug(conf, 2, ("vmod_rewrite(%s) begin", arg));
cp = dbrw_connection_get(conf);
AN(cp);
- res = do_rewrite(ctx, cp, arg);
+ rc = do_rewrite(ctx, cp, arg, &res);
dbrw_connection_release(cp);
- debug(conf, 1, ("vmod_rewrite: res=%s", res ? res : "(NULL)"));
+ debug(conf, 1, ("vmod_rewrite: %d, res=%s", rc, res ? res : "(NULL)"));
+ if (rc) {
+ dbrw_sethdr(ctx, HDR_REQ, "\022X-VMOD-DBRW-Error:", "1");
+ }
return res;
}
diff --git a/tests/Makefile.am b/tests/Makefile.am
index 792ce24..28b795f 100644
--- a/tests/Makefile.am
+++ b/tests/Makefile.am
@@ -40,24 +40,25 @@ $(srcdir)/package.m4: $(top_srcdir)/configure.ac
## Test suite. ##
## ------------ ##
TESTSUITE_AT = \
exact01.at\
initdb.at\
rewrite01.at\
rewrite02.at\
rewrite03.at\
rewrite04.at\
rewrite05.at\
rewrite06.at\
+ rewrite07.at\
testsuite.at
TESTSUITE = $(srcdir)/testsuite
M4=m4
AUTOTEST = $(AUTOM4TE) --language=autotest
$(TESTSUITE): package.m4 $(TESTSUITE_AT)
$(AM_V_GEN)$(AUTOTEST) -I $(srcdir) testsuite.at -o $@.tmp
$(AM_V_at)mv $@.tmp $@
atconfig: $(top_builddir)/config.status
@cd $(top_builddir) && ./config.status tests/$@
diff --git a/tests/atlocal.in b/tests/atlocal.in
index 18a0b69..14f3fd4 100644
--- a/tests/atlocal.in
+++ b/tests/atlocal.in
@@ -42,27 +42,34 @@ server s1 {
} -start
varnish v1 -vcl+backend {
import std;
import dbrw from "$abs_top_builddir/src/.libs/libvmod_dbrw.so";
sub vcl_recv {
dbrw.config("$DBRW_TEST_DBTYPE", "$DBRW_TEST_PARAMS",
{"$1"});
set req.http.X-Redirect-To =
dbrw.rewrite("host=" + req.http.Host + ";" +
"url=" + req.url);
- return(synth(301, "Redirect"));
+ if (req.http.X-Redirect-To != "") {
+ return(synth(301, "Redirect"));
+ } else if (req.http.X-VMOD-DBRW-Error == "1") {
+ return(synth(500, "DBRW Error"));
+ }
}
sub vcl_synth {
+ if (req.http.X-VMOD-DBRW-Error == "1") {
+ set resp.http.X-VMOD-DBRW-Error = "1";
+ }
if (resp.status == 301) {
if (req.http.X-VMOD-DBRW-Status) {
set resp.status = std.integer(req.http.X-VMOD-DBRW-Status, 301);
}
set resp.http.Location = req.http.X-Redirect-To;
return (deliver);
}
}
} -start
EOT
}
diff --git a/tests/initdb.at b/tests/initdb.at
index 0355e3b..f7bfe0b 100644
--- a/tests/initdb.at
+++ b/tests/initdb.at
@@ -35,26 +35,26 @@ DROP TABLE IF EXISTS rewrite;
CREATE TABLE rewrite (
host varchar(255) NOT NULL DEFAULT '',
url varchar(255) NOT NULL DEFAULT '',
dest varchar(255) DEFAULT NULL,
value varchar(255) DEFAULT NULL,
pattern varchar(255) DEFAULT NULL,
flags varchar(64) DEFAULT NULL
);
CREATE INDEX source ON rewrite(host,url);
INSERT INTO rewrite VALUES
-('en.example.net','/local','http://uno.example.com/remote',NULL,NULL,NULL),
+('en.example.net','/local','http://uno.example.com/remote',NULL,NULL,'eq'),
('en.example.net','/local','http://dos.example.com/$[]1','$url','/local/(.*)',NULL),
('en.example.net','/local2','http://to.example.net/$[]1$[]2','$url','/local2/([[^\\?]]*)(\\?.*)?',NULL),
('to.example.net','/local','http://dos.example.net/$[]1','$url','/local/(.*)','QSA'),
('tre.example.net','/local','http://dos.example.net/$[]1?i=10','$url','/local/(.*)','QSA,R=302');
EOT
],
[0],
[],
[],
[mv err $FAILFILE],
[echo "OK" > $INITFILE])
-]) \ No newline at end of file
+])
diff --git a/tests/rewrite07.at b/tests/rewrite07.at
new file mode 100644
index 0000000..6554fc7
--- /dev/null
+++ b/tests/rewrite07.at
@@ -0,0 +1,40 @@
+# This file is part of vmod-dbrw -*- autotest -*-
+# Copyright (C) 2013-2018 Sergey Poznyakoff
+#
+# Vmod-dbrw 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-dbrw 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-dbrw. If not, see <http://www.gnu.org/licenses/>.
+
+AT_SETUP(Rewrite 7 - SQL error handling)
+AT_KEYWORDS(rewrite rewrite07)
+
+AT_DBRW_INIT
+
+AT_CHECK([
+AT_DBINIT_PREREQ
+# This test simulates rewrite failure in order to check whether
+# the X-VMOD-DBRW-Error header is correctly set on return.
+AT_VCL([SELECT dest,pattern,value,flags
+ FROM rewrite
+ WHERE host='$BADHOST' AND url IN ($(urlprefixes $url))
+ ORDER BY LENGTH(dest),value DESC],
+ [txreq -url /local/foo/bar?x&y&z -hdr "Host:tre.example.net"
+ rxresp
+ expect resp.status == 500
+ expect resp.http.X-VMOD-DBRW-Error == 1
+])
+AT_VARNISHTEST
+],
+[0],
+[OK
+])
+AT_CLEANUP
diff --git a/tests/testsuite.at b/tests/testsuite.at
index 5858b6a..203b667 100644
--- a/tests/testsuite.at
+++ b/tests/testsuite.at
@@ -51,15 +51,16 @@ m4_define([AT_VARNISHTEST],
AT_INIT
m4_include([exact01.at])
m4_include([rewrite01.at])
m4_include([rewrite02.at])
m4_include([rewrite03.at])
m4_include([rewrite04.at])
m4_include([rewrite05.at])
m4_include([rewrite06.at])
+m4_include([rewrite07.at])
# End of testsuite.at

Return to:

Send suggestions and report system problems to the System administrator.