[master] 0799e0cc0 Sort out VSB quoting, add testcases and asserts on usage.
Poul-Henning Kamp
phk at FreeBSD.org
Mon Mar 1 13:36:09 UTC 2021
commit 0799e0cc0669d42c2f79dd54db9ee5414c6bddae
Author: Poul-Henning Kamp <phk at FreeBSD.org>
Date: Mon Mar 1 13:03:14 2021 +0000
Sort out VSB quoting, add testcases and asserts on usage.
diff --git a/include/vsb.h b/include/vsb.h
index f67220e96..7a32360e3 100644
--- a/include/vsb.h
+++ b/include/vsb.h
@@ -79,12 +79,52 @@ ssize_t VSB_len(const struct vsb *);
void VSB_delete(struct vsb *) v_deprecated_;
void VSB_fini(struct vsb *);
void VSB_destroy(struct vsb **);
-#define VSB_QUOTE_NONL 1
+
+/*
+ * VSB_quote[_pfx] has four major modes, and two modifiers
+ */
+
+#define VSB_QUOTE_PLAIN 0
+ /*
+ * Basic "show me the string" mode
+ * All output is a single line
+ */
#define VSB_QUOTE_JSON 2
+ /*
+ * Output suitable for inclusion between "..." in JSON
+ * Uses JSON \u%04x quoting.
+ * Anything above 0x7e had better be UTF-8
+ */
#define VSB_QUOTE_HEX 4
+ /*
+ * Hex dump, single line.
+ * All zero data is compressed to "0x0...0"
+ */
#define VSB_QUOTE_CSTR 8
+ /*
+ * C lanuage source code literal string(s)
+ * Breaks strings at \n (expecting string concatenation)
+ */
#define VSB_QUOTE_UNSAFE 16
+ /*
+ * For general display applications
+ * " and \ are not quoted
+ * Splits output to new line at '\n'
+ * Implies VSB_QUOTE_NONL
+ */
+
+#define VSB_QUOTE_NONL 1
+ /*
+ * If the output does not end in \n, append \n
+ * Can be combined with all other modes.
+ */
+
#define VSB_QUOTE_ESCHEX 32
+ /*
+ * Use \x%02x instead of \%03o
+ * Not valid with VSB_QUOTE_JSON and VSB_QUOTE_HEX
+ */
+
void VSB_quote_pfx(struct vsb *, const char*, const void *,
int len, int how);
void VSB_quote(struct vsb *, const void *, int len, int how);
diff --git a/lib/libvarnish/vsb.c b/lib/libvarnish/vsb.c
index 8857bd26a..5cc76601a 100644
--- a/lib/libvarnish/vsb.c
+++ b/lib/libvarnish/vsb.c
@@ -555,73 +555,102 @@ VSB_destroy(struct vsb **s)
/*
* Quote a string
*/
+
+static void
+vsb_quote_hex(struct vsb *s, const uint8_t *u, size_t len)
+{
+ const uint8_t *w;
+
+ VSB_cat(s, "0x");
+ for (w = u; w < u + len; w++)
+ if (*w != 0x00)
+ break;
+ if (w == u + len && len > 4) {
+ VSB_cat(s, "0...0");
+ } else {
+ for (w = u; w < u + len; w++)
+ VSB_printf(s, "%02x", *w);
+ }
+}
+
void
VSB_quote_pfx(struct vsb *s, const char *pfx, const void *v, int len, int how)
{
- const char *p;
- const char *q;
+ const uint8_t *p = v;
+ const uint8_t *q;
int quote = 0;
- int nl = 0;
- const unsigned char *u, *w;
+ int nl;
+
+ nl = how &
+ (VSB_QUOTE_JSON|VSB_QUOTE_HEX|VSB_QUOTE_CSTR|VSB_QUOTE_UNSAFE);
+ AZ(nl & (nl - 1)); // Only one bit can be set
+
+ if (how & VSB_QUOTE_ESCHEX)
+ AZ(how & (VSB_QUOTE_JSON|VSB_QUOTE_HEX));
+
+ if (how & VSB_QUOTE_UNSAFE)
+ how |= VSB_QUOTE_NONL;
- assert(v != NULL);
+ assert(p != NULL);
if (len == -1)
len = strlen(v);
if (len == 0 && (how & VSB_QUOTE_CSTR)) {
VSB_printf(s, "%s\"\"", pfx);
- return;
- } else if (len == 0)
+ if ((how & VSB_QUOTE_NONL))
+ VSB_putc(s, '\n');
+ }
+
+ if (len == 0)
return;
VSB_cat(s, pfx);
if (how & VSB_QUOTE_HEX) {
- u = v;
- for (w = u; w < u + len; w++)
- if (*w != 0x00)
- break;
- VSB_cat(s, "0x");
- if (w == u + len && len > 4) {
- VSB_cat(s, "0...0");
- } else {
- for (w = u; w < u + len; w++)
- VSB_printf(s, "%02x", *w);
- }
+ vsb_quote_hex(s, v, len);
+ if (how & VSB_QUOTE_NONL)
+ VSB_putc(s, '\n');
return;
}
- p = v;
+
+ if (how & VSB_QUOTE_CSTR)
+ VSB_putc(s, '"');
for (q = p; q < p + len; q++) {
- if (!isgraph(*q) || *q == '"' || *q == '\\') {
+ if (
+ *q < 0x20 ||
+ *q == '"' ||
+ *q == '\\' ||
+ (*q == '?' && (how & VSB_QUOTE_CSTR)) ||
+ (*q > 0x7e && !(how & VSB_QUOTE_JSON))
+ ) {
quote++;
break;
}
}
- if (!quote && !(how & (VSB_QUOTE_JSON|VSB_QUOTE_CSTR))) {
- (void)VSB_bcat(s, p, len);
- if ((how & (VSB_QUOTE_UNSAFE|VSB_QUOTE_NONL)) &&
+
+ if (!quote) {
+ VSB_bcat(s, p, len);
+ if ((how & VSB_QUOTE_NONL) &&
p[len-1] != '\n')
(void)VSB_putc(s, '\n');
+ if (how & VSB_QUOTE_CSTR)
+ VSB_putc(s, '"');
return;
}
- if (how & VSB_QUOTE_CSTR)
- (void)VSB_putc(s, '"');
-
+ nl = 0;
for (q = p; q < p + len; q++) {
if (nl)
VSB_cat(s, pfx);
nl = 0;
switch (*q) {
case '?':
- if (how & VSB_QUOTE_CSTR)
+ /* Avoid C Trigraph insanity */
+ if (how & VSB_QUOTE_CSTR && !(how & VSB_QUOTE_JSON))
(void)VSB_putc(s, '\\');
(void)VSB_putc(s, *q);
break;
- case ' ':
- (void)VSB_putc(s, *q);
- break;
case '\\':
case '"':
if (!(how & VSB_QUOTE_UNSAFE))
@@ -630,38 +659,43 @@ VSB_quote_pfx(struct vsb *s, const char *pfx, const void *v, int len, int how)
break;
case '\n':
if (how & VSB_QUOTE_CSTR) {
- (void)VSB_printf(s, "\\n\"\n%s\"", pfx);
- } else if (how & (VSB_QUOTE_NONL|VSB_QUOTE_UNSAFE)) {
- (void)VSB_printf(s, "\n");
+ VSB_printf(s, "\\n\"\n%s\"", pfx);
+ } else if (how & VSB_QUOTE_JSON) {
+ VSB_printf(s, "\\n");
+ } else if (how & VSB_QUOTE_NONL) {
+ VSB_putc(s, *q);
nl = 1;
} else {
- (void)VSB_printf(s, "\\n");
+ VSB_printf(s, "\\n");
}
break;
case '\r':
- (void)VSB_cat(s, "\\r");
+ VSB_cat(s, "\\r");
break;
case '\t':
- (void)VSB_cat(s, "\\t");
+ VSB_cat(s, "\\t");
break;
case '\v':
- (void)VSB_cat(s, "\\v");
+ VSB_cat(s, "\\v");
break;
default:
- /* XXX: Implement VSB_QUOTE_JSON */
- if (isgraph(*q))
- (void)VSB_putc(s, *q);
+ if (0x20 <= *q && *q <= 0x7e)
+ VSB_putc(s, *q);
+ else if (*q > 0x7e && (how & VSB_QUOTE_JSON))
+ VSB_putc(s, *q);
+ else if (how & VSB_QUOTE_JSON)
+ VSB_printf(s, "\\u%04x", *q);
else if (how & VSB_QUOTE_ESCHEX)
- (void)VSB_printf(s, "\\x%02x", *q & 0xff);
+ VSB_printf(s, "\\x%02x", *q);
else
- (void)VSB_printf(s, "\\%03o", *q & 0xff);
+ VSB_printf(s, "\\%03o", *q);
break;
}
}
if (how & VSB_QUOTE_CSTR)
- (void)VSB_putc(s, '"');
- if ((how & (VSB_QUOTE_NONL|VSB_QUOTE_UNSAFE)) && !nl)
- (void)VSB_putc(s, '\n');
+ VSB_putc(s, '"');
+ if ((how & VSB_QUOTE_NONL) && !nl)
+ VSB_putc(s, '\n');
}
void
diff --git a/lib/libvarnish/vsb_test.c b/lib/libvarnish/vsb_test.c
index 8ce277c65..ef09a14c4 100644
--- a/lib/libvarnish/vsb_test.c
+++ b/lib/libvarnish/vsb_test.c
@@ -10,13 +10,79 @@
struct tc {
int how;
+ int inlen;
const char *in;
const char *out;
};
static struct tc tcs[] = {
{
- 0, NULL, NULL
+ VSB_QUOTE_HEX,
+ 5, "\x00\n\x7e\x7f\xff",
+ "PFX0x000a7e7fff"
+ },
+ {
+ VSB_QUOTE_HEX,
+ 5, "\0\0\0\0\0",
+ "PFX0x0...0"
+ },
+ {
+ VSB_QUOTE_HEX | VSB_QUOTE_NONL,
+ 5, "\x00\n\x7e\x7f\xff",
+ "PFX0x000a7e7fff\n"
+ },
+ {
+ VSB_QUOTE_ESCHEX,
+ 5, "\x00\n\x7e\x7f\xff",
+ "PFX\\x00\\n~\\x7f\\xff",
+ },
+ {
+ 0,
+ 5, "\x00\n\x7e\x7f\xff",
+ "PFX\\000\\n~\\177\\377",
+ },
+ {
+ VSB_QUOTE_UNSAFE,
+ 5, "\x00\n\x7e\x7f\xff",
+ "PFX\\000\nPFX~\\177\\377\n",
+ },
+ {
+ VSB_QUOTE_UNSAFE,
+ -1, "\n\"\\\t",
+ "PFX\nPFX\"\\\\t\n"
+ },
+ {
+ VSB_QUOTE_CSTR | VSB_QUOTE_ESCHEX,
+ 5, "\x00\n\x7e\x7f\xff",
+ "PFX\"\\x00\\n\"\nPFX\"~\\x7f\\xff\"",
+ },
+ {
+ VSB_QUOTE_JSON,
+ 5, "\x00\n\x7e\x7f\xff",
+ "PFX\\u0000\\n~\x7f\xff",
+ },
+ {
+ VSB_QUOTE_JSON | VSB_QUOTE_NONL,
+ 5, "\x00\n\x7e\x7f\xff",
+ "PFX\\u0000\\n~\x7f\xff\n",
+ },
+ {
+ VSB_QUOTE_CSTR,
+ -1, "",
+ "PFX\"\""
+ },
+ {
+ VSB_QUOTE_CSTR,
+ -1, "?",
+ "PFX\"\\?\""
+ },
+ {
+ VSB_QUOTE_NONL,
+ -1, "\n\t",
+ "PFX\nPFX\\t\n"
+ },
+ {
+ 0, -1, NULL, NULL
}
};
@@ -26,25 +92,58 @@ main(int argc, char *argv[])
int err = 0;
struct tc *tc;
struct vsb *vsb;
+ struct vsb *vsbo;
(void)argc;
(void)argv;
vsb = VSB_new_auto();
AN(vsb);
+ vsbo = VSB_new_auto();
+ AN(vsbo);
for (tc = tcs; tc->in; tc++) {
- VSB_quote(vsb, tc->in, -1, tc->how);
+ VSB_quote_pfx(vsb, "PFX", tc->in, tc->inlen, tc->how);
assert(VSB_finish(vsb) == 0);
- printf("%s -> %s", tc->in, VSB_data(vsb));
+ VSB_clear(vsbo);
+ VSB_printf(vsbo, "0x%02x: ", tc->how);
+ VSB_quote(vsbo, tc->in, tc->inlen, VSB_QUOTE_HEX);
+ VSB_printf(vsbo, " -> ");
+ VSB_quote(vsbo, VSB_data(vsb), -1, VSB_QUOTE_HEX);
+ VSB_printf(vsbo, " (");
+ VSB_quote(vsbo, tc->out, -1, VSB_QUOTE_ESCHEX);
+ VSB_printf(vsbo, ")");
if (strcmp(VSB_data(vsb), tc->out)) {
- printf(", but should have been %s", tc->out);
+ VSB_printf(vsbo, "\nShould have been:\n\t");
+ VSB_quote(vsbo, tc->out, -1, VSB_QUOTE_HEX);
+ VSB_printf(vsbo, "\nThat's:\n\t");
+ VSB_quote(vsbo, VSB_data(vsb), -1, VSB_QUOTE_ESCHEX);
+ VSB_printf(vsbo, "\nvs:\n\t");
+ VSB_quote(vsbo, tc->out, -1, VSB_QUOTE_ESCHEX);
+ VSB_printf(vsbo, "\nFlags 0x%02x = ", tc->how);
+ if (!tc->how)
+ VSB_printf(vsbo, "\n\t0");
+ if (tc->how & VSB_QUOTE_NONL)
+ VSB_printf(vsbo, "\n\tVSB_QUOTE_NONL");
+ if (tc->how & VSB_QUOTE_JSON)
+ VSB_printf(vsbo, "\n\tVSB_QUOTE_JSON");
+ if (tc->how & VSB_QUOTE_HEX)
+ VSB_printf(vsbo, "\n\tVSB_QUOTE_HEX");
+ if (tc->how & VSB_QUOTE_CSTR)
+ VSB_printf(vsbo, "\n\tVSB_QUOTE_CSTR");
+ if (tc->how & VSB_QUOTE_UNSAFE)
+ VSB_printf(vsbo, "\n\tVSB_QUOTE_UNSAFE");
+ if (tc->how & VSB_QUOTE_ESCHEX)
+ VSB_printf(vsbo, "\n\tVSB_QUOTE_ESCHEX");
+ VSB_printf(vsbo, "\n\n");
err = 1;
}
- printf("\n");
+ AZ(VSB_finish(vsbo));
+ printf("%s\n", VSB_data(vsbo));
VSB_clear(vsb);
}
VSB_destroy(&vsb);
+ VSB_destroy(&vsbo);
printf("error is %i\n", err);
return (err);
}
More information about the varnish-commit
mailing list