From nils.goroll at uplex.de Sat May 3 12:23:07 2025 From: nils.goroll at uplex.de (Nils Goroll) Date: Sat, 3 May 2025 12:23:07 +0000 (UTC) Subject: [master] f43b83c49 ban_lurker: Push stats when pacing Message-ID: <20250503122307.42E5C11B99D@lists.varnish-cache.org> commit f43b83c492a8c76bc5526f1bdc52fc6bedcaf2ab Author: Nils Goroll Date: Sat May 3 14:16:32 2025 +0200 ban_lurker: Push stats when pacing When the ban lurker was busy with a long ban list, it did not update statistics in potentially a very long time, which could lead to irritating statistics values. We now try to update the stats when we pace the ban lurker anyway. The update already happens unconditionally when the ban lurker runs dry. diff --git a/bin/varnishd/cache/cache_ban_lurker.c b/bin/varnishd/cache/cache_ban_lurker.c index 17087a8e2..3057312c0 100644 --- a/bin/varnishd/cache/cache_ban_lurker.c +++ b/bin/varnishd/cache/cache_ban_lurker.c @@ -255,6 +255,7 @@ ban_lurker_test_ban(struct worker *wrk, struct ban *bt, while (1) { if (++ban_batch > cache_param->ban_lurker_batch) { + (void)Pool_TrySumstat(wrk); VTIM_sleep(cache_param->ban_lurker_sleep); ban_batch = 0; } From nils.goroll at uplex.de Mon May 5 11:33:06 2025 From: nils.goroll at uplex.de (Nils Goroll) Date: Mon, 5 May 2025 11:33:06 +0000 (UTC) Subject: [master] 7ef595c2e Flexelint silencing Message-ID: <20250505113306.A2984102552@lists.varnish-cache.org> commit 7ef595c2e19c93a9abecbf900527362b6fa71025 Author: Nils Goroll Date: Mon May 5 13:32:39 2025 +0200 Flexelint silencing diff --git a/flint.lnt b/flint.lnt index 4cc5cb348..6607b23ad 100644 --- a/flint.lnt +++ b/flint.lnt @@ -325,6 +325,10 @@ -esym(534, wredrawln) -esym(534, wrefresh) +/////////////////////////////////////////////////////////////////////// +// vmod_debug, put here because it is used twice (varnishd2 and separate) +-esym(768, arg_xyzzy_debug_argtest::*) + /////////////////////////////////////////////////////////////////////// // Noise reduction, review periodically From asadsa at varnish-software.com Mon May 12 12:57:06 2025 From: asadsa at varnish-software.com (Asad Sajjad Ahmed) Date: Mon, 12 May 2025 12:57:06 +0000 (UTC) Subject: [master] ba59fd2d0 v1f: Read end-of-chunk as part of the chunk Message-ID: <20250512125706.4A66411E28F@lists.varnish-cache.org> commit ba59fd2d0dd88022d7390a5d943f458a45fc5e6d Author: Nils Goroll Date: Mon May 30 13:09:11 2022 +0200 v1f: Read end-of-chunk as part of the chunk Until now, we read the (CR)?LF at the end of a chunk as part of the next chunk header (see: /* Skip leading whitespace */). For a follow up commit, we are going to want to know if the next chunk header is available for read, so we now consume the chunk end as part of the chunk itself. This also fixes a corner case: We previously accepted chunks with a missing end-of-chunk (see fix of r01729.vtc). Ref: https://datatracker.ietf.org/doc/html/rfc7230#section-4.1 diff --git a/bin/varnishd/http1/cache_http1_vfp.c b/bin/varnishd/http1/cache_http1_vfp.c index 20f349d1c..aceb5a628 100644 --- a/bin/varnishd/http1/cache_http1_vfp.c +++ b/bin/varnishd/http1/cache_http1_vfp.c @@ -89,6 +89,24 @@ v1f_read(const struct vfp_ctx *vc, struct http_conn *htc, void *d, ssize_t len) } +/*-------------------------------------------------------------------- + * read (CR)?LF at the end of a chunk + */ +static enum vfp_status +v1f_chunk_end(struct vfp_ctx *vc, struct http_conn *htc) +{ + char c; + + if (v1f_read(vc, htc, &c, 1) <= 0) + return (VFP_Error(vc, "chunked read err")); + if (c == '\r' && v1f_read(vc, htc, &c, 1) <= 0) + return (VFP_Error(vc, "chunked read err")); + if (c != '\n') + return (VFP_Error(vc, "chunked tail no NL")); + return (VFP_OK); +} + + /*-------------------------------------------------------------------- * Read a chunked HTTP object. * @@ -99,6 +117,7 @@ static enum vfp_status v_matchproto_(vfp_pull_f) v1f_chunked_pull(struct vfp_ctx *vc, struct vfp_entry *vfe, void *ptr, ssize_t *lp) { + static enum vfp_status vfps; struct http_conn *htc; char buf[20]; /* XXX: 20 is arbitrary */ char *q; @@ -168,18 +187,15 @@ v1f_chunked_pull(struct vfp_ctx *vc, struct vfp_entry *vfe, void *ptr, return (VFP_Error(vc, "chunked insufficient bytes")); *lp = lr; vfe->priv2 -= lr; - if (vfe->priv2 == 0) - vfe->priv2 = -1; - return (VFP_OK); + if (vfe->priv2 != 0) + return (VFP_OK); + + vfe->priv2 = -1; + return (v1f_chunk_end(vc, htc)); } AZ(vfe->priv2); - if (v1f_read(vc, htc, buf, 1) <= 0) - return (VFP_Error(vc, "chunked read err")); - if (buf[0] == '\r' && v1f_read(vc, htc, buf, 1) <= 0) - return (VFP_Error(vc, "chunked read err")); - if (buf[0] != '\n') - return (VFP_Error(vc, "chunked tail no NL")); - return (VFP_END); + vfps = v1f_chunk_end(vc, htc); + return (vfps == VFP_OK ? VFP_END : vfps); } static const struct vfp v1f_chunked = { diff --git a/bin/varnishtest/tests/r01184.vtc b/bin/varnishtest/tests/r01184.vtc index 0988e65a3..94ecd3c23 100644 --- a/bin/varnishtest/tests/r01184.vtc +++ b/bin/varnishtest/tests/r01184.vtc @@ -62,6 +62,7 @@ server s1 { sendhex " 10 45 f3 a9 83 b8 18 1c 7b c2 30 55 04 17 13 c4" sendhex " 0f 07 5f 7a 38 f4 8e 50 b3 37 d4 3a 32 4a 34 07" sendhex " FF FF FF FF FF FF FF FF 72 ea 06 5f b3 1c fa dd" + send "\n" expect_close } -start @@ -93,6 +94,7 @@ server s1 { sendhex " 10 45 f3 a9 83 b8 18 1c 7b c2 30 55 04 17 13 c4" sendhex " 0f 07 5f 7a 38 f4 8e 50 b3 37 d4 3a 32 4a 34 07" sendhex " FF FF FF FF FF FF FF FF 72 ea 06 5f b3 1c fa dd" + send "\n" expect_close } -start diff --git a/bin/varnishtest/tests/r01506.vtc b/bin/varnishtest/tests/r01506.vtc index 96b7b54c9..f7f89a716 100644 --- a/bin/varnishtest/tests/r01506.vtc +++ b/bin/varnishtest/tests/r01506.vtc @@ -7,15 +7,15 @@ server s0 { txresp -nolen \ -hdr "Transfer-Encoding: chunked" \ -hdr "Connection: close" - send "11\r\n0_23456789abcdef\n" - send "11\r\n1_23456789abcdef\n" - send "11\r\n2_23456789abcdef\n" - send "11\r\n3_23456789abcdef\n" + send "11\r\n0_23456789abcdef\n\n" + send "11\r\n1_23456789abcdef\n\n" + send "11\r\n2_23456789abcdef\n\n" + send "11\r\n3_23456789abcdef\n\n" barrier b1 sync - send "11\r\n4_23456789abcdef\n" - send "11\r\n5_23456789abcdef\n" - send "11\r\n6_23456789abcdef\n" - send "11\r\n7_23456789abcdef\n" + send "11\r\n4_23456789abcdef\n\n" + send "11\r\n5_23456789abcdef\n\n" + send "11\r\n6_23456789abcdef\n\n" + send "11\r\n7_23456789abcdef\n\n" chunkedlen 0 } -dispatch diff --git a/bin/varnishtest/tests/r01729.vtc b/bin/varnishtest/tests/r01729.vtc index 883a60cc6..f6a01e976 100644 --- a/bin/varnishtest/tests/r01729.vtc +++ b/bin/varnishtest/tests/r01729.vtc @@ -11,7 +11,7 @@ server s1 { send "\r\n" send "14\r\n" send "0123456789" - send "0123456789" + send "0123456789\n" send "0\r\n" send "\r\n" @@ -29,7 +29,7 @@ client c1 { send "\r\n" send "14\r\n" send "0123456789" - send "0123456789" + send "0123456789\n" send "0\r\n" send "\r\n" @@ -45,7 +45,7 @@ client c1 { send "\r\n" send "14\r\n" send "0123456789" - send "0123456789" + send "0123456789\n" send "0\r\n" send "\r\n" From asadsa at varnish-software.com Mon May 12 12:57:06 2025 From: asadsa at varnish-software.com (Asad Sajjad Ahmed) Date: Mon, 12 May 2025 12:57:06 +0000 (UTC) Subject: [master] e5ccb8b8c v1f: pull chunk header parsing into an own function Message-ID: <20250512125706.5F15411E292@lists.varnish-cache.org> commit e5ccb8b8c4a760b23c75e7d928005769934149ff Author: Nils Goroll Date: Mon May 30 13:42:58 2022 +0200 v1f: pull chunk header parsing into an own function ... which we are going to need in a follow up commit. No functional changes, diff best viewed with -b diff --git a/bin/varnishd/http1/cache_http1_vfp.c b/bin/varnishd/http1/cache_http1_vfp.c index aceb5a628..d684f1043 100644 --- a/bin/varnishd/http1/cache_http1_vfp.c +++ b/bin/varnishd/http1/cache_http1_vfp.c @@ -108,76 +108,98 @@ v1f_chunk_end(struct vfp_ctx *vc, struct http_conn *htc) /*-------------------------------------------------------------------- - * Read a chunked HTTP object. + * Parse a chunk header and, for VFP_OK, return size in a pointer * * XXX: Reading one byte at a time is pretty pessimal. */ -static enum vfp_status v_matchproto_(vfp_pull_f) -v1f_chunked_pull(struct vfp_ctx *vc, struct vfp_entry *vfe, void *ptr, - ssize_t *lp) +static enum vfp_status +v1f_chunked_hdr(struct vfp_ctx *vc, struct http_conn *htc, ssize_t *szp) { - static enum vfp_status vfps; - struct http_conn *htc; char buf[20]; /* XXX: 20 is arbitrary */ - char *q; unsigned u; uintmax_t cll; - ssize_t cl, l, lr; + ssize_t cl, lr; + char *q; CHECK_OBJ_NOTNULL(vc, VFP_CTX_MAGIC); - CHECK_OBJ_NOTNULL(vfe, VFP_ENTRY_MAGIC); - CAST_OBJ_NOTNULL(htc, vfe->priv1, HTTP_CONN_MAGIC); - AN(ptr); - AN(lp); + CHECK_OBJ_NOTNULL(htc, HTTP_CONN_MAGIC); + AN(szp); + assert(*szp == -1); - l = *lp; - *lp = 0; - if (vfe->priv2 == -1) { - /* Skip leading whitespace */ - do { - lr = v1f_read(vc, htc, buf, 1); - if (lr <= 0) - return (VFP_Error(vc, "chunked read err")); - } while (vct_islws(buf[0])); - - if (!vct_ishex(buf[0])) - return (VFP_Error(vc, "chunked header non-hex")); - - /* Collect hex digits, skipping leading zeros */ - for (u = 1; u < sizeof buf; u++) { - do { - lr = v1f_read(vc, htc, buf + u, 1); - if (lr <= 0) - return (VFP_Error(vc, "chunked read err")); - } while (u == 1 && buf[0] == '0' && buf[u] == '0'); - if (!vct_ishex(buf[u])) - break; - } + /* Skip leading whitespace */ + do { + lr = v1f_read(vc, htc, buf, 1); + if (lr <= 0) + return (VFP_Error(vc, "chunked read err")); + } while (vct_islws(buf[0])); - if (u >= sizeof buf) - return (VFP_Error(vc, "chunked header too long")); + if (!vct_ishex(buf[0])) + return (VFP_Error(vc, "chunked header non-hex")); - /* Skip trailing white space */ - while (vct_islws(buf[u]) && buf[u] != '\n') { + /* Collect hex digits, skipping leading zeros */ + for (u = 1; u < sizeof buf; u++) { + do { lr = v1f_read(vc, htc, buf + u, 1); if (lr <= 0) return (VFP_Error(vc, "chunked read err")); - } + } while (u == 1 && buf[0] == '0' && buf[u] == '0'); + if (!vct_ishex(buf[u])) + break; + } - if (buf[u] != '\n') - return (VFP_Error(vc, "chunked header no NL")); + if (u >= sizeof buf) + return (VFP_Error(vc, "chunked header too long")); + + /* Skip trailing white space */ + while (vct_islws(buf[u]) && buf[u] != '\n') { + lr = v1f_read(vc, htc, buf + u, 1); + if (lr <= 0) + return (VFP_Error(vc, "chunked read err")); + } - buf[u] = '\0'; + if (buf[u] != '\n') + return (VFP_Error(vc, "chunked header no NL")); - cll = strtoumax(buf, &q, 16); - if (q == NULL || *q != '\0') - return (VFP_Error(vc, "chunked header number syntax")); - cl = (ssize_t)cll; - if (cl < 0 || (uintmax_t)cl != cll) - return (VFP_Error(vc, "bogusly large chunk size")); + buf[u] = '\0'; - vfe->priv2 = cl; + cll = strtoumax(buf, &q, 16); + if (q == NULL || *q != '\0') + return (VFP_Error(vc, "chunked header number syntax")); + cl = (ssize_t)cll; + if (cl < 0 || (uintmax_t)cl != cll) + return (VFP_Error(vc, "bogusly large chunk size")); + + *szp = cl; + return (VFP_OK); +} + + +/*-------------------------------------------------------------------- + * Read a chunked HTTP object. + * + */ + +static enum vfp_status v_matchproto_(vfp_pull_f) +v1f_chunked_pull(struct vfp_ctx *vc, struct vfp_entry *vfe, void *ptr, + ssize_t *lp) +{ + static enum vfp_status vfps; + struct http_conn *htc; + ssize_t l, lr; + + CHECK_OBJ_NOTNULL(vc, VFP_CTX_MAGIC); + CHECK_OBJ_NOTNULL(vfe, VFP_ENTRY_MAGIC); + CAST_OBJ_NOTNULL(htc, vfe->priv1, HTTP_CONN_MAGIC); + AN(ptr); + AN(lp); + + l = *lp; + *lp = 0; + if (vfe->priv2 == -1) { + vfps = v1f_chunked_hdr(vc, htc, &vfe->priv2); + if (vfps != VFP_OK) + return (vfps); } if (vfe->priv2 > 0) { if (vfe->priv2 < l) From asadsa at varnish-software.com Mon May 12 12:57:06 2025 From: asadsa at varnish-software.com (Asad Sajjad Ahmed) Date: Mon, 12 May 2025 12:57:06 +0000 (UTC) Subject: [master] d7a0253ca v1f: Parse CRLF separately from other white space Message-ID: <20250512125706.76FBB11E295@lists.varnish-cache.org> commit d7a0253ca1bbf3177e6c980c22b41602561592a5 Author: Dridi Boukelmoune Date: Wed Apr 2 16:03:43 2025 +0200 v1f: Parse CRLF separately from other white space It's a little bit harder to follow the CRLF logic when it is intertwined with the skipped surrounding white space. diff --git a/bin/varnishd/http1/cache_http1_vfp.c b/bin/varnishd/http1/cache_http1_vfp.c index d684f1043..fbc3dcb8b 100644 --- a/bin/varnishd/http1/cache_http1_vfp.c +++ b/bin/varnishd/http1/cache_http1_vfp.c @@ -132,7 +132,7 @@ v1f_chunked_hdr(struct vfp_ctx *vc, struct http_conn *htc, ssize_t *szp) lr = v1f_read(vc, htc, buf, 1); if (lr <= 0) return (VFP_Error(vc, "chunked read err")); - } while (vct_islws(buf[0])); + } while (vct_isows(buf[0])); if (!vct_ishex(buf[0])) return (VFP_Error(vc, "chunked header non-hex")); @@ -152,12 +152,14 @@ v1f_chunked_hdr(struct vfp_ctx *vc, struct http_conn *htc, ssize_t *szp) return (VFP_Error(vc, "chunked header too long")); /* Skip trailing white space */ - while (vct_islws(buf[u]) && buf[u] != '\n') { + while (vct_isows(buf[u])) { lr = v1f_read(vc, htc, buf + u, 1); if (lr <= 0) return (VFP_Error(vc, "chunked read err")); } + if (buf[u] == '\r' && v1f_read(vc, htc, buf + u, 1) <= 0) + return (VFP_Error(vc, "chunked read err")); if (buf[u] != '\n') return (VFP_Error(vc, "chunked header no NL")); From asadsa at varnish-software.com Mon May 12 12:57:06 2025 From: asadsa at varnish-software.com (Asad Sajjad Ahmed) Date: Mon, 12 May 2025 12:57:06 +0000 (UTC) Subject: [master] 7d6cfa384 vtc: Test coverage for VSV16 Message-ID: <20250512125706.94EFE11E29A@lists.varnish-cache.org> commit 7d6cfa384d7296646e73c80e572fbe182274d701 Author: Dridi Boukelmoune Date: Thu Apr 3 15:52:56 2025 +0200 vtc: Test coverage for VSV16 diff --git a/bin/varnishtest/tests/f00016.vtc b/bin/varnishtest/tests/f00016.vtc new file mode 100644 index 000000000..a38b8b1ef --- /dev/null +++ b/bin/varnishtest/tests/f00016.vtc @@ -0,0 +1,69 @@ +varnishtest "Do not tolerate anything else than CRLF as chunked ending" + +server s0 { + rxreq + expect_close +} -dispatch + +varnish v1 -vcl+backend {} -start + +logexpect l1 -v v1 { + expect * 1001 FetchError "chunked tail no NL" + expect * 1004 FetchError "chunked tail no NL" + expect * 1007 FetchError "chunked header non-hex" + expect * 1010 FetchError "chunked header non-hex" +} -start + +client c1 { + non_fatal + txreq -req POST -hdr "Transfer-encoding: chunked" + send "1\r\n" + send "This is more than one byte of data\r\n" + send "0\r\n" + send "\r\n" + fatal + rxresp + expect resp.status == 503 + expect_close +} -run + +client c2 { + non_fatal + txreq -req POST -hdr "Transfer-encoding: chunked" + send "1\r\n" + send "Z 2\r\n" + send "3d\r\n" + send "0\r\n\r\nPOST /evil HTTP/1.1\r\nHost: whatever\r\nContent-Length: 5\r\n\r\n" + send "0\r\n" + send "\r\n" + fatal + rxresp + expect resp.status == 503 + expect_close +} -run + +client c3 { + non_fatal + txreq -req POST -hdr "Transfer-encoding: chunked" + send "d\r\n" + send "Spurious CRLF\r\n\r\n" + send "0\r\n" + send "\r\n" + fatal + rxresp + expect resp.status == 503 + expect_close +} -run + +client c4 { + non_fatal + txreq -req POST -hdr "Transfer-encoding: chunked" + send "\n0\r\n" + send "\r\n" + fatal + rxresp + expect resp.status == 503 + expect_close +} -run + +logexpect l1 -wait From walid.boudebouda at gmail.com Mon May 12 15:29:05 2025 From: walid.boudebouda at gmail.com (Walid Boudebouda) Date: Mon, 12 May 2025 15:29:05 +0000 (UTC) Subject: [7.6] 2c807185b doc: Document how vmodtool.py generates C symbol names Message-ID: <20250512152905.1BA775C9D@lists.varnish-cache.org> commit 2c807185b5bc63f20d7335e8a0a1d1799de143c5 Author: Nils Goroll Date: Thu Apr 17 12:11:33 2025 +0200 doc: Document how vmodtool.py generates C symbol names in preparation of the changes from #4240 "write the documentation first" did not always apply in the past... diff --git a/doc/sphinx/reference/vmod.rst b/doc/sphinx/reference/vmod.rst index e8de061ec..38868e002 100644 --- a/doc/sphinx/reference/vmod.rst +++ b/doc/sphinx/reference/vmod.rst @@ -115,7 +115,7 @@ For the std VMOD, the compiled vcc_if.h file looks like this:: vmod_event_f event_function; Those are your C prototypes. Notice the ``vmod_`` prefix on the -function names. +function names, more on that in :ref:`ref-vmod-symbols`. Named arguments and default values ---------------------------------- @@ -506,6 +506,51 @@ VOID Can only be used for return-value, which makes the function a VCL procedure. +.. _ref-vmod-symbols: + +C symbols +========= + +Through generation of ``vcc_if.h``, ``vmodtool.py`` pre-defines the names of +most symbols on the C side of the vmod interface, namely: + +* function names as *_* + +* event handler names as *_* + +* method names as *__*, with two special methods named + + * ``_init`` for the constructor and + * ``_fini`` for the destructor + +* class struct names as *__* + +* argument struct names for support of optional arguments as + *arg___* for functions and + *arg____* for methods, with member names + + * *valid_* for the flag of optional arguments being present and + * ** for the argument name + +* enum values as *enum___* + +For the above, the ** placeholders are defined as: + +** + The ``$Prefix`` stanza value, if defined in the ``.vcc`` file, or + ``vmod`` by default. + +** + The vmod name fro the ``$Module`` stanza of the ``.vcc`` file. + +** + The function or method argument name + +The other placeholders should be self-explanatory as the name of the respective +function, class, method or handler name. + +In summary, only some symbol names (those with **) can be influenced by +the vmod author. .. _ref-vmod-private-pointers: From walid.boudebouda at gmail.com Mon May 12 15:29:05 2025 From: walid.boudebouda at gmail.com (Walid Boudebouda) Date: Mon, 12 May 2025 15:29:05 +0000 (UTC) Subject: [7.6] abc04dea0 vcc: refactor passing the function's cname to argument expressions Message-ID: <20250512152905.312BE5CA2@lists.varnish-cache.org> commit abc04dea042fe9cc65264f14357e02308342025d Author: Nils Goroll Date: Wed Dec 4 13:21:44 2024 +0100 vcc: refactor passing the function's cname to argument expressions We had (ab)used the cname member of struct func_arg to pass the name of the function to have it available for constructing the C name for enum arguments. Make this explicit diff --git a/lib/libvcc/vcc_expr.c b/lib/libvcc/vcc_expr.c index 7475053aa..15f03626f 100644 --- a/lib/libvcc/vcc_expr.c +++ b/lib/libvcc/vcc_expr.c @@ -453,20 +453,20 @@ struct func_arg { VTAILQ_ENTRY(func_arg) list; }; -static void -vcc_do_enum(struct vcc *tl, struct func_arg *fa, int len, const char *ptr) +static struct expr * +vcc_do_enum(struct vcc *tl, const char *cfunc, int len, const char *ptr) { const char *r; (void)tl; - r = strchr(fa->cname, '.'); + r = strchr(cfunc, '.'); AN(r); - fa->result = vcc_mk_expr(VOID, "*%.*s.enum_%.*s", - (int)(r - fa->cname), fa->cname, len, ptr); + return (vcc_mk_expr(VOID, "*%.*s.enum_%.*s", + (int)(r - cfunc), cfunc, len, ptr)); } static void -vcc_do_arg(struct vcc *tl, struct func_arg *fa) +vcc_do_arg(struct vcc *tl, const char *cfunc, struct func_arg *fa) { struct expr *e2; struct vjsn_val *vv; @@ -485,7 +485,7 @@ vcc_do_arg(struct vcc *tl, struct func_arg *fa) vcc_ErrWhere(tl, tl->t); return; } - vcc_do_enum(tl, fa, PF(tl->t)); + fa->result = vcc_do_enum(tl, cfunc, PF(tl->t)); SkipToken(tl, ID); } else { if (fa->type == SUB) @@ -551,7 +551,6 @@ vcc_func(struct vcc *tl, struct expr **e, const void *priv, assert(vjsn_is_array(vv)); fa = calloc(1, sizeof *fa); AN(fa); - fa->cname = cfunc; VTAILQ_INSERT_TAIL(&head, fa, list); vvp = VTAILQ_FIRST(&vv->children); @@ -594,7 +593,7 @@ vcc_func(struct vcc *tl, struct expr **e, const void *priv, if (t1->tok == '=') break; } - vcc_do_arg(tl, fa); + vcc_do_arg(tl, cfunc, fa); if (tl->err) VSB_printf(tl->sb, "Expected argument: %s %s\n\n", fa->type->name, @@ -626,7 +625,7 @@ vcc_func(struct vcc *tl, struct expr **e, const void *priv, } vcc_NextToken(tl); SkipToken(tl, '='); - vcc_do_arg(tl, fa); + vcc_do_arg(tl, cfunc, fa); ERRCHK(tl); if (tl->t->tok == ')') break; @@ -649,7 +648,7 @@ vcc_func(struct vcc *tl, struct expr **e, const void *priv, e1 = vcc_expr_edit(tl, e1->fmt, ssa, e1, NULL); } if (fa->result == NULL && fa->type == ENUM && fa->val != NULL) - vcc_do_enum(tl, fa, strlen(fa->val), fa->val); + fa->result = vcc_do_enum(tl, cfunc, strlen(fa->val), fa->val); if (fa->result == NULL && fa->val != NULL) fa->result = vcc_mk_expr(fa->type, "%s", fa->val); if (fa->result != NULL && sa != NULL) { From walid.boudebouda at gmail.com Mon May 12 15:29:05 2025 From: walid.boudebouda at gmail.com (Walid Boudebouda) Date: Mon, 12 May 2025 15:29:05 +0000 (UTC) Subject: [7.6] 69259780b vmodtool: Add option to specify c names of arguments and avoid "bool" Message-ID: <20250512152905.5312C5CA6@lists.varnish-cache.org> commit 69259780b790fe287c6dad79d412dd1ab1526ec2 Author: Nils Goroll Date: Tue Dec 3 18:32:29 2024 +0100 vmodtool: Add option to specify c names of arguments and avoid "bool" I tried gcc version 15.0.0 20241203 and even without -std=c23, it would reserve "bool": In file included from vcc_std_if.c:10: vcc_std_if.h:78:33: error: two or more data types in declaration specifiers 78 | VCL_BOOL bool; | ^~~~ (and more) So this patch adds the option to specify the c name of arguments to vmod functions/methods by separating it with a colon in the spec in the VCC file. vclname:cname In the next commit, we use this facility to rename the "bool" argument to vmod_std functions to "boolean". Implementation -------------- The cname needs to be specified by the VMOD author because it is also used in the vmod implementation. Obviously, VCC needs to know that name, too, to generate argument structs, so we need to transport it from the VMOD to VCC via the JSON spec. A logical place for cname is next to the name, because the other attributes following in the argument spec array are optional. So this changes the JSON spec format, and, consequently, we need to increase the version number. So, ultimately, this is a breaking change with respect to vmodtool: One can not import vmods built before this change. We could add compatibility for this case, but as we have a tradition of forcing rebuilds with each release by bumping the VRT major number anyway, I did not see my time well spent on implementing backwards compatibility. Groundwork for the fix of #4294 diff --git a/bin/varnishtest/tests/m00003.vtc b/bin/varnishtest/tests/m00003.vtc index 635b3f0aa..eafd30197 100644 --- a/bin/varnishtest/tests/m00003.vtc +++ b/bin/varnishtest/tests/m00003.vtc @@ -59,12 +59,15 @@ varnish v1 -errvcl {Not string[2]} { import wrong; } filewrite ${tmpdir}/libvmod_wrong.so "VMOD_JSON_SPEC\x02" "[[\"$VBLA\"]]" "\x03" varnish v1 -errvcl {Not $VMOD[3]} { import wrong; } +filewrite ${tmpdir}/libvmod_wrong.so "VMOD_JSON_SPEC\x02" "[[\"$VMOD\",\"1.0\"]]" "\x03" +varnish v1 -errvcl {Syntax != 2.0} { import wrong; } + filewrite ${tmpdir}/libvmod_wrong.so "VMOD_JSON_SPEC\x02" filewrite -a ${tmpdir}/libvmod_wrong.so { [ [ "$VMOD", - "1.0", + "2.0", "wrong", "Vmod_vmod_wrong_Func", "0000000000000000000000000000000000000000000000000000000000000000", @@ -82,7 +85,7 @@ filewrite -a ${tmpdir}/libvmod_wrong.so { [ [ "$VMOD", - "1.0", + "2.0", "wrong", "Vmod_vmod_wrong_Func", "0000000000000000000000000000000000000000000000000000000000000000", @@ -103,7 +106,7 @@ filewrite -a ${tmpdir}/libvmod_wrong.so { [ [ "$VMOD", - "1.0", + "2.0", "wrong", "Vmod_vmod_wrong_Func", "0000000000000000000000000000000000000000000000000000000000000000", @@ -123,7 +126,7 @@ filewrite -a ${tmpdir}/libvmod_wrong.so { [ [ "$VMOD", - "1.0", + "2.0", "wrong", "Vmod_vmod_wrong_Func", "0000000000000000000000000000000000000000000000000000000000000000", @@ -141,7 +144,7 @@ filewrite -a ${tmpdir}/libvmod_wrong.so { [ [ "$VMOD", - "1.0", + "2.0", "wrong", "Vmod_vmod_wrong_Func", "0000000000000000000000000000000000000000000000000000000000000000", @@ -163,7 +166,7 @@ filewrite -a ${tmpdir}/libvmod_wrong.so { [ [ "$VMOD", - "1.0", + "2.0", "std", "Vmod_vmod_std_Func", "0000000000000000000000000000000000000000000000000000000000000000", diff --git a/bin/varnishtest/tests/m00055.vtc b/bin/varnishtest/tests/m00055.vtc index 7d17010aa..183fed0a3 100644 --- a/bin/varnishtest/tests/m00055.vtc +++ b/bin/varnishtest/tests/m00055.vtc @@ -16,7 +16,7 @@ filewrite -a ${tmpdir}/libvmod_wrong.so { [ [ "$VMOD", - "1.0", + "2.0", "wrong", "Vmod_vmod_wrong_Func", "0000000000000000000000000000000000000000000000000000000000000000", diff --git a/doc/sphinx/reference/vmod.rst b/doc/sphinx/reference/vmod.rst index 38868e002..5039830d0 100644 --- a/doc/sphinx/reference/vmod.rst +++ b/doc/sphinx/reference/vmod.rst @@ -190,6 +190,9 @@ declarations: with `n` starting at 1 and incrementing with the argument's position. +Optionally, the VCL and C argument names can be specified independently using +the ``:`` syntax. See :ref:`ref-vmod-symbols` for details. + .. _ref-vmod-vcl-c-objects: Objects and methods @@ -544,13 +547,14 @@ For the above, the ** placeholders are defined as: The vmod name fro the ``$Module`` stanza of the ``.vcc`` file. ** - The function or method argument name + The function or method argument *cname* or, if not given, *vclname* + as specified using the *:* syntax. The other placeholders should be self-explanatory as the name of the respective -function, class, method or handler name. +function, class, method or handler. -In summary, only some symbol names (those with **) can be influenced by -the vmod author. +In summary, symbol names can either be influenced by the vmod author globally +using ``$Prefix``, or using the *:* syntax for argument names. .. _ref-vmod-private-pointers: diff --git a/lib/libvcc/vcc_expr.c b/lib/libvcc/vcc_expr.c index 15f03626f..c4fcf397c 100644 --- a/lib/libvcc/vcc_expr.c +++ b/lib/libvcc/vcc_expr.c @@ -558,7 +558,7 @@ vcc_func(struct vcc *tl, struct expr **e, const void *priv, fa->result = vcc_priv_arg(tl, vvp->value, sym); vvp = VTAILQ_NEXT(vvp, list); if (vvp != NULL) - fa->name = vvp->value; + fa->cname = fa->name = vvp->value; continue; } fa->type = VCC_Type(vvp->value); @@ -567,6 +567,9 @@ vcc_func(struct vcc *tl, struct expr **e, const void *priv, if (vvp != NULL) { fa->name = vvp->value; vvp = VTAILQ_NEXT(vvp, list); + AN(vvp); /* vmod_syntax 2.0 */ + fa->cname = vvp->value; + vvp = VTAILQ_NEXT(vvp, list); if (vvp != NULL) { fa->val = vvp->value; vvp = VTAILQ_NEXT(vvp, list); @@ -642,9 +645,9 @@ vcc_func(struct vcc *tl, struct expr **e, const void *priv, VTAILQ_FOREACH_SAFE(fa, &head, list, fa2) { n++; if (fa->optional) { - AN(fa->name); + AN(fa->cname); bprintf(ssa, "\v1.valid_%s = %d,\n", - fa->name, fa->avail); + fa->cname, fa->avail); e1 = vcc_expr_edit(tl, e1->fmt, ssa, e1, NULL); } if (fa->result == NULL && fa->type == ENUM && fa->val != NULL) @@ -652,8 +655,8 @@ vcc_func(struct vcc *tl, struct expr **e, const void *priv, if (fa->result == NULL && fa->val != NULL) fa->result = vcc_mk_expr(fa->type, "%s", fa->val); if (fa->result != NULL && sa != NULL) { - if (fa->name) - bprintf(ssa, "\v1.%s = \v2,\n", fa->name); + if (fa->cname) + bprintf(ssa, "\v1.%s = \v2,\n", fa->cname); else bprintf(ssa, "\v1.arg%d = \v2,\n", n); e1 = vcc_expr_edit(tl, e1->fmt, ssa, e1, fa->result); diff --git a/lib/libvcc/vcc_vmod.c b/lib/libvcc/vcc_vmod.c index 7e7b3b8af..6fe8b0450 100644 --- a/lib/libvcc/vcc_vmod.c +++ b/lib/libvcc/vcc_vmod.c @@ -162,7 +162,8 @@ vcc_ParseJSON(const struct vcc *tl, const char *jsn, struct vmod_import *vim) AN(vv3); assert(vjsn_is_string(vv3)); vim->vmod_syntax = strtod(vv3->value, NULL); - assert (vim->vmod_syntax == 1.0); + if (vim->vmod_syntax != 2.0) + return ("Syntax != 2.0"); vv3 = VTAILQ_NEXT(vv3, list); AN(vv3); diff --git a/lib/libvcc/vmodtool.py b/lib/libvcc/vmodtool.py index 2d3d94640..3235dd2c5 100755 --- a/lib/libvcc/vmodtool.py +++ b/lib/libvcc/vmodtool.py @@ -27,6 +27,10 @@ # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY # OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF # SUCH DAMAGE. +# +# syntax version history: +# 1.0: initial +# 2.0: added cname (nm2) after argument name """ Read the first existing file from arguments or vmod.vcc and produce: @@ -319,7 +323,7 @@ class arg(CType): self.defval = x def jsonproto(self, jl): - jl.append([self.vt, self.nm, self.defval, self.spec]) + jl.append([self.vt, self.nm, self.nm2, self.defval, self.spec]) if self.opt: jl[-1].append(True) while jl[-1][-1] is None: @@ -385,6 +389,8 @@ class ProtoType(): err("arguments cannot be of type '%s'" % t.vt, warn=False) if t.nm is None: t.nm2 = "arg%d" % n + elif ':' in t.nm: + [t.nm, t.nm2] = t.nm.split(':') else: t.nm2 = t.nm self.args.append(t) @@ -466,8 +472,8 @@ class ProtoType(): s = "\n" + self.argstructname() + " {\n" for i in self.args: if i.opt: - assert i.nm is not None - s += "\tchar\t\t\tvalid_%s;\n" % i.nm + assert i.nm2 is not None + s += "\tchar\t\t\tvalid_%s;\n" % i.nm2 for i in self.args: s += "\t" + i.ct if len(i.ct) < 8: @@ -1146,7 +1152,7 @@ class vcc(): def iter_json(self, fnx): - jl = [["$VMOD", "1.0", self.modname, self.csn, self.file_id]] + jl = [["$VMOD", "2.0", self.modname, self.csn, self.file_id]] jl.append(["$CPROTO"]) for i in open(fnx): jl[-1].append(i.rstrip()) From walid.boudebouda at gmail.com Mon May 12 15:29:05 2025 From: walid.boudebouda at gmail.com (Walid Boudebouda) Date: Mon, 12 May 2025 15:29:05 +0000 (UTC) Subject: [7.6] e0d69f998 vmod_std: Use new : syntax to avoid reserved C symbol "bool" Message-ID: <20250512152905.6A37A5CAE@lists.varnish-cache.org> commit e0d69f998b149dcf3e0f0974306ccb27c6a41b02 Author: Nils Goroll Date: Thu Apr 17 15:26:23 2025 +0200 vmod_std: Use new : syntax to avoid reserved C symbol "bool" Fixes #4294 diff --git a/vmod/vmod_std.vcc b/vmod/vmod_std.vcc index b5ab2a8aa..8da9055e6 100644 --- a/vmod/vmod_std.vcc +++ b/vmod/vmod_std.vcc @@ -308,7 +308,7 @@ Example:: std.cache_req_body(std.bytes(real=10.0*1024)); $Function INT integer([STRING s], [INT fallback], - [BOOL bool], [BYTES bytes], [DURATION duration], [REAL real], + [BOOL bool:boolean], [BYTES bytes], [DURATION duration], [REAL real], [TIME time]) Returns an INT from a STRING, BOOL or other quantity. @@ -376,7 +376,7 @@ Example:: } $Function REAL real([STRING s], [REAL fallback], [INT integer], - [BOOL bool], [BYTES bytes], [DURATION duration], + [BOOL bool:boolean], [BYTES bytes], [DURATION duration], [TIME time]) Returns a REAL from a STRING, BOOL or other quantity. diff --git a/vmod/vmod_std_conversions.c b/vmod/vmod_std_conversions.c index c45410114..f8ef8d936 100644 --- a/vmod/vmod_std_conversions.c +++ b/vmod/vmod_std_conversions.c @@ -149,15 +149,15 @@ vmod_integer(VRT_CTX, struct VARGS(integer) *a) CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC); - nargs = a->valid_s + a->valid_bool + a->valid_bytes + + nargs = a->valid_s + a->valid_boolean + a->valid_bytes + a->valid_duration + a->valid_real + a->valid_time; if (!onearg(ctx, "integer", nargs)) return (0); r = NAN; - if (a->valid_bool) - return (a->bool ? 1 : 0); + if (a->valid_boolean) + return (a->boolean ? 1 : 0); if (a->valid_bytes) return (a->bytes); @@ -245,7 +245,7 @@ vmod_real(VRT_CTX, struct VARGS(real) *a) CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC); - nargs = a->valid_s + a->valid_integer + a->valid_bool + a->valid_bytes + + nargs = a->valid_s + a->valid_integer + a->valid_boolean + a->valid_bytes + a->valid_duration + a->valid_time; if (!onearg(ctx, "real", nargs)) @@ -254,8 +254,8 @@ vmod_real(VRT_CTX, struct VARGS(real) *a) if (a->valid_integer) return ((VCL_REAL)a->integer); - if (a->valid_bool) - return ((VCL_REAL)(a->bool ? 1 : 0)); + if (a->valid_boolean) + return ((VCL_REAL)(a->boolean ? 1 : 0)); if (a->valid_bytes) return ((VCL_REAL)a->bytes); From walid.boudebouda at gmail.com Mon May 12 15:29:05 2025 From: walid.boudebouda at gmail.com (Walid Boudebouda) Date: Mon, 12 May 2025 15:29:05 +0000 (UTC) Subject: [7.6] 8927e63c7 circleci: Install awk on Fedora Message-ID: <20250512152905.829285CB2@lists.varnish-cache.org> commit 8927e63c7bd97d6fcb69e1a598a7b4860bceef02 Author: Guillaume Quintard Date: Wed Apr 16 01:20:14 2025 -0700 circleci: Install awk on Fedora It's no longer present since the fedora:42 base image. diff --git a/.circleci/Dockerfile b/.circleci/Dockerfile index 9ef231e0f..f2196d253 100644 --- a/.circleci/Dockerfile +++ b/.circleci/Dockerfile @@ -2,6 +2,7 @@ FROM fedora-latest RUN set -e; \ dnf -y install \ + awk \ automake \ git \ jemalloc-devel \ diff --git a/.circleci/config.yml b/.circleci/config.yml index 38bffa161..80d2aeae0 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -36,6 +36,7 @@ jobs: name: Install deps command: | dnf -y install \ + awk \ automake \ jemalloc-devel \ git \ @@ -245,6 +246,7 @@ jobs: ;; fedora:*) dnf -y group install development-tools + dnf -y install awk ;; esac dnf -y install \ From walid.boudebouda at gmail.com Mon May 12 15:29:05 2025 From: walid.boudebouda at gmail.com (Walid Boudebouda) Date: Mon, 12 May 2025 15:29:05 +0000 (UTC) Subject: [7.6] 0a76ab174 vmod_blob: Account for NUL to simplify alphabet initialization Message-ID: <20250512152905.98B805CB8@lists.varnish-cache.org> commit 0a76ab174f10c210e2c0544c935b696d5e3ac244 Author: Nils Goroll Date: Wed Mar 19 16:31:23 2025 +0100 vmod_blob: Account for NUL to simplify alphabet initialization Alphabets for base64 and hex are 64 and 16 bytes long, respectively, but initializing them with a string needs one additional byte for the final NUL. Fixes #4300 diff --git a/vmod/vmod_blob.h b/vmod/vmod_blob.h index 90872b61a..f775dd6c9 100644 --- a/vmod/vmod_blob.h +++ b/vmod/vmod_blob.h @@ -140,7 +140,7 @@ len_f hex_decode_l; encode_f hex_encode; decode_f hex_decode; -extern const char hex_alphabet[][16]; +extern const char hex_alphabet[][17]; extern const uint8_t hex_nibble[]; /* url.c */ diff --git a/vmod/vmod_blob_base64.c b/vmod/vmod_blob_base64.c index 74a895754..971a05e91 100644 --- a/vmod/vmod_blob_base64.c +++ b/vmod/vmod_blob_base64.c @@ -37,7 +37,7 @@ #include "vmod_blob.h" static const struct b64_alphabet { - const char b64[64]; + const char b64[65]; const int8_t i64[256]; const int padding; } b64_alphabet[] = { diff --git a/vmod/vmod_blob_hex.c b/vmod/vmod_blob_hex.c index 539ab87a7..b11701940 100644 --- a/vmod/vmod_blob_hex.c +++ b/vmod/vmod_blob_hex.c @@ -38,7 +38,7 @@ #include "vmod_blob.h" -const char hex_alphabet[][16] = { +const char hex_alphabet[][17] = { "0123456789abcdef", "0123456789ABCDEF" }; From walid.boudebouda at gmail.com Mon May 12 15:29:05 2025 From: walid.boudebouda at gmail.com (Walid Boudebouda) Date: Mon, 12 May 2025 15:29:05 +0000 (UTC) Subject: [7.6] 6dd6607c9 v1f: Read end-of-chunk as part of the chunk Message-ID: <20250512152905.B0F555CBD@lists.varnish-cache.org> commit 6dd6607c93af5a9cc663a174aefc3a0aeff8f26c Author: Nils Goroll Date: Mon May 30 13:09:11 2022 +0200 v1f: Read end-of-chunk as part of the chunk Until now, we read the (CR)?LF at the end of a chunk as part of the next chunk header (see: /* Skip leading whitespace */). For a follow up commit, we are going to want to know if the next chunk header is available for read, so we now consume the chunk end as part of the chunk itself. This also fixes a corner case: We previously accepted chunks with a missing end-of-chunk (see fix of r01729.vtc). Ref: https://datatracker.ietf.org/doc/html/rfc7230#section-4.1 diff --git a/bin/varnishd/http1/cache_http1_vfp.c b/bin/varnishd/http1/cache_http1_vfp.c index 20f349d1c..aceb5a628 100644 --- a/bin/varnishd/http1/cache_http1_vfp.c +++ b/bin/varnishd/http1/cache_http1_vfp.c @@ -89,6 +89,24 @@ v1f_read(const struct vfp_ctx *vc, struct http_conn *htc, void *d, ssize_t len) } +/*-------------------------------------------------------------------- + * read (CR)?LF at the end of a chunk + */ +static enum vfp_status +v1f_chunk_end(struct vfp_ctx *vc, struct http_conn *htc) +{ + char c; + + if (v1f_read(vc, htc, &c, 1) <= 0) + return (VFP_Error(vc, "chunked read err")); + if (c == '\r' && v1f_read(vc, htc, &c, 1) <= 0) + return (VFP_Error(vc, "chunked read err")); + if (c != '\n') + return (VFP_Error(vc, "chunked tail no NL")); + return (VFP_OK); +} + + /*-------------------------------------------------------------------- * Read a chunked HTTP object. * @@ -99,6 +117,7 @@ static enum vfp_status v_matchproto_(vfp_pull_f) v1f_chunked_pull(struct vfp_ctx *vc, struct vfp_entry *vfe, void *ptr, ssize_t *lp) { + static enum vfp_status vfps; struct http_conn *htc; char buf[20]; /* XXX: 20 is arbitrary */ char *q; @@ -168,18 +187,15 @@ v1f_chunked_pull(struct vfp_ctx *vc, struct vfp_entry *vfe, void *ptr, return (VFP_Error(vc, "chunked insufficient bytes")); *lp = lr; vfe->priv2 -= lr; - if (vfe->priv2 == 0) - vfe->priv2 = -1; - return (VFP_OK); + if (vfe->priv2 != 0) + return (VFP_OK); + + vfe->priv2 = -1; + return (v1f_chunk_end(vc, htc)); } AZ(vfe->priv2); - if (v1f_read(vc, htc, buf, 1) <= 0) - return (VFP_Error(vc, "chunked read err")); - if (buf[0] == '\r' && v1f_read(vc, htc, buf, 1) <= 0) - return (VFP_Error(vc, "chunked read err")); - if (buf[0] != '\n') - return (VFP_Error(vc, "chunked tail no NL")); - return (VFP_END); + vfps = v1f_chunk_end(vc, htc); + return (vfps == VFP_OK ? VFP_END : vfps); } static const struct vfp v1f_chunked = { diff --git a/bin/varnishtest/tests/r01184.vtc b/bin/varnishtest/tests/r01184.vtc index 0988e65a3..94ecd3c23 100644 --- a/bin/varnishtest/tests/r01184.vtc +++ b/bin/varnishtest/tests/r01184.vtc @@ -62,6 +62,7 @@ server s1 { sendhex " 10 45 f3 a9 83 b8 18 1c 7b c2 30 55 04 17 13 c4" sendhex " 0f 07 5f 7a 38 f4 8e 50 b3 37 d4 3a 32 4a 34 07" sendhex " FF FF FF FF FF FF FF FF 72 ea 06 5f b3 1c fa dd" + send "\n" expect_close } -start @@ -93,6 +94,7 @@ server s1 { sendhex " 10 45 f3 a9 83 b8 18 1c 7b c2 30 55 04 17 13 c4" sendhex " 0f 07 5f 7a 38 f4 8e 50 b3 37 d4 3a 32 4a 34 07" sendhex " FF FF FF FF FF FF FF FF 72 ea 06 5f b3 1c fa dd" + send "\n" expect_close } -start diff --git a/bin/varnishtest/tests/r01506.vtc b/bin/varnishtest/tests/r01506.vtc index 96b7b54c9..f7f89a716 100644 --- a/bin/varnishtest/tests/r01506.vtc +++ b/bin/varnishtest/tests/r01506.vtc @@ -7,15 +7,15 @@ server s0 { txresp -nolen \ -hdr "Transfer-Encoding: chunked" \ -hdr "Connection: close" - send "11\r\n0_23456789abcdef\n" - send "11\r\n1_23456789abcdef\n" - send "11\r\n2_23456789abcdef\n" - send "11\r\n3_23456789abcdef\n" + send "11\r\n0_23456789abcdef\n\n" + send "11\r\n1_23456789abcdef\n\n" + send "11\r\n2_23456789abcdef\n\n" + send "11\r\n3_23456789abcdef\n\n" barrier b1 sync - send "11\r\n4_23456789abcdef\n" - send "11\r\n5_23456789abcdef\n" - send "11\r\n6_23456789abcdef\n" - send "11\r\n7_23456789abcdef\n" + send "11\r\n4_23456789abcdef\n\n" + send "11\r\n5_23456789abcdef\n\n" + send "11\r\n6_23456789abcdef\n\n" + send "11\r\n7_23456789abcdef\n\n" chunkedlen 0 } -dispatch diff --git a/bin/varnishtest/tests/r01729.vtc b/bin/varnishtest/tests/r01729.vtc index 883a60cc6..f6a01e976 100644 --- a/bin/varnishtest/tests/r01729.vtc +++ b/bin/varnishtest/tests/r01729.vtc @@ -11,7 +11,7 @@ server s1 { send "\r\n" send "14\r\n" send "0123456789" - send "0123456789" + send "0123456789\n" send "0\r\n" send "\r\n" @@ -29,7 +29,7 @@ client c1 { send "\r\n" send "14\r\n" send "0123456789" - send "0123456789" + send "0123456789\n" send "0\r\n" send "\r\n" @@ -45,7 +45,7 @@ client c1 { send "\r\n" send "14\r\n" send "0123456789" - send "0123456789" + send "0123456789\n" send "0\r\n" send "\r\n" From walid.boudebouda at gmail.com Mon May 12 15:29:05 2025 From: walid.boudebouda at gmail.com (Walid Boudebouda) Date: Mon, 12 May 2025 15:29:05 +0000 (UTC) Subject: [7.6] 383278e98 v1f: pull chunk header parsing into an own function Message-ID: <20250512152905.C8B245CC1@lists.varnish-cache.org> commit 383278e98fa4e74c0f396431449bb4bd729b563d Author: Nils Goroll Date: Mon May 30 13:42:58 2022 +0200 v1f: pull chunk header parsing into an own function ... which we are going to need in a follow up commit. No functional changes, diff best viewed with -b diff --git a/bin/varnishd/http1/cache_http1_vfp.c b/bin/varnishd/http1/cache_http1_vfp.c index aceb5a628..d684f1043 100644 --- a/bin/varnishd/http1/cache_http1_vfp.c +++ b/bin/varnishd/http1/cache_http1_vfp.c @@ -108,76 +108,98 @@ v1f_chunk_end(struct vfp_ctx *vc, struct http_conn *htc) /*-------------------------------------------------------------------- - * Read a chunked HTTP object. + * Parse a chunk header and, for VFP_OK, return size in a pointer * * XXX: Reading one byte at a time is pretty pessimal. */ -static enum vfp_status v_matchproto_(vfp_pull_f) -v1f_chunked_pull(struct vfp_ctx *vc, struct vfp_entry *vfe, void *ptr, - ssize_t *lp) +static enum vfp_status +v1f_chunked_hdr(struct vfp_ctx *vc, struct http_conn *htc, ssize_t *szp) { - static enum vfp_status vfps; - struct http_conn *htc; char buf[20]; /* XXX: 20 is arbitrary */ - char *q; unsigned u; uintmax_t cll; - ssize_t cl, l, lr; + ssize_t cl, lr; + char *q; CHECK_OBJ_NOTNULL(vc, VFP_CTX_MAGIC); - CHECK_OBJ_NOTNULL(vfe, VFP_ENTRY_MAGIC); - CAST_OBJ_NOTNULL(htc, vfe->priv1, HTTP_CONN_MAGIC); - AN(ptr); - AN(lp); + CHECK_OBJ_NOTNULL(htc, HTTP_CONN_MAGIC); + AN(szp); + assert(*szp == -1); - l = *lp; - *lp = 0; - if (vfe->priv2 == -1) { - /* Skip leading whitespace */ - do { - lr = v1f_read(vc, htc, buf, 1); - if (lr <= 0) - return (VFP_Error(vc, "chunked read err")); - } while (vct_islws(buf[0])); - - if (!vct_ishex(buf[0])) - return (VFP_Error(vc, "chunked header non-hex")); - - /* Collect hex digits, skipping leading zeros */ - for (u = 1; u < sizeof buf; u++) { - do { - lr = v1f_read(vc, htc, buf + u, 1); - if (lr <= 0) - return (VFP_Error(vc, "chunked read err")); - } while (u == 1 && buf[0] == '0' && buf[u] == '0'); - if (!vct_ishex(buf[u])) - break; - } + /* Skip leading whitespace */ + do { + lr = v1f_read(vc, htc, buf, 1); + if (lr <= 0) + return (VFP_Error(vc, "chunked read err")); + } while (vct_islws(buf[0])); - if (u >= sizeof buf) - return (VFP_Error(vc, "chunked header too long")); + if (!vct_ishex(buf[0])) + return (VFP_Error(vc, "chunked header non-hex")); - /* Skip trailing white space */ - while (vct_islws(buf[u]) && buf[u] != '\n') { + /* Collect hex digits, skipping leading zeros */ + for (u = 1; u < sizeof buf; u++) { + do { lr = v1f_read(vc, htc, buf + u, 1); if (lr <= 0) return (VFP_Error(vc, "chunked read err")); - } + } while (u == 1 && buf[0] == '0' && buf[u] == '0'); + if (!vct_ishex(buf[u])) + break; + } - if (buf[u] != '\n') - return (VFP_Error(vc, "chunked header no NL")); + if (u >= sizeof buf) + return (VFP_Error(vc, "chunked header too long")); + + /* Skip trailing white space */ + while (vct_islws(buf[u]) && buf[u] != '\n') { + lr = v1f_read(vc, htc, buf + u, 1); + if (lr <= 0) + return (VFP_Error(vc, "chunked read err")); + } - buf[u] = '\0'; + if (buf[u] != '\n') + return (VFP_Error(vc, "chunked header no NL")); - cll = strtoumax(buf, &q, 16); - if (q == NULL || *q != '\0') - return (VFP_Error(vc, "chunked header number syntax")); - cl = (ssize_t)cll; - if (cl < 0 || (uintmax_t)cl != cll) - return (VFP_Error(vc, "bogusly large chunk size")); + buf[u] = '\0'; - vfe->priv2 = cl; + cll = strtoumax(buf, &q, 16); + if (q == NULL || *q != '\0') + return (VFP_Error(vc, "chunked header number syntax")); + cl = (ssize_t)cll; + if (cl < 0 || (uintmax_t)cl != cll) + return (VFP_Error(vc, "bogusly large chunk size")); + + *szp = cl; + return (VFP_OK); +} + + +/*-------------------------------------------------------------------- + * Read a chunked HTTP object. + * + */ + +static enum vfp_status v_matchproto_(vfp_pull_f) +v1f_chunked_pull(struct vfp_ctx *vc, struct vfp_entry *vfe, void *ptr, + ssize_t *lp) +{ + static enum vfp_status vfps; + struct http_conn *htc; + ssize_t l, lr; + + CHECK_OBJ_NOTNULL(vc, VFP_CTX_MAGIC); + CHECK_OBJ_NOTNULL(vfe, VFP_ENTRY_MAGIC); + CAST_OBJ_NOTNULL(htc, vfe->priv1, HTTP_CONN_MAGIC); + AN(ptr); + AN(lp); + + l = *lp; + *lp = 0; + if (vfe->priv2 == -1) { + vfps = v1f_chunked_hdr(vc, htc, &vfe->priv2); + if (vfps != VFP_OK) + return (vfps); } if (vfe->priv2 > 0) { if (vfe->priv2 < l) From walid.boudebouda at gmail.com Mon May 12 15:29:05 2025 From: walid.boudebouda at gmail.com (Walid Boudebouda) Date: Mon, 12 May 2025 15:29:05 +0000 (UTC) Subject: [7.6] c7c470bf7 v1f: Parse CRLF separately from other white space Message-ID: <20250512152906.01DDB5CC9@lists.varnish-cache.org> commit c7c470bf73aa3e933fef75624c3d6f60208d0240 Author: Dridi Boukelmoune Date: Wed Apr 2 16:03:43 2025 +0200 v1f: Parse CRLF separately from other white space It's a little bit harder to follow the CRLF logic when it is intertwined with the skipped surrounding white space. diff --git a/bin/varnishd/http1/cache_http1_vfp.c b/bin/varnishd/http1/cache_http1_vfp.c index d684f1043..fbc3dcb8b 100644 --- a/bin/varnishd/http1/cache_http1_vfp.c +++ b/bin/varnishd/http1/cache_http1_vfp.c @@ -132,7 +132,7 @@ v1f_chunked_hdr(struct vfp_ctx *vc, struct http_conn *htc, ssize_t *szp) lr = v1f_read(vc, htc, buf, 1); if (lr <= 0) return (VFP_Error(vc, "chunked read err")); - } while (vct_islws(buf[0])); + } while (vct_isows(buf[0])); if (!vct_ishex(buf[0])) return (VFP_Error(vc, "chunked header non-hex")); @@ -152,12 +152,14 @@ v1f_chunked_hdr(struct vfp_ctx *vc, struct http_conn *htc, ssize_t *szp) return (VFP_Error(vc, "chunked header too long")); /* Skip trailing white space */ - while (vct_islws(buf[u]) && buf[u] != '\n') { + while (vct_isows(buf[u])) { lr = v1f_read(vc, htc, buf + u, 1); if (lr <= 0) return (VFP_Error(vc, "chunked read err")); } + if (buf[u] == '\r' && v1f_read(vc, htc, buf + u, 1) <= 0) + return (VFP_Error(vc, "chunked read err")); if (buf[u] != '\n') return (VFP_Error(vc, "chunked header no NL")); From walid.boudebouda at gmail.com Mon May 12 15:29:06 2025 From: walid.boudebouda at gmail.com (Walid Boudebouda) Date: Mon, 12 May 2025 15:29:06 +0000 (UTC) Subject: [7.6] c1e6774ae vtc: Test coverage for VSV16 Message-ID: <20250512152906.1A3EB5CD6@lists.varnish-cache.org> commit c1e6774ae5b7e5fdd7aef7352170b1d6e7613070 Author: Dridi Boukelmoune Date: Thu Apr 3 15:52:56 2025 +0200 vtc: Test coverage for VSV16 diff --git a/bin/varnishtest/tests/f00016.vtc b/bin/varnishtest/tests/f00016.vtc new file mode 100644 index 000000000..a38b8b1ef --- /dev/null +++ b/bin/varnishtest/tests/f00016.vtc @@ -0,0 +1,69 @@ +varnishtest "Do not tolerate anything else than CRLF as chunked ending" + +server s0 { + rxreq + expect_close +} -dispatch + +varnish v1 -vcl+backend {} -start + +logexpect l1 -v v1 { + expect * 1001 FetchError "chunked tail no NL" + expect * 1004 FetchError "chunked tail no NL" + expect * 1007 FetchError "chunked header non-hex" + expect * 1010 FetchError "chunked header non-hex" +} -start + +client c1 { + non_fatal + txreq -req POST -hdr "Transfer-encoding: chunked" + send "1\r\n" + send "This is more than one byte of data\r\n" + send "0\r\n" + send "\r\n" + fatal + rxresp + expect resp.status == 503 + expect_close +} -run + +client c2 { + non_fatal + txreq -req POST -hdr "Transfer-encoding: chunked" + send "1\r\n" + send "Z 2\r\n" + send "3d\r\n" + send "0\r\n\r\nPOST /evil HTTP/1.1\r\nHost: whatever\r\nContent-Length: 5\r\n\r\n" + send "0\r\n" + send "\r\n" + fatal + rxresp + expect resp.status == 503 + expect_close +} -run + +client c3 { + non_fatal + txreq -req POST -hdr "Transfer-encoding: chunked" + send "d\r\n" + send "Spurious CRLF\r\n\r\n" + send "0\r\n" + send "\r\n" + fatal + rxresp + expect resp.status == 503 + expect_close +} -run + +client c4 { + non_fatal + txreq -req POST -hdr "Transfer-encoding: chunked" + send "\n0\r\n" + send "\r\n" + fatal + rxresp + expect resp.status == 503 + expect_close +} -run + +logexpect l1 -wait From walid.boudebouda at gmail.com Mon May 12 15:29:06 2025 From: walid.boudebouda at gmail.com (Walid Boudebouda) Date: Mon, 12 May 2025 15:29:06 +0000 (UTC) Subject: [7.6] 2a916fd1d changes: Populate changelog for 7.6.3 Message-ID: <20250512152906.46B945CE2@lists.varnish-cache.org> commit 2a916fd1d13b584c867a176fae64edf4fd663c6f Author: Walid Boudebouda Date: Mon May 12 13:10:04 2025 +0200 changes: Populate changelog for 7.6.3 diff --git a/doc/changes.rst b/doc/changes.rst index e1af49eb6..5f08fa4f8 100644 --- a/doc/changes.rst +++ b/doc/changes.rst @@ -34,6 +34,19 @@ http://varnish-cache.org/docs/trunk/whats-new/index.html and via individual releases. These documents are updated as part of the release process. +================================ +Varnish-Cache 7.6.3 (unreleased) +================================ + +.. _VSV00016: https://varnish-cache.org/security/VSV00016.html + +* We now check for CRLF after chunked body in HTTP/1. (VSV00016_) + +.. _4240: https://github.com/varnishcache/varnish-cache/pull/4240 + +* Add option to specify c names of arguments and avoid "bool". This makes it + possible to build VMODs using reserved names in the C language. (4240_) + ================================ Varnish Cache 7.6.2 (2025-03-17) ================================ From walid.boudebouda at gmail.com Mon May 12 15:29:06 2025 From: walid.boudebouda at gmail.com (Walid Boudebouda) Date: Mon, 12 May 2025 15:29:06 +0000 (UTC) Subject: [7.6] aece3c402 Prepare for 7.6.3 Message-ID: <20250512152906.6F6A65CF7@lists.varnish-cache.org> commit aece3c402a8406818638b69e1fc120679f9cd81e Author: Walid Boudebouda Date: Mon May 12 13:10:48 2025 +0200 Prepare for 7.6.3 diff --git a/configure.ac b/configure.ac index e861a5f2b..3963ad7dc 100644 --- a/configure.ac +++ b/configure.ac @@ -3,7 +3,7 @@ AC_COPYRIGHT([Copyright (c) 2006 Verdens Gang AS Copyright (c) 2006-2025 Varnish Software Copyright 2010-2025 UPLEX - Nils Goroll Systemoptimierung]) AC_REVISION([$Id$]) -AC_INIT([Varnish], [7.6.2], [varnish-dev at varnish-cache.org]) +AC_INIT([Varnish], [7.6.3], [varnish-dev at varnish-cache.org]) AC_CONFIG_SRCDIR(include/miniobj.h) AC_CONFIG_HEADERS([config.h]) AC_CONFIG_MACRO_DIR([m4]) diff --git a/doc/changes.rst b/doc/changes.rst index 5f08fa4f8..e6216d337 100644 --- a/doc/changes.rst +++ b/doc/changes.rst @@ -35,7 +35,7 @@ individual releases. These documents are updated as part of the release process. ================================ -Varnish-Cache 7.6.3 (unreleased) +Varnish-Cache 7.6.3 (2025-05-12) ================================ .. _VSV00016: https://varnish-cache.org/security/VSV00016.html From daghf at varnish-software.com Tue May 13 12:07:05 2025 From: daghf at varnish-software.com (Dag Haavi Finstad) Date: Tue, 13 May 2025 12:07:05 +0000 (UTC) Subject: [6.0] 471962b1c circleci: Install awk on Fedora Message-ID: <20250513120705.DC095103888@lists.varnish-cache.org> commit 471962b1c5ac82ff5f2a31ff571766f85bdd62c8 Author: Guillaume Quintard Date: Wed Apr 16 01:20:14 2025 -0700 circleci: Install awk on Fedora It's no longer present since the fedora:42 base image. Conflicts: .circleci/config.yml diff --git a/.circleci/Dockerfile b/.circleci/Dockerfile index c8cd03af5..0f8f9d220 100644 --- a/.circleci/Dockerfile +++ b/.circleci/Dockerfile @@ -2,6 +2,7 @@ FROM fedora-latest RUN set -e; \ dnf -y install \ + awk \ automake \ git \ jemalloc-devel \ diff --git a/.circleci/config.yml b/.circleci/config.yml index dd5524d9c..fe06d4fba 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -35,6 +35,7 @@ jobs: name: Install deps command: | dnf -y install \ + awk \ automake \ jemalloc-devel \ git \ @@ -243,6 +244,7 @@ jobs: ;; esac dnf -y install \ + awk \ cpio \ automake \ git \ From daghf at varnish-software.com Tue May 13 12:46:04 2025 From: daghf at varnish-software.com (Dag Haavi Finstad) Date: Tue, 13 May 2025 12:46:04 +0000 (UTC) Subject: [6.0] 7d46abdf9 cci: Fix alma:[89] Message-ID: <20250513124604.A26CF104FE6@lists.varnish-cache.org> commit 7d46abdf9c5a3f119f645c8de6d87efffe3889b8 Author: Dag Haavi Finstad Date: Tue May 13 14:45:28 2025 +0200 cci: Fix alma:[89] diff --git a/.circleci/config.yml b/.circleci/config.yml index fe06d4fba..cbb8dcbb7 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -242,9 +242,11 @@ jobs: dnf -y install diffutils dnf -y install epel-release ;; + fedora:*) + dnf -y install awk + ;; esac dnf -y install \ - awk \ cpio \ automake \ git \ From dridi.boukelmoune at gmail.com Thu May 15 16:22:05 2025 From: dridi.boukelmoune at gmail.com (Dridi Boukelmoune) Date: Thu, 15 May 2025 16:22:05 +0000 (UTC) Subject: [master] efebc8f74 param: Move alias resolution before protected check Message-ID: <20250515162205.0EE179B06@lists.varnish-cache.org> commit efebc8f74a017fab87e39e72362342a6c754edde Author: Dridi Boukelmoune Date: Fri May 2 15:13:00 2025 +0200 param: Move alias resolution before protected check Instead of resolving tweaks when the function is called, this is now done in the MGT code performing the protected check. Since aliases may be used to reset a single bit of another parameter (namely vcc_feature) the default value is looked up before the alias resolution. Unfortunately, that also means resolving deprecated aliases before showing them to the user, adding a little bit of duplicated logic. Refs #4323 diff --git a/bin/varnishd/mgt/mgt_param.c b/bin/varnishd/mgt/mgt_param.c index 377ae18a4..f88dc4014 100644 --- a/bin/varnishd/mgt/mgt_param.c +++ b/bin/varnishd/mgt/mgt_param.c @@ -155,6 +155,18 @@ mcf_addpar(struct parspec *ps) VTAILQ_INSERT_TAIL(&phead, pl, list); } +static const struct parspec * +mcf_alias(struct parspec *alias, const struct parspec *pp) +{ + const struct parspec *orig; + + orig = TRUST_ME(pp->priv); + AN(orig); + memcpy(alias, orig, sizeof *alias); + alias->priv = TRUST_ME(orig); + return (alias); +} + /*-------------------------------------------------------------------- * Wrap the text nicely. * Lines are allowed to contain two TABS and we render that as a table @@ -253,6 +265,7 @@ mcf_param_show(struct cli *cli, const char * const *av, void *priv) { struct plist *pl; const struct parspec *pp, *pa; + struct parspec alias[1]; int n, lfmt = 0, chg = 0; struct vsb *vsb; const char *show = NULL; @@ -285,9 +298,13 @@ mcf_param_show(struct cli *cli, const char * const *av, void *priv) pp = pl->spec; if (lfmt && show != NULL && strcmp(pp->name, show)) continue; - if (pp->func == tweak_alias && - (show == NULL || strcmp(pp->name, show))) - continue; + if (pp->func == tweak_alias) { + if (show == NULL) + continue; + if (strcmp(pp->name, show)) + continue; + pp = mcf_alias(alias, pp); + } n++; VSB_clear(vsb); @@ -385,6 +402,7 @@ mcf_param_show_json(struct cli *cli, const char * const *av, void *priv) int n, comma = 0, chg = 0; struct plist *pl; const struct parspec *pp, *pa; + struct parspec alias[1]; struct vsb *vsb, *def; const char *show = NULL, *sep; @@ -422,9 +440,13 @@ mcf_param_show_json(struct cli *cli, const char * const *av, void *priv) pp = pl->spec; if (show != NULL && strcmp(pp->name, show) != 0) continue; - if (pp->func == tweak_alias && - (show == NULL || strcmp(pp->name, show))) - continue; + if (pp->func == tweak_alias) { + if (show == NULL) + continue; + if (strcmp(pp->name, show)) + continue; + pp = mcf_alias(alias, pp); + } n++; VSB_clear(vsb); @@ -544,6 +566,7 @@ void MCF_ParamSet(struct cli *cli, const char *param, const char *val) { const struct parspec *pp; + struct parspec alias[1]; pp = mcf_findpar(param); if (pp == NULL) { @@ -559,13 +582,17 @@ MCF_ParamSet(struct cli *cli, const char *param, const char *val) ); return; } + if (!val) + val = pp->def; + if (pp->func == tweak_alias) { + pp = mcf_alias(alias, pp); + alias->name = param; + } if (pp->flags & PROTECTED) { VCLI_SetResult(cli, CLIS_AUTH); VCLI_Out(cli, "parameter \"%s\" is protected.", param); return; } - if (!val) - val = pp->def; if (pp->func(cli->sb, pp, val)) VCLI_SetResult(cli, CLIS_PARAM); diff --git a/bin/varnishd/mgt/mgt_param_tweak.c b/bin/varnishd/mgt/mgt_param_tweak.c index d3e753f1f..be406bccc 100644 --- a/bin/varnishd/mgt/mgt_param_tweak.c +++ b/bin/varnishd/mgt/mgt_param_tweak.c @@ -614,15 +614,11 @@ tweak_storage(struct vsb *vsb, const struct parspec *par, const char *arg) int v_matchproto_(tweak_t) tweak_alias(struct vsb *vsb, const struct parspec *par, const char *arg) { - const struct parspec *orig; - struct parspec alias[1]; - - orig = TRUST_ME(par->priv); - AN(orig); - memcpy(alias, orig, sizeof *orig); - alias->name = par->name; - alias->priv = TRUST_ME(orig); - return (alias->func(vsb, alias, arg)); + + (void)vsb; + (void)par; + (void)arg; + WRONG("param tweak never called directly"); } /*-------------------------------------------------------------------- diff --git a/bin/varnishtest/tests/r04323.vtc b/bin/varnishtest/tests/r04323.vtc new file mode 100644 index 000000000..02365fe2b --- /dev/null +++ b/bin/varnishtest/tests/r04323.vtc @@ -0,0 +1,6 @@ +varnishtest "parameter alias bypassing protection" + +varnish v1 -arg "-r vcc_feature" + +varnish v1 -clierr 107 "param.set vcc_feature all" +varnish v1 -clierr 107 "param.set vcc_allow_inline_c on" From dridi.boukelmoune at gmail.com Thu May 15 16:22:05 2025 From: dridi.boukelmoune at gmail.com (Dridi Boukelmoune) Date: Thu, 15 May 2025 16:22:05 +0000 (UTC) Subject: [master] 6f4bd2fd0 param: Refuse read-only setting for aliases Message-ID: <20250515162205.235D59B0A@lists.varnish-cache.org> commit 6f4bd2fd07be86f5d1bb01285425fbc64b14a63e Author: Dridi Boukelmoune Date: Fri May 2 15:05:03 2025 +0200 param: Refuse read-only setting for aliases The alternative could have been to mark both aliases and their original parameters as protected but there isn't always a one-to-one mapping between deprecated aliases and the parameters they point to. The old vcc_* boolean parameters turned into vcc_feature flags. Refs #4323 diff --git a/bin/varnishd/mgt/mgt_param.c b/bin/varnishd/mgt/mgt_param.c index f88dc4014..75491f695 100644 --- a/bin/varnishd/mgt/mgt_param.c +++ b/bin/varnishd/mgt/mgt_param.c @@ -536,8 +536,9 @@ mcf_param_show_json(struct cli *cli, const char * const *av, void *priv) void MCF_ParamProtect(struct cli *cli, const char *args) { - char **av; + const struct parspec *orig; struct parspec *pp; + char **av; int i; av = VAV_Parse(args, NULL, ARGV_COMMA); @@ -552,8 +553,17 @@ MCF_ParamProtect(struct cli *cli, const char *args) if (pp == NULL) { VCLI_Out(cli, "Unknown parameter %s", av[i]); VCLI_SetResult(cli, CLIS_PARAM); - VAV_Free(av); - return; + break; + } + if (pp->func == tweak_alias) { + orig = TRUST_ME(pp->priv); + AN(orig); + VCLI_Out(cli, + "Cannot mark alias %s read only.\n" + "Did you mean to mark %s read only?", + pp->name, orig->name); + VCLI_SetResult(cli, CLIS_PARAM); + break; } pp->flags |= PROTECTED; } diff --git a/bin/varnishtest/tests/r04323.vtc b/bin/varnishtest/tests/r04323.vtc index 02365fe2b..5bd595227 100644 --- a/bin/varnishtest/tests/r04323.vtc +++ b/bin/varnishtest/tests/r04323.vtc @@ -4,3 +4,7 @@ varnish v1 -arg "-r vcc_feature" varnish v1 -clierr 107 "param.set vcc_feature all" varnish v1 -clierr 107 "param.set vcc_allow_inline_c on" + +shell -err -expect "Cannot mark alias vcc_allow_inline_c read only" { + varnishd -d -r vcc_allow_inline_c -n ${tmpdir}/vd +} From dridi.boukelmoune at gmail.com Thu May 15 16:22:05 2025 From: dridi.boukelmoune at gmail.com (Dridi Boukelmoune) Date: Thu, 15 May 2025 16:22:05 +0000 (UTC) Subject: [master] d7147ce48 doc: Update read-only suggestions for vcc_feature Message-ID: <20250515162205.42A1D9B0E@lists.varnish-cache.org> commit d7147ce4886e29863bd60bc6b7a2a384e700e647 Author: Dridi Boukelmoune Date: Fri May 2 15:19:32 2025 +0200 doc: Update read-only suggestions for vcc_feature Fixes #4323 diff --git a/doc/sphinx/users-guide/run_security.rst b/doc/sphinx/users-guide/run_security.rst index 121b47f6a..48afe4373 100644 --- a/doc/sphinx/users-guide/run_security.rst +++ b/doc/sphinx/users-guide/run_security.rst @@ -155,18 +155,18 @@ HTTP service, but a few can do more damage than others: :ref:`ref_param_cc_command` Execute arbitrary programs -:ref:`ref_param_vcc_allow_inline_c` - Allow inline C in VCL, which would allow any C code from VCL - to be executed by Varnish. +:ref:`ref_param_vcc_feature` + The ``allow_inline_c`` flag would allow any C code from VCL to be + executed by Varnish. Furthermore you may want to look at and lock down: :ref:`ref_param_syslog_cli_traffic` Log all CLI commands to `syslog(8)`, so you know what goes on. -:ref:`ref_param_vcc_unsafe_path` - Restrict VCL/VMODs to :ref:`ref_param_vcl_path` and - :ref:`ref_param_vmod_path` +:ref:`ref_param_vcc_feature` again + The ``unsafe_path`` flag allows VCL/VMODs to be loaded outside of + :ref:`ref_param_vcl_path` and :ref:`ref_param_vmod_path`. :ref:`ref_param_vmod_path` The directory (or colon separated list of directories) where From nils.goroll at uplex.de Sat May 17 18:25:09 2025 From: nils.goroll at uplex.de (Nils Goroll) Date: Sat, 17 May 2025 18:25:09 +0000 (UTC) Subject: [master] 1feb6bbd3 varnishtest: reduce delta to vtest wrt the name Message-ID: <20250517182509.C73CA117CC3@lists.varnish-cache.org> commit 1feb6bbd30b6fc9394bf378d657fc785784268cd Author: Nils Goroll Date: Sat May 17 20:01:30 2025 +0200 varnishtest: reduce delta to vtest wrt the name In preparation of a new attempt to merge varnishtest and vtest, change all a*.vtc self-tests to invoke varnishtest as vtest and use the vtest magic at the beginning of the vtc file. For invocation as vtest, we create links. For the installed binary, this is done via a hook, but to support varnishtest -i usage, we also need to create the link in the builddir. diff --git a/bin/varnishtest/Makefile.am b/bin/varnishtest/Makefile.am index 653d0e2bc..411be155d 100644 --- a/bin/varnishtest/Makefile.am +++ b/bin/varnishtest/Makefile.am @@ -13,6 +13,19 @@ AM_CPPFLAGS = \ bin_PROGRAMS = varnishtest +# for -i invocation / a00000.vtc +all-local: vtest + +vtest: varnishtest + ln -f .libs/varnishtest vtest + +install-exec-hook: + ln -f $(DESTDIR)$(bindir)/varnishtest$(EXEEXT) \ + $(DESTDIR)$(bindir)/vtest$(EXEEXT) + +uninstall-hook: + rm -f $(DESTDIR)$(bindir)/vtest$(EXEEXT) + varnishtest_SOURCES = \ hpack.h \ cmds.h \ @@ -88,4 +101,5 @@ BUILT_SOURCES = vtc_h2_dectbl.h CLEANFILES = \ $(builddir)/teken_state.h \ - $(BUILT_SOURCES) + $(BUILT_SOURCES) \ + vtest diff --git a/bin/varnishtest/tests/a00000.vtc b/bin/varnishtest/tests/a00000.vtc index c11fda8f0..1d6292fad 100644 --- a/bin/varnishtest/tests/a00000.vtc +++ b/bin/varnishtest/tests/a00000.vtc @@ -1,44 +1,44 @@ -varnishtest "Test varnishtest itself" +vtest "Test vtest itself" -shell -exit 1 -expect {varnishtest [options]} {varnishtest -h} +shell -exit 1 -expect {vtest [options]} {vtest -h} -shell -exit 1 -match {-D.*Define macro} {varnishtest -h} +shell -exit 1 -match {-D.*Define macro} {vtest -h} shell { pwd - echo 'notvarnishtest foo bar' > _.vtc + echo 'notvtest foo bar' > _.vtc echo 'shell "exit 9"' >> _.vtc } shell -exit 2 -expect {doesn't start with 'vtest' or 'varnishtest} { - varnishtest -v _.vtc + vtest -v _.vtc } shell -exit 77 -expect {0 tests failed, 1 tests skipped, 0 tests passed} { unset TMPDIR - varnishtest -k _.vtc + vtest -k _.vtc } # Test external macro-def with a two-turtle test shell -expect {__=barf} { - echo varnishtest foo > _.vtc + echo vtest foo > _.vtc printf 'shell {echo %c{foobar} > ${tmpdir}/__}' '$' >> _.vtc - varnishtest -q -Dfoobar=barf _.vtc + vtest -q -Dfoobar=barf _.vtc echo __=`cat __` } # Test a test failure shell -exit 2 -expect {TEST _.vtc FAILED} { - echo varnishtest foo > _.vtc + echo vtest foo > _.vtc echo 'shell {false}' >> _.vtc - exec varnishtest -v _.vtc || true + exec vtest -v _.vtc || true } # Test a test skip shell -exit 77 -expect {TEST _.vtc skipped} { - echo varnishtest foo > _.vtc + echo vtest foo > _.vtc echo 'feature cmd false' >> _.vtc - exec varnishtest -v _.vtc || true + exec vtest -v _.vtc || true } # Simple process tests diff --git a/bin/varnishtest/tests/a00002.vtc b/bin/varnishtest/tests/a00002.vtc index 0fb81d94e..83c00a008 100644 --- a/bin/varnishtest/tests/a00002.vtc +++ b/bin/varnishtest/tests/a00002.vtc @@ -1,4 +1,4 @@ -varnishtest "basic default HTTP transactions with expect and options" +vtest "basic default HTTP transactions with expect and options" server s1 { rxreq diff --git a/bin/varnishtest/tests/a00003.vtc b/bin/varnishtest/tests/a00003.vtc index 7962eef7c..794ce7640 100644 --- a/bin/varnishtest/tests/a00003.vtc +++ b/bin/varnishtest/tests/a00003.vtc @@ -1,4 +1,4 @@ -varnishtest "dual independent HTTP transactions" +vtest "dual independent HTTP transactions" server s1 { rxreq diff --git a/bin/varnishtest/tests/a00004.vtc b/bin/varnishtest/tests/a00004.vtc index 05474f620..d37288bc1 100644 --- a/bin/varnishtest/tests/a00004.vtc +++ b/bin/varnishtest/tests/a00004.vtc @@ -1,4 +1,4 @@ -varnishtest "dual shared server HTTP transactions" +vtest "dual shared server HTTP transactions" server s1 -repeat 2 { rxreq diff --git a/bin/varnishtest/tests/a00005.vtc b/bin/varnishtest/tests/a00005.vtc index fe4885fa1..ede67c529 100644 --- a/bin/varnishtest/tests/a00005.vtc +++ b/bin/varnishtest/tests/a00005.vtc @@ -1,4 +1,4 @@ -varnishtest "dual shared client HTTP transactions" +vtest "dual shared client HTTP transactions" server s1 { rxreq diff --git a/bin/varnishtest/tests/a00006.vtc b/bin/varnishtest/tests/a00006.vtc index 2ca486b83..7f1448747 100644 --- a/bin/varnishtest/tests/a00006.vtc +++ b/bin/varnishtest/tests/a00006.vtc @@ -1,4 +1,4 @@ -varnishtest "bidirectional message bodies" +vtest "bidirectional message bodies" server s1 { rxreq diff --git a/bin/varnishtest/tests/a00007.vtc b/bin/varnishtest/tests/a00007.vtc index 5c7312d9b..fb971dba0 100644 --- a/bin/varnishtest/tests/a00007.vtc +++ b/bin/varnishtest/tests/a00007.vtc @@ -1,4 +1,4 @@ -varnishtest "TCP reuse" +vtest "TCP reuse" server s1 { rxreq diff --git a/bin/varnishtest/tests/a00008.vtc b/bin/varnishtest/tests/a00008.vtc index dfa84d35b..9cf37d9f4 100644 --- a/bin/varnishtest/tests/a00008.vtc +++ b/bin/varnishtest/tests/a00008.vtc @@ -1,4 +1,4 @@ -varnishtest "Barrier operations" +vtest "Barrier operations" # bs -> server, bc -> client, bb -> both barrier bs cond 4 diff --git a/bin/varnishtest/tests/a00009.vtc b/bin/varnishtest/tests/a00009.vtc index 79c5aec7b..902869c41 100644 --- a/bin/varnishtest/tests/a00009.vtc +++ b/bin/varnishtest/tests/a00009.vtc @@ -1,4 +1,4 @@ -varnishtest "VTC process: match text" +vtest "VTC process: match text" process p1 { echo 0123 diff --git a/bin/varnishtest/tests/a00010.vtc b/bin/varnishtest/tests/a00010.vtc index ce6b8190d..d29628ec7 100644 --- a/bin/varnishtest/tests/a00010.vtc +++ b/bin/varnishtest/tests/a00010.vtc @@ -1,4 +1,4 @@ -varnishtest "simply test that the framework support \0" +vtest "simply test that the framework support \0" server s1 { rxreq diff --git a/bin/varnishtest/tests/a00011.vtc b/bin/varnishtest/tests/a00011.vtc index 7b71d0de6..fab31a883 100644 --- a/bin/varnishtest/tests/a00011.vtc +++ b/bin/varnishtest/tests/a00011.vtc @@ -1,4 +1,4 @@ -varnishtest "test vtc gzip support" +vtest "test vtc gzip support" server s1 { rxreq diff --git a/bin/varnishtest/tests/a00012.vtc b/bin/varnishtest/tests/a00012.vtc index 9b044fca5..66b7f1f0b 100644 --- a/bin/varnishtest/tests/a00012.vtc +++ b/bin/varnishtest/tests/a00012.vtc @@ -1,4 +1,4 @@ -varnishtest "Ensure that we can test non-existence of headers (#1062)" +vtest "Ensure that we can test non-existence of headers (#1062)" server s1 { rxreq diff --git a/bin/varnishtest/tests/a00013.vtc b/bin/varnishtest/tests/a00013.vtc index 91bfce0bd..d95ee8725 100644 --- a/bin/varnishtest/tests/a00013.vtc +++ b/bin/varnishtest/tests/a00013.vtc @@ -1,4 +1,4 @@ -varnishtest "Barrier operations" +vtest "Barrier operations" # same as a00008.vtc, with socket barriers instead diff --git a/bin/varnishtest/tests/a00014.vtc b/bin/varnishtest/tests/a00014.vtc index 860c1ca04..0207a0fae 100644 --- a/bin/varnishtest/tests/a00014.vtc +++ b/bin/varnishtest/tests/a00014.vtc @@ -1,6 +1,6 @@ -varnishtest "Custom feature verification" +vtest "Custom feature verification" feature cmd true feature cmd false -this is an invalid varnishtest command +this is an invalid vtest command diff --git a/bin/varnishtest/tests/a00015.vtc b/bin/varnishtest/tests/a00015.vtc index f363d66d9..f3fcfdc5a 100644 --- a/bin/varnishtest/tests/a00015.vtc +++ b/bin/varnishtest/tests/a00015.vtc @@ -1,4 +1,4 @@ -varnishtest "Write a body to a file" +vtest "Write a body to a file" server s1 { # First, HTTP checks diff --git a/bin/varnishtest/tests/a00016.vtc b/bin/varnishtest/tests/a00016.vtc index 888b59ccd..2e4f90417 100644 --- a/bin/varnishtest/tests/a00016.vtc +++ b/bin/varnishtest/tests/a00016.vtc @@ -1,4 +1,4 @@ -varnishtest "Shutdown" +vtest "Shutdown" barrier b1 cond 2 diff --git a/bin/varnishtest/tests/a00018.vtc b/bin/varnishtest/tests/a00018.vtc index 5500d7c6e..45d1675e9 100644 --- a/bin/varnishtest/tests/a00018.vtc +++ b/bin/varnishtest/tests/a00018.vtc @@ -1,4 +1,4 @@ -varnishtest "feature ignore_unknown_macro" +vtest "feature ignore_unknown_macro" feature ignore_unknown_macro diff --git a/bin/varnishtest/tests/a00019.vtc b/bin/varnishtest/tests/a00019.vtc index 975364445..727c33ca9 100644 --- a/bin/varnishtest/tests/a00019.vtc +++ b/bin/varnishtest/tests/a00019.vtc @@ -1,4 +1,4 @@ -varnishtest "Check rxrespbody -max" +vtest "Check rxrespbody -max" server s1 { rxreq diff --git a/bin/varnishtest/tests/a00020.vtc b/bin/varnishtest/tests/a00020.vtc index 6b50c11df..73d21dadd 100644 --- a/bin/varnishtest/tests/a00020.vtc +++ b/bin/varnishtest/tests/a00020.vtc @@ -1,14 +1,14 @@ -varnishtest "Test -bodyfrom" +vtest "Test -bodyfrom" server s1 { rxreq - expect req.bodylen == 241 + expect req.bodylen == 235 txresp -bodyfrom ${testdir}/a00020.vtc } -start client c1 -connect ${s1_sock} { txreq -bodyfrom ${testdir}/a00020.vtc rxresp - expect resp.bodylen == 241 + expect resp.bodylen == 235 } -run diff --git a/bin/varnishtest/tests/a00021.vtc b/bin/varnishtest/tests/a00021.vtc index 9eb158582..e57f89d35 100644 --- a/bin/varnishtest/tests/a00021.vtc +++ b/bin/varnishtest/tests/a00021.vtc @@ -1,4 +1,4 @@ -varnishtest "tunnel basic coverage" +vtest "tunnel basic coverage" barrier b1 cond 2 barrier b2 cond 2 diff --git a/bin/varnishtest/tests/a00022.vtc b/bin/varnishtest/tests/a00022.vtc index 86a26e86f..adf522935 100644 --- a/bin/varnishtest/tests/a00022.vtc +++ b/bin/varnishtest/tests/a00022.vtc @@ -1,4 +1,4 @@ -varnishtest "Test setenv" +vtest "Test setenv" setenv FOO "BAR BAZ" shell -expect "BAR BAZ" {echo $FOO} diff --git a/bin/varnishtest/tests/a00023.vtc b/bin/varnishtest/tests/a00023.vtc index b35a9ec7e..a8b8c245f 100644 --- a/bin/varnishtest/tests/a00023.vtc +++ b/bin/varnishtest/tests/a00023.vtc @@ -1,10 +1,10 @@ -varnishtest "Run server -dispatch more than once" +vtest "Run server -dispatch more than once" feature ignore_unknown_macro shell { cat <<-'EOF' >_.vtc - varnishtest "Run server -dispatch more than once (nested)" + vtest "Run server -dispatch more than once (nested)" server s0 { rxreq @@ -22,7 +22,7 @@ shell { client c1 -run EOF - varnishtest -v _.vtc >_.log + vtest -v _.vtc >_.log } shell -match "s1 +rxhdr" { cat _.log } diff --git a/bin/varnishtest/tests/a00024.vtc b/bin/varnishtest/tests/a00024.vtc index 34e16c155..efab48af0 100644 --- a/bin/varnishtest/tests/a00024.vtc +++ b/bin/varnishtest/tests/a00024.vtc @@ -1,4 +1,4 @@ -varnishtest "test -nouseragent and -noserver" +vtest "test -nouseragent and -noserver" server s1 { rxreq diff --git a/bin/varnishtest/tests/a00025.vtc b/bin/varnishtest/tests/a00025.vtc index 21c513f5c..f2e105ed0 100644 --- a/bin/varnishtest/tests/a00025.vtc +++ b/bin/varnishtest/tests/a00025.vtc @@ -1,4 +1,4 @@ -varnishtest "Basic VSM status check" +vtest "Basic VSM status check" # just launch varnishd varnish v1 -cliok ping diff --git a/bin/varnishtest/tests/a02000.vtc b/bin/varnishtest/tests/a02000.vtc index c272dac01..faed96df3 100644 --- a/bin/varnishtest/tests/a02000.vtc +++ b/bin/varnishtest/tests/a02000.vtc @@ -1,4 +1,4 @@ -varnishtest "Close/accept after H/2 upgrade" +vtest "Close/accept after H/2 upgrade" server s1 { stream 1 { diff --git a/bin/varnishtest/tests/a02001.vtc b/bin/varnishtest/tests/a02001.vtc index eb0253983..1cab65db5 100644 --- a/bin/varnishtest/tests/a02001.vtc +++ b/bin/varnishtest/tests/a02001.vtc @@ -1,4 +1,4 @@ -varnishtest "Quickly test all frames" +vtest "Quickly test all frames" server s1 { rxpri diff --git a/bin/varnishtest/tests/a02002.vtc b/bin/varnishtest/tests/a02002.vtc index 2f6c504bd..14125d55b 100644 --- a/bin/varnishtest/tests/a02002.vtc +++ b/bin/varnishtest/tests/a02002.vtc @@ -1,4 +1,4 @@ -varnishtest "Trigger a compression error via bad index" +vtest "Trigger a compression error via bad index" server s1 { non_fatal diff --git a/bin/varnishtest/tests/a02003.vtc b/bin/varnishtest/tests/a02003.vtc index eced2da04..abda36db9 100644 --- a/bin/varnishtest/tests/a02003.vtc +++ b/bin/varnishtest/tests/a02003.vtc @@ -1,4 +1,4 @@ -varnishtest "Check bodylen" +vtest "Check bodylen" server s1 { stream 1 { diff --git a/bin/varnishtest/tests/a02004.vtc b/bin/varnishtest/tests/a02004.vtc index 915446080..90e22d55f 100644 --- a/bin/varnishtest/tests/a02004.vtc +++ b/bin/varnishtest/tests/a02004.vtc @@ -1,4 +1,4 @@ -varnishtest "Simple request with body" +vtest "Simple request with body" server s1 { stream 1 { diff --git a/bin/varnishtest/tests/a02005.vtc b/bin/varnishtest/tests/a02005.vtc index 986a07270..e596f8381 100644 --- a/bin/varnishtest/tests/a02005.vtc +++ b/bin/varnishtest/tests/a02005.vtc @@ -1,4 +1,4 @@ -varnishtest "Continuation frames" +vtest "Continuation frames" server s1 { stream 1 { diff --git a/bin/varnishtest/tests/a02006.vtc b/bin/varnishtest/tests/a02006.vtc index ecb49745f..9e18ef9d0 100644 --- a/bin/varnishtest/tests/a02006.vtc +++ b/bin/varnishtest/tests/a02006.vtc @@ -1,4 +1,4 @@ -varnishtest "Keep track of window credits" +vtest "Keep track of window credits" server s1 { stream 2 { diff --git a/bin/varnishtest/tests/a02007.vtc b/bin/varnishtest/tests/a02007.vtc index 7d1be9282..5cd3e72aa 100644 --- a/bin/varnishtest/tests/a02007.vtc +++ b/bin/varnishtest/tests/a02007.vtc @@ -1,4 +1,4 @@ -varnishtest "HPACK test" +vtest "HPACK test" server s1 { stream 1 { diff --git a/bin/varnishtest/tests/a02008.vtc b/bin/varnishtest/tests/a02008.vtc index 0c0c4029c..0e7143144 100644 --- a/bin/varnishtest/tests/a02008.vtc +++ b/bin/varnishtest/tests/a02008.vtc @@ -1,4 +1,4 @@ -varnishtest "HPACK test" +vtest "HPACK test" server s1 { stream 0 { diff --git a/bin/varnishtest/tests/a02009.vtc b/bin/varnishtest/tests/a02009.vtc index 8668cb91a..37de4e6d7 100644 --- a/bin/varnishtest/tests/a02009.vtc +++ b/bin/varnishtest/tests/a02009.vtc @@ -1,4 +1,4 @@ -varnishtest "More HPACK tests" +vtest "More HPACK tests" server s1 { stream 1 { rxreq diff --git a/bin/varnishtest/tests/a02010.vtc b/bin/varnishtest/tests/a02010.vtc index 2e77c4d2d..151f64e24 100644 --- a/bin/varnishtest/tests/a02010.vtc +++ b/bin/varnishtest/tests/a02010.vtc @@ -1,4 +1,4 @@ -varnishtest "Verify the initial window size" +vtest "Verify the initial window size" server s1 { stream 0 { diff --git a/bin/varnishtest/tests/a02011.vtc b/bin/varnishtest/tests/a02011.vtc index d4a7719e6..d0123c66d 100644 --- a/bin/varnishtest/tests/a02011.vtc +++ b/bin/varnishtest/tests/a02011.vtc @@ -1,4 +1,4 @@ -varnishtest "overflow" +vtest "overflow" server s1 { stream 1 { diff --git a/bin/varnishtest/tests/a02012.vtc b/bin/varnishtest/tests/a02012.vtc index 83960b8e8..b560cd4b3 100644 --- a/bin/varnishtest/tests/a02012.vtc +++ b/bin/varnishtest/tests/a02012.vtc @@ -1,4 +1,4 @@ -varnishtest "padded DATA frames" +vtest "padded DATA frames" server s1 { stream 1 { diff --git a/bin/varnishtest/tests/a02013.vtc b/bin/varnishtest/tests/a02013.vtc index 51d3a8038..48edfb69f 100644 --- a/bin/varnishtest/tests/a02013.vtc +++ b/bin/varnishtest/tests/a02013.vtc @@ -1,4 +1,4 @@ -varnishtest "H/2 state after sending/receiving preface" +vtest "H/2 state after sending/receiving preface" server s1 { expect h2.state == false diff --git a/bin/varnishtest/tests/a02014.vtc b/bin/varnishtest/tests/a02014.vtc index b026e56b9..c58e158db 100644 --- a/bin/varnishtest/tests/a02014.vtc +++ b/bin/varnishtest/tests/a02014.vtc @@ -1,4 +1,4 @@ -varnishtest "priority" +vtest "priority" server s1 { stream 1 { diff --git a/bin/varnishtest/tests/a02015.vtc b/bin/varnishtest/tests/a02015.vtc index e1f7b59d0..a38100fb7 100644 --- a/bin/varnishtest/tests/a02015.vtc +++ b/bin/varnishtest/tests/a02015.vtc @@ -1,4 +1,4 @@ -varnishtest "exclusive dependency" +vtest "exclusive dependency" server s1 { stream 1 { diff --git a/bin/varnishtest/tests/a02016.vtc b/bin/varnishtest/tests/a02016.vtc index ea06e33cc..e75dddc5d 100644 --- a/bin/varnishtest/tests/a02016.vtc +++ b/bin/varnishtest/tests/a02016.vtc @@ -1,4 +1,4 @@ -varnishtest "Test pseudo-headers inspection" +vtest "Test pseudo-headers inspection" server s1 { stream 1 { diff --git a/bin/varnishtest/tests/a02017.vtc b/bin/varnishtest/tests/a02017.vtc index c0374f70e..c917fcf9c 100644 --- a/bin/varnishtest/tests/a02017.vtc +++ b/bin/varnishtest/tests/a02017.vtc @@ -1,4 +1,4 @@ -varnishtest "Push promise" +vtest "Push promise" server s1 { stream 1 { diff --git a/bin/varnishtest/tests/a02018.vtc b/bin/varnishtest/tests/a02018.vtc index 7c281dfb0..c3da45ddf 100644 --- a/bin/varnishtest/tests/a02018.vtc +++ b/bin/varnishtest/tests/a02018.vtc @@ -1,4 +1,4 @@ -varnishtest "H/2 state after sending/receiving preface" +vtest "H/2 state after sending/receiving preface" server s1 { expect h2.state == "false" diff --git a/bin/varnishtest/tests/a02019.vtc b/bin/varnishtest/tests/a02019.vtc index 388b6e506..299c61baa 100644 --- a/bin/varnishtest/tests/a02019.vtc +++ b/bin/varnishtest/tests/a02019.vtc @@ -1,4 +1,4 @@ -varnishtest "Static table encoding" +vtest "Static table encoding" server s1 { stream 1 { diff --git a/bin/varnishtest/tests/a02020.vtc b/bin/varnishtest/tests/a02020.vtc index 38aba8755..962dcd0e9 100644 --- a/bin/varnishtest/tests/a02020.vtc +++ b/bin/varnishtest/tests/a02020.vtc @@ -1,4 +1,4 @@ -varnishtest "Reduce dynamic table while incoming headers are flying" +vtest "Reduce dynamic table while incoming headers are flying" server s1 { stream 1 { diff --git a/bin/varnishtest/tests/a02021.vtc b/bin/varnishtest/tests/a02021.vtc index a04265f5d..8116e1542 100644 --- a/bin/varnishtest/tests/a02021.vtc +++ b/bin/varnishtest/tests/a02021.vtc @@ -1,4 +1,4 @@ -varnishtest "Reduce dynamic table size" +vtest "Reduce dynamic table size" server s1 { stream 1 { diff --git a/bin/varnishtest/tests/a02022.vtc b/bin/varnishtest/tests/a02022.vtc index 1be052e05..c0aef0e19 100644 --- a/bin/varnishtest/tests/a02022.vtc +++ b/bin/varnishtest/tests/a02022.vtc @@ -1,4 +1,4 @@ -varnishtest "H/1 -> H/2 upgrade" +vtest "H/1 -> H/2 upgrade" feature cmd "nghttp --version | grep -q 'nghttp2/[1-9]'" diff --git a/bin/varnishtest/tests/a02023.vtc b/bin/varnishtest/tests/a02023.vtc index 3cbc94d58..e545b0631 100644 --- a/bin/varnishtest/tests/a02023.vtc +++ b/bin/varnishtest/tests/a02023.vtc @@ -1,4 +1,4 @@ -varnishtest "Window update" +vtest "Window update" server s1 { stream 1 { diff --git a/bin/varnishtest/tests/a02024.vtc b/bin/varnishtest/tests/a02024.vtc index 746773592..3e702af10 100644 --- a/bin/varnishtest/tests/a02024.vtc +++ b/bin/varnishtest/tests/a02024.vtc @@ -1,4 +1,4 @@ -varnishtest "Write a body to a file" +vtest "Write a body to a file" server s1 { fatal diff --git a/bin/varnishtest/tests/a02025.vtc b/bin/varnishtest/tests/a02025.vtc index 396e29e6e..594de1c28 100644 --- a/bin/varnishtest/tests/a02025.vtc +++ b/bin/varnishtest/tests/a02025.vtc @@ -1,4 +1,4 @@ -varnishtest "Test -bodyfrom" +vtest "Test -bodyfrom" shell {printf helloworld >body.txt} diff --git a/bin/varnishtest/tests/a02026.vtc b/bin/varnishtest/tests/a02026.vtc index bf61aebc9..25c44b7b0 100644 --- a/bin/varnishtest/tests/a02026.vtc +++ b/bin/varnishtest/tests/a02026.vtc @@ -1,4 +1,4 @@ -varnishtest "Test -gzipbody and -gziplen" +vtest "Test -gzipbody and -gziplen" server s1 { stream 1 { diff --git a/bin/varnishtest/tests/a02027.vtc b/bin/varnishtest/tests/a02027.vtc index e9dcf6619..8156c199a 100644 --- a/bin/varnishtest/tests/a02027.vtc +++ b/bin/varnishtest/tests/a02027.vtc @@ -1,4 +1,4 @@ -varnishtest "Malformed :path handling" +vtest "Malformed :path handling" server s1 { } -start diff --git a/bin/varnishtest/tests/a02028.vtc b/bin/varnishtest/tests/a02028.vtc index 4ba8986ed..08f696793 100644 --- a/bin/varnishtest/tests/a02028.vtc +++ b/bin/varnishtest/tests/a02028.vtc @@ -1,4 +1,4 @@ -varnishtest "Automatic stream numbers" +vtest "Automatic stream numbers" server s1 { loop 4 { From nils.goroll at uplex.de Sat May 17 18:25:09 2025 From: nils.goroll at uplex.de (Nils Goroll) Date: Sat, 17 May 2025 18:25:09 +0000 (UTC) Subject: [master] 4d06897eb varnishtest: Add include command Message-ID: <20250517182509.DAB34117CC6@lists.varnish-cache.org> commit 4d06897eb6a5bd2ad47b8819f09910559d8d6649 Author: AlveElde Date: Thu Jan 18 16:38:46 2024 +0100 varnishtest: Add include command The contents of a file included by this command are executed as-is. The command is global, which means that the include command can be used inside HTTP specs as the test case shows. We require at least one argument, and subsequent file path arguments are parsed in order. This command can be used to build more complex VTCs, as well as reducing repetitiveness. We make no attempts to avoid circular includes. Committer edit: This change was originally added to vtest as 5a5b4ebfa33da9dfb57e8707d98a2f64f4dee7e2 and then manually applied to Varnish-Cache as f0dc9964e953fbcc11869957f11382098be0a9ab, but without the test case. The test-case was originally added as a00021.vtc, which, at the time of this retrospective addition, was already taken by the tunnel command test. diff --git a/bin/varnishtest/tests/a00026.vtc b/bin/varnishtest/tests/a00026.vtc new file mode 100644 index 000000000..bb0b88cd5 --- /dev/null +++ b/bin/varnishtest/tests/a00026.vtc @@ -0,0 +1,28 @@ +varnishtest "Test VTC includes" + +shell { + cat >${tmpdir}/f1 <<-EOF + rxreq + EOF +} + +shell { + cat >${tmpdir}/f2 <<-EOF + txresp + EOF +} + +shell { + cat >${tmpdir}/f3 <<-EOF + server s1 { + include "${tmpdir}/f1" "${tmpdir}/f2" + } -start + EOF +} + +include "${tmpdir}/f3" + +client c1 -connect "${s1_sock}" { + txreq + rxresp +} -run From nils.goroll at uplex.de Sun May 18 15:03:04 2025 From: nils.goroll at uplex.de (Nils Goroll) Date: Sun, 18 May 2025 15:03:04 +0000 (UTC) Subject: [master] f6d23b62c vre: turn autoconf check into cpp check Message-ID: <20250518150305.01B60119858@lists.varnish-cache.org> commit f6d23b62cc5368d0f33156e528e3b19937fae50a Author: Nils Goroll Date: Sun May 18 12:50:13 2025 +0200 vre: turn autoconf check into cpp check Good idea from vtest (phk, b3d75c2a0e3d54ee205e8c4a238239fc2de75fa5): We can do without autoconf here because pcre2_set_depth_limit is a macro Also, add a warning to prepare for turning missing pcre2_set_depth_limit into an error sooner or later. Found while working on reducing the delta to vtest diff --git a/configure.ac b/configure.ac index f457af5d5..a0701f4d3 100644 --- a/configure.ac +++ b/configure.ac @@ -144,13 +144,6 @@ fi AC_SUBST(PCRE2_CFLAGS) AC_SUBST(PCRE2_LIBS) -save_LIBS="${LIBS}" -LIBS="${LIBS} ${PCRE2_LIBS}" -AC_CHECK_FUNCS([pcre2_set_depth_limit_8], [ - AC_DEFINE([HAVE_PCRE2_SET_DEPTH_LIMIT], [1], [Use pcre2_set_depth_limit()]) -]) -LIBS="${save_LIBS}" - # --enable-pcre2-jit AC_ARG_ENABLE(pcre2-jit, AS_HELP_STRING([--enable-pcre2-jit], diff --git a/lib/libvarnish/vre.c b/lib/libvarnish/vre.c index c0f87c0c4..6d9b58bc1 100644 --- a/lib/libvarnish/vre.c +++ b/lib/libvarnish/vre.c @@ -43,7 +43,9 @@ #include "vre.h" #include "vre_pcre2.h" -#if !HAVE_PCRE2_SET_DEPTH_LIMIT +/* should be turned into an error sooner or later */ +#if !defined(pcre2_set_depth_limit) +# warning pcre2 missing pcre2_set_depth_limit - update recommended # define pcre2_set_depth_limit(r, d) pcre2_set_recursion_limit(r, d) #endif From nils.goroll at uplex.de Sun May 18 15:03:05 2025 From: nils.goroll at uplex.de (Nils Goroll) Date: Sun, 18 May 2025 15:03:05 +0000 (UTC) Subject: [master] b721e2b57 varnishtest: do not require PACKAGE_VERSION and PACKAGE_BRANCH be defined Message-ID: <20250518150305.15F9411985B@lists.varnish-cache.org> commit b721e2b5720ee666d5d6508256a86715ecf24a32 Author: Nils Goroll Date: Sun May 18 16:51:55 2025 +0200 varnishtest: do not require PACKAGE_VERSION and PACKAGE_BRANCH be defined reduces delta to vtest diff --git a/bin/varnishtest/vtc_main.c b/bin/varnishtest/vtc_main.c index 9880c7c5e..83b6ef15d 100644 --- a/bin/varnishtest/vtc_main.c +++ b/bin/varnishtest/vtc_main.c @@ -836,8 +836,12 @@ main(int argc, char * const *argv) else tmppath = strdup("/tmp"); +#ifdef PACKAGE_VERSION extmacro_def("pkg_version", NULL, PACKAGE_VERSION); +#endif +#ifdef PACKAGE_BRANCH extmacro_def("pkg_branch", NULL, PACKAGE_BRANCH); +#endif cwd = getcwd(buf, sizeof buf); extmacro_def("pwd", NULL, "%s", cwd); From nils.goroll at uplex.de Mon May 19 09:48:05 2025 From: nils.goroll at uplex.de (Nils Goroll) Date: Mon, 19 May 2025 09:48:05 +0000 (UTC) Subject: [master] e8f6d9fec Start NEXT section in changes.rst Message-ID: <20250519094805.B152E1178F5@lists.varnish-cache.org> commit e8f6d9fec0db073d7df54ad67ca2dcc61bf115b5 Author: Nils Goroll Date: Mon May 19 11:47:15 2025 +0200 Start NEXT section in changes.rst to anchor patches diff --git a/doc/changes.rst b/doc/changes.rst index 61ee71669..33609d263 100644 --- a/doc/changes.rst +++ b/doc/changes.rst @@ -34,13 +34,17 @@ http://varnish-cache.org/docs/trunk/whats-new/index.html and via individual releases. These documents are updated as part of the release process. -============================== -Varnish-Cache 7.7 (2025-03-17) -============================== +==================================== +Varnish Cache NEXT (8.0, 2025-09-15) +==================================== .. PLEASE keep this roughly in commit order as shown by git-log / tig (new to old) +============================== +Varnish-Cache 7.7 (2025-03-17) +============================== + .. _VSV00015: https://varnish-cache.org/security/VSV00015.html * The client connection is now always closed when a malformed request From nils.goroll at uplex.de Mon May 19 09:50:04 2025 From: nils.goroll at uplex.de (Nils Goroll) Date: Mon, 19 May 2025 09:50:04 +0000 (UTC) Subject: [master] a44cdfe9f forgot addition to .gitignore Message-ID: <20250519095004.EFC90117B63@lists.varnish-cache.org> commit a44cdfe9fa7bec1bbb455ca5d7caf1a09da16fef Author: Nils Goroll Date: Mon May 19 11:48:38 2025 +0200 forgot addition to .gitignore Ref 1feb6bbd30b6fc9394bf378d657fc785784268cd diff --git a/.gitignore b/.gitignore index fe61bb233..695c8ce01 100644 --- a/.gitignore +++ b/.gitignore @@ -98,6 +98,7 @@ cscope.*out /bin/varnishstat/vsc2rst /bin/varnishtest/teken_state.h /bin/varnishtest/varnishtest +/bin/varnishtest/vtest /bin/varnishtop/varnishtop # Doc-stuff generated from xml From nils.goroll at uplex.de Mon May 19 13:18:05 2025 From: nils.goroll at uplex.de (Nils Goroll) Date: Mon, 19 May 2025 13:18:05 +0000 (UTC) Subject: [master] 0cfb214dc vrt: Constify sentinel pointers Message-ID: <20250519131805.1848D11EE4F@lists.varnish-cache.org> commit 0cfb214dc7a50384448a437ffd268319995518fd Author: Dridi Boukelmoune Date: Thu May 15 10:58:48 2025 +0200 vrt: Constify sentinel pointers They are pointing to sentinel values, but nothing prevented the variables themselves to be assigned a different const value. diff --git a/bin/varnishd/cache/cache_vrt.c b/bin/varnishd/cache/cache_vrt.c index fce89425d..5484f75aa 100644 --- a/bin/varnishd/cache/cache_vrt.c +++ b/bin/varnishd/cache/cache_vrt.c @@ -55,11 +55,11 @@ #include "proxy/cache_proxy.h" // NOT using TOSTRANDS() to create a NULL pointer element despite n == 0 -const struct strands *vrt_null_strands = &(struct strands){ +const struct strands *const vrt_null_strands = &(struct strands){ .n = 0, .p = (const char *[1]){NULL} }; -const struct vrt_blob *vrt_null_blob = &(struct vrt_blob){ +const struct vrt_blob *const vrt_null_blob = &(struct vrt_blob){ .type = VRT_NULL_BLOB_TYPE, .len = 0, .blob = "\0" diff --git a/include/vrt.h b/include/vrt.h index 0644063f7..0c17c956f 100644 --- a/include/vrt.h +++ b/include/vrt.h @@ -375,7 +375,7 @@ struct strands { /* * A VCL_STRANDS return value must never be NULL. Use this instead */ -extern const struct strands *vrt_null_strands; +extern const struct strands *const vrt_null_strands; /* * Macros for VCL_STRANDS creation @@ -409,7 +409,7 @@ struct vrt_blob { }; #define VRT_NULL_BLOB_TYPE 0xfade4fa0 -extern const struct vrt_blob *vrt_null_blob; +extern const struct vrt_blob *const vrt_null_blob; /*********************************************************************** * This is the central definition of the mapping from VCL types to From nils.goroll at uplex.de Mon May 19 13:18:05 2025 From: nils.goroll at uplex.de (Nils Goroll) Date: Mon, 19 May 2025 13:18:05 +0000 (UTC) Subject: [master] 6748169d8 vsl: Constify VSLQ_grouping[] array Message-ID: <20250519131805.32C2D11EE52@lists.varnish-cache.org> commit 6748169d82767d1d2fa03c8b2676ad5f0ebf3f0e Author: Dridi Boukelmoune Date: Thu May 15 11:00:42 2025 +0200 vsl: Constify VSLQ_grouping[] array Just like VSL_tags[], both the array and the strings its members are pointing to should be immutable. diff --git a/include/vapi/vsl.h b/include/vapi/vsl.h index 3b43f2cb4..7a44cd5f8 100644 --- a/include/vapi/vsl.h +++ b/include/vapi/vsl.h @@ -230,7 +230,7 @@ int VSL_List2Tags(const char *list, int l, VSL_tagfind_f *func, void *priv); * -3: Syntax error */ -extern const char *VSLQ_grouping[VSL_g__MAX]; +extern const char *const VSLQ_grouping[VSL_g__MAX]; /* * Grouping mode to string array. */ diff --git a/lib/libvarnishapi/vsl_arg.c b/lib/libvarnishapi/vsl_arg.c index 0fcdb4156..d7101c2ae 100644 --- a/lib/libvarnishapi/vsl_arg.c +++ b/lib/libvarnishapi/vsl_arg.c @@ -175,7 +175,7 @@ VSL_List2Tags(const char *list, int l, VSL_tagfind_f *func, void *priv) return (t); } -const char *VSLQ_grouping[VSL_g__MAX] = { +const char *const VSLQ_grouping[VSL_g__MAX] = { [VSL_g_raw] = "raw", [VSL_g_vxid] = "vxid", [VSL_g_request] = "request", From nils.goroll at uplex.de Mon May 19 13:21:05 2025 From: nils.goroll at uplex.de (Nils Goroll) Date: Mon, 19 May 2025 13:21:05 +0000 (UTC) Subject: [master] da204fb40 vbt: New VBT_dump() function Message-ID: <20250519132105.4159011F289@lists.varnish-cache.org> commit da204fb40467eb319f8516f6c185c232e8e61d1b Author: Asad Sajjad Ahmed Date: Wed Jun 5 12:34:14 2024 +0200 vbt: New VBT_dump() function This is useful when printing a backtrace into a fixed size buffer. Suggested-by: Dridi Boukelmoune Signed-off-by: Asad Sajjad Ahmed diff --git a/include/vbt.h b/include/vbt.h index bd3a384b9..c2c8b28f2 100644 --- a/include/vbt.h +++ b/include/vbt.h @@ -31,3 +31,4 @@ struct vsb; void VBT_format(struct vsb *); +int VBT_dump(size_t len, char [len]); diff --git a/lib/libvarnish/vbt.c b/lib/libvarnish/vbt.c index c38227a88..99fc59400 100644 --- a/lib/libvarnish/vbt.c +++ b/lib/libvarnish/vbt.c @@ -157,3 +157,19 @@ VBT_format(struct vsb *vsb) vbt_execinfo(vsb); #endif } + +int +VBT_dump(size_t len, char buf[len]) +{ + struct vsb vsb[1]; + + if (VSB_init(vsb, buf, len) == NULL) + return (-1); + + VSB_printf(vsb, "Backtrace:\n"); + VSB_indent(vsb, 2); + VBT_format(vsb); + VSB_indent(vsb, -2); + + return (0); +} From nils.goroll at uplex.de Mon May 19 13:21:05 2025 From: nils.goroll at uplex.de (Nils Goroll) Date: Mon, 19 May 2025 13:21:05 +0000 (UTC) Subject: [master] 07e5adfeb vas: Print a back-trace by default Message-ID: <20250519132105.5764B11F28C@lists.varnish-cache.org> commit 07e5adfebdca301d57f73d565fa58fcd5be73a24 Author: Asad Sajjad Ahmed Date: Wed Jun 5 16:12:38 2024 +0200 vas: Print a back-trace by default Signed-off-by: Asad Sajjad Ahmed diff --git a/lib/libvarnish/vas.c b/lib/libvarnish/vas.c index b64418cc9..bee30f5b7 100644 --- a/lib/libvarnish/vas.c +++ b/lib/libvarnish/vas.c @@ -36,8 +36,10 @@ #include #include #include +#include #include "vdef.h" +#include "vbt.h" #include "vas.h" @@ -57,37 +59,53 @@ VAS_errtxt(int e) vas_f *VAS_Fail_Func v_noreturn_; -void v_noreturn_ -VAS_Fail(const char *func, const char *file, int line, +static void +vas_default(const char *func, const char *file, int line, const char *cond, enum vas_e kind) { int err = errno; + char buf[4096]; - if (VAS_Fail_Func != NULL) { - VAS_Fail_Func(func, file, line, cond, kind); + if (kind == VAS_MISSING) { + fprintf(stderr, + "Missing error handling code in %s(), %s line %d:\n" + " Condition(%s) not true.\n", + func, file, line, cond); + } else if (kind == VAS_INCOMPLETE) { + fprintf(stderr, + "Incomplete code in %s(), %s line %d:\n", + func, file, line); + } else if (kind == VAS_WRONG) { + fprintf(stderr, + "Wrong turn in %s(), %s line %d: %s\n", + func, file, line, cond); } else { - if (kind == VAS_MISSING) { - fprintf(stderr, - "Missing error handling code in %s(), %s line %d:\n" - " Condition(%s) not true.\n", - func, file, line, cond); - } else if (kind == VAS_INCOMPLETE) { - fprintf(stderr, - "Incomplete code in %s(), %s line %d:\n", - func, file, line); - } else if (kind == VAS_WRONG) { - fprintf(stderr, - "Wrong turn in %s(), %s line %d: %s\n", - func, file, line, cond); - } else { - fprintf(stderr, - "Assert error in %s(), %s line %d:\n" - " Condition(%s) not true.\n", - func, file, line, cond); - } - if (err) - fprintf(stderr, - " errno = %d (%s)\n", err, strerror(err)); + fprintf(stderr, + "Assert error in %s(), %s line %d:\n" + " Condition(%s) not true.\n", + func, file, line, cond); + } + if (err) { + fprintf(stderr, + " errno = %d (%s)\n", err, strerror(err)); } + + if (VBT_dump(sizeof buf, buf) < 0) { + bprintf(buf, "Failed to print backtrace: %d (%s)", + errno, strerror(errno)); + } + + syslog(LOG_DEBUG, "%s", buf); +} + +void v_noreturn_ +VAS_Fail(const char *func, const char *file, int line, + const char *cond, enum vas_e kind) +{ + + if (VAS_Fail_Func != NULL) + VAS_Fail_Func(func, file, line, cond, kind); + else + vas_default(func, file, line, cond, kind); abort(); } From nils.goroll at uplex.de Mon May 19 13:21:05 2025 From: nils.goroll at uplex.de (Nils Goroll) Date: Mon, 19 May 2025 13:21:05 +0000 (UTC) Subject: [master] 1f00b018c varnishtest: Add backtrace Message-ID: <20250519132105.6C95D11F290@lists.varnish-cache.org> commit 1f00b018c3912a3413f48a9b06950d3b33ed643e Author: Asad Sajjad Ahmed Date: Wed Jun 5 16:21:49 2024 +0200 varnishtest: Add backtrace Signed-off-by: Asad Sajjad Ahmed diff --git a/bin/varnishtest/vtc_log.c b/bin/varnishtest/vtc_log.c index f96dc9690..c42a94553 100644 --- a/bin/varnishtest/vtc_log.c +++ b/bin/varnishtest/vtc_log.c @@ -40,6 +40,7 @@ #include "vtc_log.h" #include "vtim.h" +#include "vbt.h" static pthread_mutex_t vtclog_mtx; static char *vtclog_buf; @@ -298,20 +299,31 @@ static void v_noreturn_ vtc_log_VAS_Fail(const char *func, const char *file, int line, const char *cond, enum vas_e why) { + char buf[4096] = ""; struct vtclog *vl; int e = errno; (void)why; + + if (VBT_dump(sizeof buf, buf) < 0) { + bprintf(buf, "Failed to print backtrace: %d (%s)\n", + errno, strerror(errno)); + } + vl = pthread_getspecific(log_key); if (vl == NULL || vl->act) { fprintf(stderr, "Assert error in %s(), %s line %d:\n" - " Condition(%s) not true. (errno=%d %s)\n", - func, file, line, cond, e, strerror(e)); - } else + " Condition(%s) not true. (errno=%d %s)\n" + "%s\n", + func, file, line, cond, e, strerror(e), buf); + } else { vtc_fatal(vl, "Assert error in %s(), %s line %d:" " Condition(%s) not true." - " Errno=%d %s", func, file, line, cond, e, strerror(e)); + " Errno=%d %s\n" + "%s\n", + func, file, line, cond, e, strerror(e), buf); + } abort(); } From nils.goroll at uplex.de Tue May 20 09:12:05 2025 From: nils.goroll at uplex.de (Nils Goroll) Date: Tue, 20 May 2025 09:12:05 +0000 (UTC) Subject: [master] 6cb6dfe9f vtc_server: Disambiguate "shutting fd" logs Message-ID: <20250520091205.7B8E81201D2@lists.varnish-cache.org> commit 6cb6dfe9f3d914b94e1f31d766244cbffc8f0c21 Author: Dridi Boukelmoune Date: Mon May 6 14:27:56 2024 +0200 vtc_server: Disambiguate "shutting fd" logs diff --git a/bin/varnishtest/vtc_server.c b/bin/varnishtest/vtc_server.c index 45913aaa7..5de00896a 100644 --- a/bin/varnishtest/vtc_server.c +++ b/bin/varnishtest/vtc_server.c @@ -265,7 +265,7 @@ server_disc(void *priv, struct vtclog *vl, int *fdp) struct server *s; CAST_OBJ_NOTNULL(s, priv, SERVER_MAGIC); - vtc_log(vl, 3, "shutting fd %d", *fdp); + vtc_log(vl, 3, "shutting fd %d (server run)", *fdp); j = shutdown(*fdp, SHUT_WR); if (!vtc_stop && !VTCP_Check(j)) vtc_fatal(vl, "Shutdown failed: %s", strerror(errno)); @@ -322,7 +322,7 @@ server_dispatch_wrk(void *priv) vtc_log(vl, 3, "start with fd %d", fd); fd = sess_process(vl, s->vsp, s->spec, fd, &s->sock, s->listen); - vtc_log(vl, 3, "shutting fd %d", fd); + vtc_log(vl, 3, "shutting fd %d (server dispatch)", fd); j = shutdown(fd, SHUT_WR); if (!VTCP_Check(j)) vtc_fatal(vl, "Shutdown failed: %s", strerror(errno)); From nils.goroll at uplex.de Tue May 20 09:12:05 2025 From: nils.goroll at uplex.de (Nils Goroll) Date: Tue, 20 May 2025 09:12:05 +0000 (UTC) Subject: [master] 0b16bb4c1 vtc: Log why execution is aborted Message-ID: <20250520091205.8F3201201D5@lists.varnish-cache.org> commit 0b16bb4c15dd55b8d51a68ebbf868743d7a363be Author: Dridi Boukelmoune Date: Mon May 6 14:32:15 2024 +0200 vtc: Log why execution is aborted diff --git a/bin/varnishtest/vtc.c b/bin/varnishtest/vtc.c index 2ac436700..60e1dd7c4 100644 --- a/bin/varnishtest/vtc.c +++ b/bin/varnishtest/vtc.c @@ -373,8 +373,11 @@ parse_string(struct vtclog *vl, void *priv, const char *spec) e = strchr(buf, '\0'); AN(e); for (p = buf; p < e; p++) { - if (vtc_error || vtc_stop) + if (vtc_error || vtc_stop) { + vtc_log(vl, 1, "Aborting execution, test %s", + vtc_error ? "failed" : "ended"); break; + } /* Start of line */ if (isspace(*p)) continue; From nils.goroll at uplex.de Tue May 20 09:12:05 2025 From: nils.goroll at uplex.de (Nils Goroll) Date: Tue, 20 May 2025 09:12:05 +0000 (UTC) Subject: [master] 288cd8d4a vtc: Break out of pointless loops Message-ID: <20250520091205.A7F131201D8@lists.varnish-cache.org> commit 288cd8d4af42ba571ca60011fc1d144f317b26fa Author: Dridi Boukelmoune Date: Mon May 6 14:33:21 2024 +0200 vtc: Break out of pointless loops diff --git a/bin/varnishtest/vtc.c b/bin/varnishtest/vtc.c index 60e1dd7c4..1a2e5d828 100644 --- a/bin/varnishtest/vtc.c +++ b/bin/varnishtest/vtc.c @@ -487,7 +487,7 @@ parse_string(struct vtclog *vl, void *priv, const char *spec) if (!strcmp(token_s[0], "loop")) { n = strtoul(token_s[1], NULL, 0); - for (m = 0; m < n; m++) { + for (m = 0; m < n && !vtc_error && !vtc_stop; m++) { vtc_log(vl, 4, "Loop #%u", m); parse_string(vl, priv, token_s[2]); } From nils.goroll at uplex.de Tue May 20 09:15:04 2025 From: nils.goroll at uplex.de (Nils Goroll) Date: Tue, 20 May 2025 09:15:04 +0000 (UTC) Subject: [master] 30f71f824 vtc: Use proper ETags in r2422 Message-ID: <20250520091504.EB960120812@lists.varnish-cache.org> commit 30f71f824e5cb25b9bf81cc5fadd20f08c6645f1 Author: Dridi Boukelmoune Date: Mon May 6 14:35:14 2024 +0200 vtc: Use proper ETags in r2422 diff --git a/bin/varnishtest/tests/r02422.vtc b/bin/varnishtest/tests/r02422.vtc index 9c956f566..a7e49315d 100644 --- a/bin/varnishtest/tests/r02422.vtc +++ b/bin/varnishtest/tests/r02422.vtc @@ -3,12 +3,12 @@ varnishtest "long polling and low latency using req.ttl" server s1 { # s1 uses the etag as a version rxreq - txresp -hdr "Etag: 5" + txresp -hdr {Etag: "5"} rxreq # wait until the new version is ready delay 1 - txresp -hdr "Etag: 6" + txresp -hdr {Etag: "6"} } -start varnish v1 -cliok "param.set default_grace 0" @@ -43,7 +43,7 @@ client c0 { txreq rxresp expect resp.status == 200 - expect resp.http.ETag == 5 + expect resp.http.ETag == {"5"} expect resp.http.Hit == false # let all other clients send their requests barrier b2 sync @@ -55,37 +55,37 @@ client c1 { txreq rxresp expect resp.status == 200 - expect resp.http.ETag == 5 + expect resp.http.ETag == {"5"} expect resp.http.Hit == true } -start client c2 { # late client, immediate hit barrier b2 sync - txreq -hdr "If-None-Match: 2" + txreq -hdr {If-None-Match: "2"} rxresp expect resp.status == 200 - expect resp.http.ETag == 5 + expect resp.http.ETag == {"5"} expect resp.http.Hit == true } -start client c3 { # late client, immediate hit barrier b2 sync - txreq -hdr "If-None-Match: 4" + txreq -hdr {If-None-Match: "4"} rxresp expect resp.status == 200 - expect resp.http.ETag == 5 + expect resp.http.ETag == {"5"} expect resp.http.Hit == true } -start client c4 { # up-to-date client, long polling barrier b2 sync - txreq -hdr "If-None-Match: 5" + txreq -hdr {If-None-Match: "5"} rxresp expect resp.status == 200 - expect resp.http.ETag == 6 + expect resp.http.ETag == {"6"} expect resp.http.Hit == false } -start @@ -94,10 +94,10 @@ client c5 { barrier b2 sync # wait to make sure c4 gets the miss delay 0.2 - txreq -hdr "If-None-Match: 5" + txreq -hdr {If-None-Match: "5"} rxresp expect resp.status == 200 - expect resp.http.ETag == 6 + expect resp.http.ETag == {"6"} expect resp.http.Hit == true } -start @@ -106,10 +106,10 @@ client c6 { barrier b2 sync # wait to make sure c4 gets the miss delay 0.2 - txreq -hdr "If-None-Match: 5" + txreq -hdr {If-None-Match: "5"} rxresp expect resp.status == 200 - expect resp.http.ETag == 6 + expect resp.http.ETag == {"6"} expect resp.http.Hit == true } -start From nils.goroll at uplex.de Tue May 20 09:15:05 2025 From: nils.goroll at uplex.de (Nils Goroll) Date: Tue, 20 May 2025 09:15:05 +0000 (UTC) Subject: [master] 4aa8d31f9 vtc: Proper synchronization in r2422 Message-ID: <20250520091505.0BD98120815@lists.varnish-cache.org> commit 4aa8d31f92283c154a8faeb7b56915015c0552f4 Author: Dridi Boukelmoune Date: Mon May 6 14:36:02 2024 +0200 vtc: Proper synchronization in r2422 diff --git a/bin/varnishtest/tests/r02422.vtc b/bin/varnishtest/tests/r02422.vtc index a7e49315d..7a51689d2 100644 --- a/bin/varnishtest/tests/r02422.vtc +++ b/bin/varnishtest/tests/r02422.vtc @@ -1,11 +1,21 @@ varnishtest "long polling and low latency using req.ttl" +# synchronizes the setup of all clients +barrier b1 cond 2 + +# makes c1..cN send their requests simultaneously-ish +barrier b2 cond 7 + +# synchronizes c4 with c5 and c6 +barrier b3 cond 3 + server s1 { # s1 uses the etag as a version rxreq txresp -hdr {Etag: "5"} rxreq + barrier b3 sync # wait until the new version is ready delay 1 txresp -hdr {Etag: "6"} @@ -30,12 +40,6 @@ varnish v1 -vcl+backend { } } -start -# synchronizes the setup of all clients -barrier b1 cond 2 - -# makes c1..cN send their requests simultaneously-ish -barrier b2 cond 7 - client c0 { # wait for all clients to be ready barrier b1 sync @@ -93,7 +97,7 @@ client c5 { # up-to-date client, long polling barrier b2 sync # wait to make sure c4 gets the miss - delay 0.2 + barrier b3 sync txreq -hdr {If-None-Match: "5"} rxresp expect resp.status == 200 @@ -105,7 +109,7 @@ client c6 { # up-to-date client, long polling barrier b2 sync # wait to make sure c4 gets the miss - delay 0.2 + barrier b3 sync txreq -hdr {If-None-Match: "5"} rxresp expect resp.status == 200 From nils.goroll at uplex.de Wed May 21 08:22:05 2025 From: nils.goroll at uplex.de (Nils Goroll) Date: Wed, 21 May 2025 08:22:05 +0000 (UTC) Subject: [master] da49f394b varnishtest: Fix argument processing with vtc_gzip_cmd() Message-ID: <20250521082205.DECF4639AB@lists.varnish-cache.org> commit da49f394b43f132525438468d8ff7b4307cbee86 Author: Nils Goroll Date: Wed May 21 10:17:34 2025 +0200 varnishtest: Fix argument processing with vtc_gzip_cmd() vtc_gzip_cmd() returns 0 for "not a gzip argument", 1 for "gzip argument with value" and 2 for "same and also add content-encoding". For 2, the argument vector was increased by 2 also, wrongly skipping an additional argument of av (and causing an out-of-bounds access, which usually was covered but did surface while working on an unrelated). diff --git a/bin/varnishtest/tests/a00011.vtc b/bin/varnishtest/tests/a00011.vtc index fab31a883..9819e4934 100644 --- a/bin/varnishtest/tests/a00011.vtc +++ b/bin/varnishtest/tests/a00011.vtc @@ -7,7 +7,7 @@ server s1 { gunzip expect req.bodylen == "3" expect req.http.content-encoding == "gzip" - txresp -gzipbody FOO + txresp -gzipbody FOO -gziplevel 9 } -start client c1 -connect ${s1_sock} { diff --git a/bin/varnishtest/vtc_http.c b/bin/varnishtest/vtc_http.c index 4ed2be366..a5f701862 100644 --- a/bin/varnishtest/vtc_http.c +++ b/bin/varnishtest/vtc_http.c @@ -862,7 +862,7 @@ http_tx_parse_args(char * const *av, struct vtclog *vl, struct http *hp, l = vtc_gzip_cmd(hp, av, &body, &bodylen); if (l == 0) break; - av += l; + av++; if (l > 1) VSB_printf(hp->vsb, "Content-Encoding: gzip%s", nl); } else diff --git a/bin/varnishtest/vtc_http2.c b/bin/varnishtest/vtc_http2.c index 7feeb42b0..827e493fc 100644 --- a/bin/varnishtest/vtc_http2.c +++ b/bin/varnishtest/vtc_http2.c @@ -1600,7 +1600,7 @@ cmd_tx11obj(CMD_ARGS) i = vtc_gzip_cmd(s->hp, av, &body, &bodylen); if (i == 0) break; - av += i; + av++; if (i > 1) { ENC(hdr, ":content-encoding", "gzip"); f.flags &= ~END_STREAM; From dridi.boukelmoune at gmail.com Wed May 21 09:35:04 2025 From: dridi.boukelmoune at gmail.com (Dridi Boukelmoune) Date: Wed, 21 May 2025 09:35:04 +0000 (UTC) Subject: [master] 941536293 build: Link vtest to varnishtest Message-ID: <20250521093504.CE3771004DB@lists.varnish-cache.org> commit 94153629361e2cd8622a360cd2ddc097a1ebab23 Author: Dridi Boukelmoune Date: Wed May 21 11:31:00 2025 +0200 build: Link vtest to varnishtest $ file bin/varnishtest/varnishtest bin/varnishtest/varnishtest: POSIX shell script, ASCII text executable This script takes care of invoking the actual varnishtest executable with a proper environment in order to link shared libraries from this build tree first, and otherwise from the system-wide locations. diff --git a/bin/varnishtest/Makefile.am b/bin/varnishtest/Makefile.am index 411be155d..287d1eaa7 100644 --- a/bin/varnishtest/Makefile.am +++ b/bin/varnishtest/Makefile.am @@ -17,7 +17,7 @@ bin_PROGRAMS = varnishtest all-local: vtest vtest: varnishtest - ln -f .libs/varnishtest vtest + ln -f varnishtest vtest install-exec-hook: ln -f $(DESTDIR)$(bindir)/varnishtest$(EXEEXT) \ From nils.goroll at uplex.de Wed May 21 09:51:15 2025 From: nils.goroll at uplex.de (Nils Goroll) Date: Wed, 21 May 2025 11:51:15 +0200 Subject: [master] 941536293 build: Link vtest to varnishtest In-Reply-To: <20250521093504.CE3771004DB@lists.varnish-cache.org> References: <20250521093504.CE3771004DB@lists.varnish-cache.org> Message-ID: <57010be9-de8e-439f-a5e1-2a705296cb85@uplex.de> Hi, I had the same before, but it does not work with https://github.com/nigoroll/vtest2/blob/main/tests/a00000.vtc#L3 vtc_main outputs argv[0], and when linking the shell script, that remains "varnishtest". Yes, we could also change the test to accept both, but I thought this way around was cleaner. On 21.05.25 11:35, Dridi Boukelmoune wrote: > > commit 94153629361e2cd8622a360cd2ddc097a1ebab23 > Author: Dridi Boukelmoune > Date: Wed May 21 11:31:00 2025 +0200 > > build: Link vtest to varnishtest > > $ file bin/varnishtest/varnishtest > bin/varnishtest/varnishtest: POSIX shell script, ASCII text executable > > This script takes care of invoking the actual varnishtest executable > with a proper environment in order to link shared libraries from this > build tree first, and otherwise from the system-wide locations. > > diff --git a/bin/varnishtest/Makefile.am b/bin/varnishtest/Makefile.am > index 411be155d..287d1eaa7 100644 > --- a/bin/varnishtest/Makefile.am > +++ b/bin/varnishtest/Makefile.am > @@ -17,7 +17,7 @@ bin_PROGRAMS = varnishtest > all-local: vtest > > vtest: varnishtest > - ln -f .libs/varnishtest vtest > + ln -f varnishtest vtest > > install-exec-hook: > ln -f $(DESTDIR)$(bindir)/varnishtest$(EXEEXT) \ > _______________________________________________ > varnish-commit mailing list > varnish-commit at varnish-cache.org > https://www.varnish-cache.org/lists/mailman/listinfo/varnish-commit -- Nils Goroll (he/him) ** * * UPLEX - Nils Goroll Systemoptimierung Scheffelstra?e 32 22301 Hamburg tel +49 40 28805731 mob +49 170 2723133 fax +49 40 42949753 xmpp://slink at jabber.int.uplex.de/ http://uplex.de/ -------------- next part -------------- A non-text attachment was scrubbed... Name: OpenPGP_0x1DCD8F57A3868BD7.asc Type: application/pgp-keys Size: 3943 bytes Desc: OpenPGP public key URL: -------------- next part -------------- A non-text attachment was scrubbed... Name: OpenPGP_signature.asc Type: application/pgp-signature Size: 488 bytes Desc: OpenPGP digital signature URL: From dridi at varni.sh Wed May 21 10:09:37 2025 From: dridi at varni.sh (Dridi Boukelmoune) Date: Wed, 21 May 2025 10:09:37 +0000 Subject: [master] 941536293 build: Link vtest to varnishtest In-Reply-To: <57010be9-de8e-439f-a5e1-2a705296cb85@uplex.de> References: <20250521093504.CE3771004DB@lists.varnish-cache.org> <57010be9-de8e-439f-a5e1-2a705296cb85@uplex.de> Message-ID: On Wed, May 21, 2025 at 9:51?AM Nils Goroll wrote: > > Hi, > > I had the same before, but it does not work with > https://github.com/nigoroll/vtest2/blob/main/tests/a00000.vtc#L3 > > vtc_main outputs argv[0], and when linking the shell script, that remains > "varnishtest". Yes, we could also change the test to accept both, but I thought > this way around was cleaner. I'm ready to push an updated a0, just waiting for the test suite to complete for good measure. Dridi From dridi.boukelmoune at gmail.com Wed May 21 10:11:05 2025 From: dridi.boukelmoune at gmail.com (Dridi Boukelmoune) Date: Wed, 21 May 2025 10:11:05 +0000 (UTC) Subject: [master] b98cd3688 vtc: Expect varnishtest spelled out in a0 Message-ID: <20250521101105.176C0101ECE@lists.varnish-cache.org> commit b98cd36888bdaf20a59d214c62c3bb97f601c35a Author: Dridi Boukelmoune Date: Wed May 21 12:07:13 2025 +0200 vtc: Expect varnishtest spelled out in a0 Refs 94153629361e2cd8622a360cd2ddc097a1ebab23 diff --git a/bin/varnishtest/tests/a00000.vtc b/bin/varnishtest/tests/a00000.vtc index 1d6292fad..2d724e80f 100644 --- a/bin/varnishtest/tests/a00000.vtc +++ b/bin/varnishtest/tests/a00000.vtc @@ -1,6 +1,6 @@ vtest "Test vtest itself" -shell -exit 1 -expect {vtest [options]} {vtest -h} +shell -exit 1 -expect {varnishtest [options]} {vtest -h} shell -exit 1 -match {-D.*Define macro} {vtest -h} From nils.goroll at uplex.de Wed May 21 11:29:19 2025 From: nils.goroll at uplex.de (Nils Goroll) Date: Wed, 21 May 2025 13:29:19 +0200 Subject: [master] 941536293 build: Link vtest to varnishtest In-Reply-To: References: <20250521093504.CE3771004DB@lists.varnish-cache.org> <57010be9-de8e-439f-a5e1-2a705296cb85@uplex.de> Message-ID: <902b349d-fbd4-4295-9111-f6d151d0e604@uplex.de> On 21.05.25 12:09, Dridi Boukelmoune wrote: > I'm ready to push an updated a0, just waiting for the test suite to > complete for good measure. I did not explain enough, the update you pushed is still not a solution: we would need to match on both varnishtest and vtest for a shared code base because the program is called vtest in vtest and varnishtest in varnish-cache. Nils -- Nils Goroll (he/him) ** * * UPLEX - Nils Goroll Systemoptimierung Scheffelstra?e 32 22301 Hamburg tel +49 40 28805731 mob +49 170 2723133 fax +49 40 42949753 xmpp://slink at jabber.int.uplex.de/ http://uplex.de/ -------------- next part -------------- A non-text attachment was scrubbed... Name: OpenPGP_0x1DCD8F57A3868BD7.asc Type: application/pgp-keys Size: 3943 bytes Desc: OpenPGP public key URL: -------------- next part -------------- A non-text attachment was scrubbed... Name: OpenPGP_signature.asc Type: application/pgp-signature Size: 488 bytes Desc: OpenPGP digital signature URL: From dridi at varni.sh Wed May 21 11:30:33 2025 From: dridi at varni.sh (Dridi Boukelmoune) Date: Wed, 21 May 2025 11:30:33 +0000 Subject: [master] 941536293 build: Link vtest to varnishtest In-Reply-To: <902b349d-fbd4-4295-9111-f6d151d0e604@uplex.de> References: <20250521093504.CE3771004DB@lists.varnish-cache.org> <57010be9-de8e-439f-a5e1-2a705296cb85@uplex.de> <902b349d-fbd4-4295-9111-f6d151d0e604@uplex.de> Message-ID: On Wed, May 21, 2025 at 11:29?AM Nils Goroll wrote: > > On 21.05.25 12:09, Dridi Boukelmoune wrote: > > I'm ready to push an updated a0, just waiting for the test suite to > > complete for good measure. > > I did not explain enough, the update you pushed is still not a solution: we > would need to match on both varnishtest and vtest for a shared code base because > the program is called vtest in vtest and varnishtest in varnish-cache. Sure, I could have done something like `-match "v.*test \[options]"` but we can take care of that when we cross that bridge. From nils.goroll at uplex.de Wed May 21 11:36:50 2025 From: nils.goroll at uplex.de (Nils Goroll) Date: Wed, 21 May 2025 13:36:50 +0200 Subject: [master] 941536293 build: Link vtest to varnishtest In-Reply-To: References: <20250521093504.CE3771004DB@lists.varnish-cache.org> <57010be9-de8e-439f-a5e1-2a705296cb85@uplex.de> <902b349d-fbd4-4295-9111-f6d151d0e604@uplex.de> Message-ID: <22410979-41f5-47b5-a4dc-ca5a4f627627@uplex.de> On 21.05.25 13:30, Dridi Boukelmoune wrote: > Sure, I could have done something like `-match "v.*test \[options]"` > but we can take care of that when we cross that bridge. I deliberately did it _before_ crossing the bridge to better automate the process and, consequently, make it more reliable and it seems to me that now we debate a detail for no good reason. Nils -- Nils Goroll (he/him) ** * * UPLEX - Nils Goroll Systemoptimierung Scheffelstra?e 32 22301 Hamburg tel +49 40 28805731 mob +49 170 2723133 fax +49 40 42949753 xmpp://slink at jabber.int.uplex.de/ http://uplex.de/ -------------- next part -------------- A non-text attachment was scrubbed... Name: OpenPGP_0x1DCD8F57A3868BD7.asc Type: application/pgp-keys Size: 3943 bytes Desc: OpenPGP public key URL: -------------- next part -------------- A non-text attachment was scrubbed... Name: OpenPGP_signature.asc Type: application/pgp-signature Size: 488 bytes Desc: OpenPGP digital signature URL: From nils.goroll at uplex.de Wed May 21 13:00:44 2025 From: nils.goroll at uplex.de (Nils Goroll) Date: Wed, 21 May 2025 15:00:44 +0200 Subject: [master] 941536293 build: Link vtest to varnishtest In-Reply-To: <22410979-41f5-47b5-a4dc-ca5a4f627627@uplex.de> References: <20250521093504.CE3771004DB@lists.varnish-cache.org> <57010be9-de8e-439f-a5e1-2a705296cb85@uplex.de> <902b349d-fbd4-4295-9111-f6d151d0e604@uplex.de> <22410979-41f5-47b5-a4dc-ca5a4f627627@uplex.de> Message-ID: <8d943b20-bc41-4324-8948-68264aa55d46@uplex.de> Dridi, can you please give me a good reason why we need to have 94153629361e2cd8622a360cd2ddc097a1ebab23 ? If that does not exist, I would prefer to revert On 21.05.25 13:36, Nils Goroll wrote: > On 21.05.25 13:30, Dridi Boukelmoune wrote: >> Sure, I could have done something like `-match "v.*test \[options]"` >> but we can take care of that when we cross that bridge. > > I deliberately did it _before_ crossing the bridge to better automate the > process and, consequently, make it more reliable and it seems to me that now we > debate a detail for no good reason. > > Nils > -- Nils Goroll (he/him) ** * * UPLEX - Nils Goroll Systemoptimierung Scheffelstra?e 32 22301 Hamburg tel +49 40 28805731 mob +49 170 2723133 fax +49 40 42949753 xmpp://slink at jabber.int.uplex.de/ http://uplex.de/ -------------- next part -------------- A non-text attachment was scrubbed... Name: OpenPGP_0x1DCD8F57A3868BD7.asc Type: application/pgp-keys Size: 3943 bytes Desc: OpenPGP public key URL: -------------- next part -------------- A non-text attachment was scrubbed... Name: OpenPGP_signature.asc Type: application/pgp-signature Size: 488 bytes Desc: OpenPGP digital signature URL: From nils.goroll at uplex.de Wed May 21 13:02:04 2025 From: nils.goroll at uplex.de (Nils Goroll) Date: Wed, 21 May 2025 13:02:04 +0000 (UTC) Subject: [master] 8a95526e8 github: bring back blank issues Message-ID: <20250521130204.E073A1084FC@lists.varnish-cache.org> commit 8a95526e83a33039cf1130fb4a3a41f6b7fa7beb Author: Nils Goroll Date: Wed May 21 14:58:12 2025 +0200 github: bring back blank issues I appreciate having the bug form, but it really bugs me (pun intended) to have to use a form if it is not helpful. diff --git a/.github/ISSUE_TEMPLATE/config.yml b/.github/ISSUE_TEMPLATE/config.yml index 3c41b4b1e..8ca24cae9 100644 --- a/.github/ISSUE_TEMPLATE/config.yml +++ b/.github/ISSUE_TEMPLATE/config.yml @@ -1,4 +1,4 @@ -blank_issues_enabled: false +blank_issues_enabled: true contact_links: - name: Getting Help url: https://varnish-cache.org/support/index.html From dridi at varni.sh Wed May 21 13:05:22 2025 From: dridi at varni.sh (Dridi Boukelmoune) Date: Wed, 21 May 2025 13:05:22 +0000 Subject: [master] 941536293 build: Link vtest to varnishtest In-Reply-To: <8d943b20-bc41-4324-8948-68264aa55d46@uplex.de> References: <20250521093504.CE3771004DB@lists.varnish-cache.org> <57010be9-de8e-439f-a5e1-2a705296cb85@uplex.de> <902b349d-fbd4-4295-9111-f6d151d0e604@uplex.de> <22410979-41f5-47b5-a4dc-ca5a4f627627@uplex.de> <8d943b20-bc41-4324-8948-68264aa55d46@uplex.de> Message-ID: On Wed, May 21, 2025 at 1:00?PM Nils Goroll wrote: > > Dridi, can you please give me a good reason why we need to have > 94153629361e2cd8622a360cd2ddc097a1ebab23 ? > > If that does not exist, I would prefer to revert I pay attention to CI failures, I just wish our sanitizers build wasn't so fragile. https://github.com/varnishcache/varnish-cache/pull/4073#issuecomment-2897263529 Also, the reason *is* in the commit message. Dridi From dridi.boukelmoune at gmail.com Wed May 21 13:10:05 2025 From: dridi.boukelmoune at gmail.com (Dridi Boukelmoune) Date: Wed, 21 May 2025 13:10:05 +0000 (UTC) Subject: [master] 0a631fd05 vtc: Relax v{arnish,}test check in a0 Message-ID: <20250521131005.858FE108C21@lists.varnish-cache.org> commit 0a631fd050693055a874404826b2f83f6705dfc0 Author: Dridi Boukelmoune Date: Wed May 21 15:09:02 2025 +0200 vtc: Relax v{arnish,}test check in a0 diff --git a/bin/varnishtest/tests/a00000.vtc b/bin/varnishtest/tests/a00000.vtc index 2d724e80f..916a7a3bc 100644 --- a/bin/varnishtest/tests/a00000.vtc +++ b/bin/varnishtest/tests/a00000.vtc @@ -1,6 +1,6 @@ vtest "Test vtest itself" -shell -exit 1 -expect {varnishtest [options]} {vtest -h} +shell -exit 1 -match {v.*test \[options]} {vtest -h} shell -exit 1 -match {-D.*Define macro} {vtest -h} From nils.goroll at uplex.de Wed May 21 13:12:40 2025 From: nils.goroll at uplex.de (Nils Goroll) Date: Wed, 21 May 2025 15:12:40 +0200 Subject: [master] 941536293 build: Link vtest to varnishtest In-Reply-To: References: <20250521093504.CE3771004DB@lists.varnish-cache.org> <57010be9-de8e-439f-a5e1-2a705296cb85@uplex.de> <902b349d-fbd4-4295-9111-f6d151d0e604@uplex.de> <22410979-41f5-47b5-a4dc-ca5a4f627627@uplex.de> <8d943b20-bc41-4324-8948-68264aa55d46@uplex.de> Message-ID: <92b6cefa-5b4e-4039-936e-59f6f8dd3fba@uplex.de> On 21.05.25 15:05, Dridi Boukelmoune wrote: > Also, the reason*is* in the commit message. Ah, thank you and sorry, now I see it. So we should add a match for vtest _or_ varnishtest in the test case. I can look after that -- Nils Goroll (he/him) ** * * UPLEX - Nils Goroll Systemoptimierung Scheffelstra?e 32 22301 Hamburg tel +49 40 28805731 mob +49 170 2723133 fax +49 40 42949753 xmpp://slink at jabber.int.uplex.de/ http://uplex.de/ -------------- next part -------------- A non-text attachment was scrubbed... Name: OpenPGP_0x1DCD8F57A3868BD7.asc Type: application/pgp-keys Size: 3943 bytes Desc: OpenPGP public key URL: -------------- next part -------------- A non-text attachment was scrubbed... Name: OpenPGP_signature.asc Type: application/pgp-signature Size: 488 bytes Desc: OpenPGP digital signature URL: From nils.goroll at uplex.de Wed May 21 13:16:47 2025 From: nils.goroll at uplex.de (Nils Goroll) Date: Wed, 21 May 2025 15:16:47 +0200 Subject: [master] 0a631fd05 vtc: Relax v{arnish,}test check in a0 In-Reply-To: <20250521131005.858FE108C21@lists.varnish-cache.org> References: <20250521131005.858FE108C21@lists.varnish-cache.org> Message-ID: On 21.05.25 15:10, Dridi Boukelmoune wrote: > vtc: Relax v{arnish,}test check in a0 thank you! -- Nils Goroll (he/him) ** * * UPLEX - Nils Goroll Systemoptimierung Scheffelstra?e 32 22301 Hamburg tel +49 40 28805731 mob +49 170 2723133 fax +49 40 42949753 xmpp://slink at jabber.int.uplex.de/ http://uplex.de/ -------------- next part -------------- A non-text attachment was scrubbed... Name: OpenPGP_0x1DCD8F57A3868BD7.asc Type: application/pgp-keys Size: 3943 bytes Desc: OpenPGP public key URL: -------------- next part -------------- A non-text attachment was scrubbed... Name: OpenPGP_signature.asc Type: application/pgp-signature Size: 488 bytes Desc: OpenPGP digital signature URL: From dridi.boukelmoune at gmail.com Wed May 21 17:13:05 2025 From: dridi.boukelmoune at gmail.com (Dridi Boukelmoune) Date: Wed, 21 May 2025 17:13:05 +0000 (UTC) Subject: [master] c7e93010a doc: Add a definition for VUT in VTLA Message-ID: <20250521171305.B5CD5114645@lists.varnish-cache.org> commit c7e93010aa5b743ebbb8d6a6b09dcf196b63acc8 Author: Audun-Marius Gangst? Date: Wed May 21 18:05:41 2025 +0200 doc: Add a definition for VUT in VTLA diff --git a/doc/sphinx/reference/vtla.rst b/doc/sphinx/reference/vtla.rst index e155926c2..9bf19e603 100644 --- a/doc/sphinx/reference/vtla.rst +++ b/doc/sphinx/reference/vtla.rst @@ -122,6 +122,9 @@ VUG developers of Varnish Cache gather to share experiences and plan future development. +VUT + Varnish UTilities -- An API for client utilities to tap into VSM or VSC. + VWx Varnish Waiter 'x' -- A code module to monitor idle sessions. From nils.goroll at uplex.de Thu May 22 07:46:05 2025 From: nils.goroll at uplex.de (Nils Goroll) Date: Thu, 22 May 2025 07:46:05 +0000 (UTC) Subject: [master] b9a204082 vdire: plug the remaining vcl temperature change races Message-ID: <20250522074605.5450510838C@lists.varnish-cache.org> commit b9a204082d73f3347b95e2c58e840849888d4ce2 Author: Nils Goroll Date: Tue Feb 4 18:34:25 2025 +0100 vdire: plug the remaining vcl temperature change races The context for this change is #4142, but while it builds upon it, it is still causally unrelated. To motivate this change, let's look at vcl_set_state() from before, and consider some non-issues and issues: transition to COLD ------------------ Lck_Lock(&vcl_mtx); vcl->temp = VTAILQ_EMPTY(&vcl->ref_list) ? VCL_TEMP_COLD : VCL_TEMP_COOLING; Lck_Unlock(&vcl_mtx); // *A* vcl_BackendEvent(vcl, VCL_EVENT_COLD); non-issues: At point *A*, backends could get added. For VCL_TEMP_COOLING, this fails (see VRT_AddDirector()). For VCL_TEMP_COLD, the state is consistent, because backends get created cold and that's it. At point *A*, backends could get removed. vdire_resign() from #4142 ensures that the temperature passed to vcldir_retire() is consistent with the events the backends have received. transition to WARM (success) ---------------------------- Lck_Lock(&vcl_mtx); vcl->temp = VCL_TEMP_WARM; Lck_Unlock(&vcl_mtx); // *B* ... i = vcl_send_event(vcl, VCL_EVENT_WARM, msg); if (i == 0) { // ... *B* vcl_BackendEvent(vcl, VCL_EVENT_WARM); break; } issues: (1) In region *B*, backends could get removed. They will not have received a WARM event, but will be called with a WARM temperature, so they will receive a bogus COLD event. (2) Also in region *B*, backends could get added. They will implicitly receive a WARM event (see VDI_event() in VRT_AddDirector()), and then another from vcl_BackendEvent() transition to WARM (failed) --------------------------- AZ(vcl_send_event(vcl, VCL_EVENT_COLD, &nomsg)); AZ(nomsg); vcl->temp = VCL_TEMP_COLD; issues: (3) Backends added in region *B* will have received the implicit WARM event, and thus need a COLD event for the "cancel". To solve these issues, we need to do two things: There needs to be some kind of transaction which begins with the temperature change and ends when all backends have been notified appropriately. Backends can not get deleted while the transaction is in progress. We need a notion of "backends from before the temperature change" and "backends from after". The first part is already delivered by #4142: The vdire facility already provides the transaction during which backends do not actually get deleted and it ensures that the right temperature gets passed when the deletion is carried out. So for this part, we only need to use vdire. But issues (2) and (3) are not yet covered. For these, we add a checkpoint, such that we know which directors from the list are the "base" from before the temperature change and which are the "delta" added after it. That's this patch. vdire_start_event() atomically (under the vcl_mtx) sets the checkpoint and the new temperature. vdire_end_event() just uses the existing vdire_end_iter() and clears the checkpoint. vcl_BackendEvent() gets split into two: backend_event_base() notifies backends from before the checkpoint. backend_event_delta() atomically sets a new temperature and notifies backends from after the checkpoint (but not from after its temperature change). Fixes #4199 diff --git a/bin/varnishd/cache/cache_vcl.c b/bin/varnishd/cache/cache_vcl.c index 5a82baf21..cc9c109d1 100644 --- a/bin/varnishd/cache/cache_vcl.c +++ b/bin/varnishd/cache/cache_vcl.c @@ -436,6 +436,36 @@ vdire_end_iter(struct vdire *vdire) vcldir_retire(vdir, temp); } +/* + * like vdire_start_iter, but also prepare for backend_event_*() + * by setting the checkpoint + */ +static void +vdire_start_event(struct vdire *vdire, const struct vcltemp *temp) +{ + + CHECK_OBJ_NOTNULL(vdire, VDIRE_MAGIC); + AN(temp); + + Lck_AssertHeld(vdire->mtx); + + // https://github.com/varnishcache/varnish-cache/pull/4142#issuecomment-2593091097 + ASSERT_CLI(); + + AZ(vdire->checkpoint); + vdire->checkpoint = VTAILQ_LAST(&vdire->directors, vcldir_head); + AN(vdire->tempp); + *vdire->tempp = temp; + vdire->iterating++; +} + +static void +vdire_end_event(struct vdire *vdire) +{ + vdire->checkpoint = NULL; + vdire_end_iter(vdire); +} + // if there are no iterators, remove from directors and retire // otherwise put on resigning list to work when iterators end void @@ -557,8 +587,13 @@ VCL_IterDirector(struct cli *cli, const char *pat, return (found); } +/* + * vdire_start_event() must have been called before + * + * send event to directors pre checkpoint + */ static void -vcl_BackendEvent(const struct vcl *vcl, enum vcl_event_e e) +backend_event_base(const struct vcl *vcl, enum vcl_event_e e) { struct vcldir *vdir; struct vdire *vdire; @@ -567,11 +602,56 @@ vcl_BackendEvent(const struct vcl *vcl, enum vcl_event_e e) CHECK_OBJ_NOTNULL(vcl, VCL_MAGIC); AZ(vcl->busy); vdire = vcl->vdire; + AN(vdire->iterating); - vdire_start_iter(vdire); - VTAILQ_FOREACH(vdir, &vdire->directors, directors_list) + vdir = vdire->checkpoint; + if (vdir == NULL) + return; + + CHECK_OBJ(vdir, VCLDIR_MAGIC); + VTAILQ_FOREACH_REVERSE_FROM(vdir, &vdire->directors, + vcldir_head, directors_list) { + CHECK_OBJ(vdir, VCLDIR_MAGIC); VDI_Event(vdir->dir, e); - vdire_end_iter(vdire); + } +} + +/* + * vdire_start_event() must have been called before + * + * set a new temperature. + * send event to directors added post checkpoint, but before + * the new temperature + */ +static void +backend_event_delta(const struct vcl *vcl, enum vcl_event_e e, const struct vcltemp *temp) +{ + struct vcldir *vdir, *end; + struct vdire *vdire; + + ASSERT_CLI(); + CHECK_OBJ_NOTNULL(vcl, VCL_MAGIC); + AZ(vcl->busy); + vdire = vcl->vdire; + AN(temp); + AN(vdire->iterating); + + Lck_Lock(vdire->mtx); + if (vdire->checkpoint == NULL) + vdir = VTAILQ_FIRST(&vdire->directors); + else + vdir = VTAILQ_NEXT(vdire->checkpoint, directors_list); + AN(vdire->tempp); + *vdire->tempp = temp; + end = VTAILQ_LAST(&vdire->directors, vcldir_head); + Lck_Unlock(vdire->mtx); + + while (vdir != NULL) { + VDI_Event(vdir->dir, e); + if (vdir == end) + break; + vdir = VTAILQ_NEXT(vdire->checkpoint, directors_list); + } } static void @@ -742,10 +822,12 @@ vcl_set_state(struct vcl *vcl, const char *state, struct vsb **msg) break; if (vcl->busy == 0 && vcl->temp->is_warm) { Lck_Lock(&vcl_mtx); - vcl->temp = VTAILQ_EMPTY(&vcl->ref_list) ? - VCL_TEMP_COLD : VCL_TEMP_COOLING; + vdire_start_event(vcl->vdire, VTAILQ_EMPTY(&vcl->ref_list) ? + VCL_TEMP_COLD : VCL_TEMP_COOLING); Lck_Unlock(&vcl_mtx); - vcl_BackendEvent(vcl, VCL_EVENT_COLD); + backend_event_base(vcl, VCL_EVENT_COLD); + vdire_end_event(vcl->vdire); + // delta directors at VCL_TEMP_COLD do not need an event AZ(vcl_send_event(vcl, VCL_EVENT_COLD, msg)); AZ(*msg); } @@ -769,16 +851,19 @@ vcl_set_state(struct vcl *vcl, const char *state, struct vsb **msg) } else { Lck_Lock(&vcl_mtx); - vcl->temp = VCL_TEMP_WARM; + vdire_start_event(vcl->vdire, VCL_TEMP_WARM); Lck_Unlock(&vcl_mtx); i = vcl_send_event(vcl, VCL_EVENT_WARM, msg); if (i == 0) { - vcl_BackendEvent(vcl, VCL_EVENT_WARM); + backend_event_base(vcl, VCL_EVENT_WARM); + // delta directors are already warm + vdire_end_event(vcl->vdire); break; } AZ(vcl_send_event(vcl, VCL_EVENT_COLD, &nomsg)); AZ(nomsg); - vcl->temp = VCL_TEMP_COLD; + backend_event_delta(vcl, VCL_EVENT_COLD, VCL_TEMP_COLD); + vdire_end_event(vcl->vdire); } break; default: diff --git a/bin/varnishd/cache/cache_vcl.h b/bin/varnishd/cache/cache_vcl.h index f7fc31b60..3d8dacc66 100644 --- a/bin/varnishd/cache/cache_vcl.h +++ b/bin/varnishd/cache/cache_vcl.h @@ -50,6 +50,8 @@ struct vdire { // to signal when iterators can enter again pthread_cond_t cond; const struct vcltemp **tempp; + // last director present when vdire_start_event is called + struct vcldir *checkpoint; }; struct vcl { From nils.goroll at uplex.de Thu May 22 07:46:05 2025 From: nils.goroll at uplex.de (Nils Goroll) Date: Thu, 22 May 2025 07:46:05 +0000 (UTC) Subject: [master] 52e0dbf35 cache_vcl: Add missing CHECK_OBJ(vdir, VCLDIR_MAGIC) Message-ID: <20250522074605.65E6010838E@lists.varnish-cache.org> commit 52e0dbf35be0e7d3480e19fa32a907452050929d Author: Nils Goroll Date: Thu May 22 09:36:08 2025 +0200 cache_vcl: Add missing CHECK_OBJ(vdir, VCLDIR_MAGIC) Add for cases where we de-reference vdir diff --git a/bin/varnishd/cache/cache_vcl.c b/bin/varnishd/cache/cache_vcl.c index cc9c109d1..6a17004e1 100644 --- a/bin/varnishd/cache/cache_vcl.c +++ b/bin/varnishd/cache/cache_vcl.c @@ -502,6 +502,7 @@ vdire_iter(struct cli *cli, const char *pat, const struct vcl *vcl, vdire_start_iter(vdire); VTAILQ_FOREACH(vdir, &vdire->directors, directors_list) { + CHECK_OBJ(vdir, VCLDIR_MAGIC); if (vdir->refcnt == 0) continue; if (pat != NULL && fnmatch(pat, vdir->cli_name, 0)) @@ -531,6 +532,7 @@ vcl_iterdir(struct cli *cli, const char *pat, const struct vcl *vcl, Lck_AssertHeld(&vcl_mtx); VTAILQ_FOREACH(vdir, &vcl->vdire->directors, directors_list) { + CHECK_OBJ(vdir, VCLDIR_MAGIC); if (fnmatch(pat, vdir->cli_name, 0)) continue; found++; @@ -647,6 +649,7 @@ backend_event_delta(const struct vcl *vcl, enum vcl_event_e e, const struct vclt Lck_Unlock(vdire->mtx); while (vdir != NULL) { + CHECK_OBJ(vdir, VCLDIR_MAGIC); VDI_Event(vdir->dir, e); if (vdir == end) break; @@ -677,8 +680,10 @@ vcl_KillBackends(const struct vcl *vcl) * Unlocked and sidelining vdire because no further directors can be added, and the * remaining ones need to be able to remove themselves. */ - VTAILQ_FOREACH_SAFE(vdir, &vdire->directors, directors_list, vdir2) + VTAILQ_FOREACH_SAFE(vdir, &vdire->directors, directors_list, vdir2) { + CHECK_OBJ(vdir, VCLDIR_MAGIC); VDI_Event(vdir->dir, VCL_EVENT_DISCARD); + } assert(VTAILQ_EMPTY(&vdire->directors)); } From nils.goroll at uplex.de Thu May 22 07:49:04 2025 From: nils.goroll at uplex.de (Nils Goroll) Date: Thu, 22 May 2025 07:49:04 +0000 (UTC) Subject: [master] d62373295 Stabilize tests Message-ID: <20250522074904.E6EFE10885A@lists.varnish-cache.org> commit d6237329599edd78fc4e815ced49b51380db0902 Author: Nils Goroll Date: Thu Mar 7 20:59:12 2024 +0100 Stabilize tests diff --git a/bin/varnishtest/tests/r01506.vtc b/bin/varnishtest/tests/r01506.vtc index f7f89a716..c2e3d481e 100644 --- a/bin/varnishtest/tests/r01506.vtc +++ b/bin/varnishtest/tests/r01506.vtc @@ -68,3 +68,4 @@ client c1 { } -run varnish v1 -expect sc_range_short == 0 +server s1 -wait diff --git a/bin/varnishtest/tests/r02035.vtc b/bin/varnishtest/tests/r02035.vtc index eff24620c..6f7f6dfd3 100644 --- a/bin/varnishtest/tests/r02035.vtc +++ b/bin/varnishtest/tests/r02035.vtc @@ -34,3 +34,4 @@ client c1 { barrier b1 sync } -run +server s1 -wait From nils.goroll at uplex.de Mon May 26 13:58:05 2025 From: nils.goroll at uplex.de (Nils Goroll) Date: Mon, 26 May 2025 13:58:05 +0000 (UTC) Subject: [master] 77d6d0be8 cache_vcl: Fix embarassing mistake in the new vdire facility Message-ID: <20250526135805.441EF112FC3@lists.varnish-cache.org> commit 77d6d0be8a91964e063cff98831a0ee8294a6958 Author: Nils Goroll Date: Mon May 26 15:56:44 2025 +0200 cache_vcl: Fix embarassing mistake in the new vdire facility Ref b9a204082d73f3347b95e2c58e840849888d4ce2 Spotted by Coverity, CID#1648026 diff --git a/bin/varnishd/cache/cache_vcl.c b/bin/varnishd/cache/cache_vcl.c index 6a17004e1..944834b29 100644 --- a/bin/varnishd/cache/cache_vcl.c +++ b/bin/varnishd/cache/cache_vcl.c @@ -653,7 +653,7 @@ backend_event_delta(const struct vcl *vcl, enum vcl_event_e e, const struct vclt VDI_Event(vdir->dir, e); if (vdir == end) break; - vdir = VTAILQ_NEXT(vdire->checkpoint, directors_list); + vdir = VTAILQ_NEXT(vdir, directors_list); } } From nils.goroll at uplex.de Mon May 26 15:25:07 2025 From: nils.goroll at uplex.de (Nils Goroll) Date: Mon, 26 May 2025 15:25:07 +0000 (UTC) Subject: [master] 48e21c3c5 Use vtest2 as a submodule Message-ID: <20250526152507.6DF7711C000@lists.varnish-cache.org> commit 48e21c3c5f5e73ae02912f83d96198bc140dcecf Author: Nils Goroll Date: Mon May 19 11:12:24 2025 +0200 Use vtest2 as a submodule We pull in vtest2 as a submodule and build varnishtest from those sources. As of today, the sources are identical and development will continue in vtest2. To build from git, either use "git clone -r" for the initial clone, or use "git submodule update --init" on an existing repository. This is also explained in changes.rst and recommended by configure if it does not find the vtest2 sources. The vtest2 reference commit can be updated by calling "make update". Other changes concerning varnishtest/Makefile.am are partly due to the existing code partly not being entirely correct. For example, explicitly using "> $(builddir)/teken_state.h" should not be necessary and built sources should be added to BUILT_SOURCES. We remove local copies of the varnishtest code and all a*.vtc test cases, which now live in vtest2. Next step in implementing #3983 diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 000000000..b7404dca4 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "bin/varnishtest/vtest2"] + path = bin/varnishtest/vtest2 + url = https://github.com/vtest/VTest2.git diff --git a/Makefile.am b/Makefile.am index e050e2ba8..934fb4553 100644 --- a/Makefile.am +++ b/Makefile.am @@ -89,4 +89,9 @@ witness.dot: all witness: witness.svg -.PHONY: cscope witness.dot +update: + git submodule sync + git submodule update --remote bin/varnishtest/vtest2 + git commit -m 'Update vtest2' bin/varnishtest/vtest2 || true + +.PHONY: cscope witness.dot update diff --git a/bin/varnishtest/Makefile.am b/bin/varnishtest/Makefile.am index 287d1eaa7..3a2235889 100644 --- a/bin/varnishtest/Makefile.am +++ b/bin/varnishtest/Makefile.am @@ -9,6 +9,7 @@ DISTCLEANFILES = _.ok AM_CPPFLAGS = \ -I$(top_srcdir)/include \ -I$(top_builddir)/include \ + -I$(top_builddir) \ -I$(top_srcdir)/lib/libvgz bin_PROGRAMS = varnishtest @@ -26,44 +27,45 @@ install-exec-hook: uninstall-hook: rm -f $(DESTDIR)$(bindir)/vtest$(EXEEXT) +vtest2_src = vtest2/src + varnishtest_SOURCES = \ - hpack.h \ - cmds.h \ - vtc.h \ - teken.c \ - teken.h \ - teken_scs.h \ - teken_subr.h \ - teken_subr_compat.h \ - teken_wcwidth.h \ - vtc.c \ - vtc_barrier.c \ - vtc_client.c \ - vtc_gzip.c \ - vtc_haproxy.c \ - vtc_h2_dectbl.h \ - vtc_h2_enctbl.h \ - vtc_h2_hpack.c \ - vtc_h2_priv.h \ - vtc_h2_stattbl.h \ - vtc_h2_tbl.c \ - vtc_http.c \ - vtc_http.h \ - vtc_http2.c \ - vtc_log.h \ - vtc_log.c \ - vtc_logexp.c \ - vtc_misc.c \ - vtc_main.c \ - vtc_process.c \ - vtc_proxy.c \ - vtc_server.c \ - vtc_sess.c \ - vtc_subr.c \ - vtc_syslog.c \ - vtc_tunnel.c \ - vtc_varnish.c \ - vtc_vsm.c + $(vtest2_src)/hpack.h \ + $(vtest2_src)/cmds.h \ + $(vtest2_src)/vtc.h \ + $(vtest2_src)/teken.c \ + $(vtest2_src)/teken.h \ + $(vtest2_src)/teken_scs.h \ + $(vtest2_src)/teken_subr.h \ + $(vtest2_src)/teken_subr_compat.h \ + $(vtest2_src)/teken_wcwidth.h \ + $(vtest2_src)/vtc.c \ + $(vtest2_src)/vtc_barrier.c \ + $(vtest2_src)/vtc_client.c \ + $(vtest2_src)/vtc_gzip.c \ + $(vtest2_src)/vtc_haproxy.c \ + $(vtest2_src)/vtc_h2_enctbl.h \ + $(vtest2_src)/vtc_h2_hpack.c \ + $(vtest2_src)/vtc_h2_priv.h \ + $(vtest2_src)/vtc_h2_stattbl.h \ + $(vtest2_src)/vtc_h2_tbl.c \ + $(vtest2_src)/vtc_http.c \ + $(vtest2_src)/vtc_http.h \ + $(vtest2_src)/vtc_http2.c \ + $(vtest2_src)/vtc_log.h \ + $(vtest2_src)/vtc_log.c \ + $(vtest2_src)/vtc_logexp.c \ + $(vtest2_src)/vtc_misc.c \ + $(vtest2_src)/vtc_main.c \ + $(vtest2_src)/vtc_process.c \ + $(vtest2_src)/vtc_proxy.c \ + $(vtest2_src)/vtc_server.c \ + $(vtest2_src)/vtc_sess.c \ + $(vtest2_src)/vtc_subr.c \ + $(vtest2_src)/vtc_syslog.c \ + $(vtest2_src)/vtc_tunnel.c \ + $(vtest2_src)/vtc_varnish.c \ + $(vtest2_src)/vtc_vsm.c varnishtest_LDADD = \ $(top_builddir)/lib/libvarnishapi/libvarnishapi.la \ @@ -77,29 +79,33 @@ varnishtest_CFLAGS = \ -DVTEST_WITH_VTC_VSM \ -DTOP_BUILDDIR='"${top_builddir}"' -EXTRA_DIST = $(top_srcdir)/bin/varnishtest/tests/*.vtc \ +EXTRA_DIST = $(srcdir)/$(vtest2_src)/../tests/*.vtc \ + $(top_srcdir)/bin/varnishtest/tests/*.vtc \ $(top_srcdir)/bin/varnishtest/tests/common.pem \ $(top_srcdir)/bin/varnishtest/tests/README \ - $(top_srcdir)/bin/varnishtest/gensequences \ - $(top_srcdir)/bin/varnishtest/sequences \ - $(top_srcdir)/bin/varnishtest/teken.3 \ - huffman_gen.py + $(vtest2_src)/gensequences \ + $(vtest2_src)/sequences \ + $(vtest2_src)/teken.3 \ + $(vtest2_src)/huffman_gen.py \ + $(vtest2_src)/tbl/vhp_huffman.h teken.c: teken_state.h -teken_state.h: $(srcdir)/sequences $(srcdir)/gensequences - awk -f $(srcdir)/gensequences $(srcdir)/sequences \ - > $(builddir)/teken_state.h +teken_state.h: $(srcdir)/$(vtest2_src)/sequences $(srcdir)/$(vtest2_src)/gensequences + awk -f $(srcdir)/$(vtest2_src)/gensequences $(srcdir)/$(vtest2_src)/sequences \ + > $@_ + mv $@_ $@ vtc_h2_hpack.c: vtc_h2_dectbl.h -vtc_h2_dectbl.h: huffman_gen.py $(top_srcdir)/include/tbl/vhp_huffman.h - $(PYTHON) $(srcdir)/huffman_gen.py \ - $(top_srcdir)/include/tbl/vhp_huffman.h > $@_ +vtc_h2_dectbl.h: $(srcdir)/$(vtest2_src)/huffman_gen.py $(srcdir)/$(vtest2_src)/tbl/vhp_huffman.h + $(PYTHON) $(srcdir)/$(vtest2_src)/huffman_gen.py \ + $(srcdir)/$(vtest2_src)/tbl/vhp_huffman.h > $@_ mv $@_ $@ -BUILT_SOURCES = vtc_h2_dectbl.h +BUILT_SOURCES = \ + teken_state.h \ + vtc_h2_dectbl.h CLEANFILES = \ - $(builddir)/teken_state.h \ $(BUILT_SOURCES) \ vtest diff --git a/bin/varnishtest/cmds.h b/bin/varnishtest/cmds.h deleted file mode 100644 index 519ca8982..000000000 --- a/bin/varnishtest/cmds.h +++ /dev/null @@ -1,68 +0,0 @@ -/*- - * Copyright (c) 2018 Varnish Software AS - * All rights reserved. - * - * Author: Poul-Henning Kamp - * - * SPDX-License-Identifier: BSD-2-Clause - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - */ - -/*lint -save -e525 -e539 */ - -#ifndef CMD_GLOBAL - #define CMD_GLOBAL(x) -#endif -CMD_GLOBAL(barrier) -CMD_GLOBAL(delay) -CMD_GLOBAL(shell) -CMD_GLOBAL(include) -#undef CMD_GLOBAL - -#ifndef CMD_TOP - #define CMD_TOP(x) -#endif -CMD_TOP(client) -CMD_TOP(feature) -CMD_TOP(filewrite) -CMD_TOP(haproxy) -#ifdef VTEST_WITH_VTC_LOGEXPECT -CMD_TOP(logexpect) -#endif -CMD_TOP(process) -CMD_TOP(server) -CMD_TOP(setenv) -CMD_TOP(syslog) -CMD_TOP(tunnel) -#ifdef VTEST_WITH_VTC_VARNISH -CMD_TOP(varnish) -#endif -CMD_TOP(varnishtest) -#ifdef VTEST_WITH_VTC_VSM -CMD_TOP(vsm) -#endif -CMD_TOP(vtest) -#undef CMD_TOP - -/*lint -restore */ diff --git a/bin/varnishtest/flint.sh b/bin/varnishtest/flint.sh index 42519e962..7089a4ff1 100644 --- a/bin/varnishtest/flint.sh +++ b/bin/varnishtest/flint.sh @@ -9,5 +9,5 @@ FLOPS=' -DVTEST_WITH_VTC_VARNISH -DTOP_BUILDDIR="foo" -I../../lib/libvgz - *.c + *.c vtest2/src/*.c ' ../../tools/flint_skel.sh $* diff --git a/bin/varnishtest/gensequences b/bin/varnishtest/gensequences deleted file mode 100644 index 59f137977..000000000 --- a/bin/varnishtest/gensequences +++ /dev/null @@ -1,169 +0,0 @@ -#!/usr/bin/awk -f - -#- -# Copyright (c) 2008-2009 Ed Schouten -# All rights reserved. -# -# SPDX-License-Identifier: BSD-2-Clause -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# 1. Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# 2. Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# -# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND -# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE -# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS -# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) -# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY -# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF -# SUCH DAMAGE. -# -# $FreeBSD: head/sys/teken/gensequences 333925 2018-05-20 14:21:20Z dumbbell $ - -function die(msg) { - print msg; - exit 1; -} - -function cchar(str) { - if (str == "^[") - return "\\x1B"; - if (str == "SP") - return " "; - - return str; -} - -function csequence(str) { - if (str == "SP") - return " "; - - return str; -} - -BEGIN { -FS = "\t+" - -while (getline > 0) { - if (NF == 0 || $1 ~ /^#/) - continue; - - if (NF != 3 && NF != 4) - die("Invalid line layout: " NF " columns"); - - split($3, sequence, " +"); - nsequences = 0; - for (s in sequence) - nsequences++; - - prefix = ""; - l_prefix_name[""] = "teken_state_init"; - for (i = 1; i < nsequences; i++) { - n = prefix csequence(sequence[i]); - l_prefix_parent[n] = prefix; - l_prefix_suffix[n] = sequence[i]; - if (!l_prefix_name[n]) - l_prefix_name[n] = "teken_state_" "" ++npr; - prefix = n; - } - - suffix = sequence[nsequences]; - cmd = prefix suffix; - - # Fill lists - if (l_cmd_name[cmd] != "") - die(cmd " already exists"); - l_cmd_prefix[cmd] = prefix; - l_cmd_suffix[cmd] = suffix; - l_cmd_args[cmd] = $4; - l_cmd_abbr[cmd] = $1; - l_cmd_name[cmd] = $2; - l_cmd_c_name[cmd] = "teken_subr_" tolower($2); - gsub(" ", "_", l_cmd_c_name[cmd]); - - if ($4 != "") - l_prefix_numbercmds[prefix]++; -} - -print "/* Generated file. Do not edit. */"; -print ""; - -for (p in l_prefix_name) { - if (l_prefix_name[p] != "teken_state_init") - print "static teken_state_t " l_prefix_name[p] ";"; -} - -for (p in l_prefix_name) { - print ""; - print "/* '" p "' */"; - print "static void"; - print l_prefix_name[p] "(teken_t *t, teken_char_t c)"; - print "{"; - - if (l_prefix_numbercmds[p] > 0) { - print ""; - print "\tif (teken_state_numbers(t, c))"; - print "\t\treturn;"; - } - - print ""; - print "\tswitch (c) {"; - for (c in l_cmd_prefix) { - if (l_cmd_prefix[c] != p) - continue; - - print "\tcase '" cchar(l_cmd_suffix[c]) "': /* " l_cmd_abbr[c] ": " l_cmd_name[c] " */"; - - if (l_cmd_args[c] == "v") { - print "\t\t" l_cmd_c_name[c] "(t, t->t_curnum, t->t_nums);"; - } else { - printf "\t\t%s(t", l_cmd_c_name[c]; - split(l_cmd_args[c], args, " "); - for (a = 1; args[a] != ""; a++) { - if (args[a] == "n") - printf ", (t->t_curnum < %d || t->t_nums[%d] == 0) ? 1 : t->t_nums[%d]", a, (a - 1), (a - 1); - else if (args[a] == "r") - printf ", t->t_curnum < %d ? 0 : t->t_nums[%d]", a, (a - 1); - else - die("Invalid argument type: " args[a]); - } - print ");"; - } - print "\t\tbreak;"; - } - for (pc in l_prefix_parent) { - if (l_prefix_parent[pc] != p) - continue; - print "\tcase '" cchar(l_prefix_suffix[pc]) "':"; - print "\t\tteken_state_switch(t, " l_prefix_name[pc] ");"; - print "\t\treturn;"; - } - - print "\tdefault:"; - if (l_prefix_name[p] == "teken_state_init") { - print "\t\tteken_subr_regular_character(t, c);"; - } else { - print "\t\tteken_printf(\"Unsupported sequence in " l_prefix_name[p] ": %u\\n\", (unsigned int)c);"; - } - print "\t\tbreak;"; - - print "\t}"; - - if (l_prefix_name[p] != "teken_state_init") { - print ""; - print "\tt->t_last = 0;"; - print "\tteken_state_switch(t, teken_state_init);"; - } - print "}"; -} - -} diff --git a/bin/varnishtest/hpack.h b/bin/varnishtest/hpack.h deleted file mode 100644 index c38fc4d90..000000000 --- a/bin/varnishtest/hpack.h +++ /dev/null @@ -1,85 +0,0 @@ -/*- - * Copyright (c) 2008-2016 Varnish Software AS - * All rights reserved. - * - * Author: Guillaume Quintard - * - * SPDX-License-Identifier: BSD-2-Clause - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#include - -enum hpk_result{ - hpk_more = 0, - hpk_done, - hpk_err, -}; - -enum hpk_indexed { - hpk_unset = 0, - hpk_idx, - hpk_inc, - hpk_not, - hpk_never, -}; - -struct hpk_txt { - char *ptr; - int len; - int huff; -}; - -struct hpk_hdr { - struct hpk_txt key; - struct hpk_txt value; - enum hpk_indexed t; - unsigned i; -}; - -struct hpk_ctx; -struct hpk_iter; - -struct hpk_ctx * HPK_NewCtx(uint32_t tblsize); -void HPK_FreeCtx(struct hpk_ctx *ctx); - -struct hpk_iter * HPK_NewIter(struct hpk_ctx *ctx, void *buf, int size); -void HPK_FreeIter(struct hpk_iter *iter); - -enum hpk_result HPK_DecHdr(struct hpk_iter *iter, struct hpk_hdr *header); -enum hpk_result HPK_EncHdr(struct hpk_iter *iter, const struct hpk_hdr *header); - -int gethpk_iterLen(const struct hpk_iter *iter); - -enum hpk_result HPK_ResizeTbl(struct hpk_ctx *ctx, uint32_t num); - -const struct hpk_hdr * HPK_GetHdr(const struct hpk_ctx *ctx, uint32_t index); - -uint32_t HPK_GetTblSize(const struct hpk_ctx *ctx); -uint32_t HPK_GetTblMaxSize(const struct hpk_ctx *ctx); -uint32_t HPK_GetTblLength(const struct hpk_ctx *ctx); - -#if 0 -/* DEBUG */ -void dump_dyn_tbl(const struct hpk_ctx *ctx); -#endif diff --git a/bin/varnishtest/huffman_gen.py b/bin/varnishtest/huffman_gen.py deleted file mode 100755 index 4b761515b..000000000 --- a/bin/varnishtest/huffman_gen.py +++ /dev/null @@ -1,96 +0,0 @@ -#!/usr/bin/env python3 - -import re -import sys - -#HPH(0x30, 0x00000000, 5) -regex = re.compile(r"^HPH\((.{4}), (.{10}), +(.{1,3})\)") - -if len(sys.argv) != 2: - print("{} takes one and only one argument".format(sys.argv[0])) - sys.exit(2) - -class sym: - def __init__(self, bigval, bigvall, chr=0, esc=None): - self.vall = bigvall % 8 if bigvall % 8 else 8 - self.val = bigval & ((1 << self.vall) - 1) - self.pfx = (bigval >> self.vall)# & 0xff - self.chr = chr - self.esc = esc - -tbls = {} -msl = {} # max sym length - -f = open(sys.argv[1]) -for l in f: - grp = 1 - match = regex.match(l) - if not match: - continue - - char = int(match.group(grp), 16) - grp += 1 - - val = int(match.group(grp), 16) - grp += 1 - - vall = int(match.group(grp)) - - s = sym(val, vall, char) - if s.pfx not in tbls: - tbls[s.pfx] = {} - - if s.val in tbls[s.pfx]: - assert tbls[s.pfx][s.val].e - tbls[s.pfx][s.val] = s - - # add the escape entry in the "previous" table - if s.pfx: - pp = s.pfx >> 8 - pv = s.pfx & 0xff - if pp not in tbls: - tbls[pp] = {} - tbls[pp][pv] = sym(pv, 8, 0, "&tbl_{:x}".format(s.pfx)) -f.close() - -# add the EOS case -s = sym(63, 6, 0) -tbls[0xffffff][63] = s - -print('''/* NB: This file is machine generated, DO NOT EDIT! - * edit 'huffman_input' instead - */ - -struct stbl; - -struct ssym { - uint8_t csm; /* bits consumed */ - uint8_t chr; /* character */ - struct stbl *nxt; /* next table */ -}; - -struct stbl { - unsigned msk; - struct ssym *syms; -}; -''') - -for pfx in sorted(tbls.keys(), reverse=True): - msl = max([x.vall for x in tbls[pfx].values()]) - for s in tbls[pfx].values(): - s.val = s.val << (msl - s.vall) - - tbl = sorted(tbls[pfx].values(), key=lambda x: x.val) - print("\nstatic struct ssym sym_{:x}_array[] = {{".format(pfx)) - for s in tbl: - for j in range(2 ** (msl - s.vall)): - print("{} {{{}, {:3d}, {}}},".format( - "\t " if j else "/* idx {:3d} */".format(s.val + j), - s.vall, s.chr % 256, - s.esc if s.esc else "NULL")) - print('''}}; - -static struct stbl tbl_{:x} = {{ - {}, - sym_{:x}_array -}};'''.format(pfx, msl, pfx)) diff --git a/bin/varnishtest/huffman_input b/bin/varnishtest/huffman_input deleted file mode 100644 index 117f87f06..000000000 --- a/bin/varnishtest/huffman_input +++ /dev/null @@ -1,258 +0,0 @@ -# For Copyright information see RFC7541 [BSD3] - ( 0) |11111111|11000 1ff8 [13] - ( 1) |11111111|11111111|1011000 7fffd8 [23] - ( 2) |11111111|11111111|11111110|0010 fffffe2 [28] - ( 3) |11111111|11111111|11111110|0011 fffffe3 [28] - ( 4) |11111111|11111111|11111110|0100 fffffe4 [28] - ( 5) |11111111|11111111|11111110|0101 fffffe5 [28] - ( 6) |11111111|11111111|11111110|0110 fffffe6 [28] - ( 7) |11111111|11111111|11111110|0111 fffffe7 [28] - ( 8) |11111111|11111111|11111110|1000 fffffe8 [28] - ( 9) |11111111|11111111|11101010 ffffea [24] - ( 10) |11111111|11111111|11111111|111100 3ffffffc [30] - ( 11) |11111111|11111111|11111110|1001 fffffe9 [28] - ( 12) |11111111|11111111|11111110|1010 fffffea [28] - ( 13) |11111111|11111111|11111111|111101 3ffffffd [30] - ( 14) |11111111|11111111|11111110|1011 fffffeb [28] - ( 15) |11111111|11111111|11111110|1100 fffffec [28] - ( 16) |11111111|11111111|11111110|1101 fffffed [28] - ( 17) |11111111|11111111|11111110|1110 fffffee [28] - ( 18) |11111111|11111111|11111110|1111 fffffef [28] - ( 19) |11111111|11111111|11111111|0000 ffffff0 [28] - ( 20) |11111111|11111111|11111111|0001 ffffff1 [28] - ( 21) |11111111|11111111|11111111|0010 ffffff2 [28] - ( 22) |11111111|11111111|11111111|111110 3ffffffe [30] - ( 23) |11111111|11111111|11111111|0011 ffffff3 [28] - ( 24) |11111111|11111111|11111111|0100 ffffff4 [28] - ( 25) |11111111|11111111|11111111|0101 ffffff5 [28] - ( 26) |11111111|11111111|11111111|0110 ffffff6 [28] - ( 27) |11111111|11111111|11111111|0111 ffffff7 [28] - ( 28) |11111111|11111111|11111111|1000 ffffff8 [28] - ( 29) |11111111|11111111|11111111|1001 ffffff9 [28] - ( 30) |11111111|11111111|11111111|1010 ffffffa [28] - ( 31) |11111111|11111111|11111111|1011 ffffffb [28] - ' ' ( 32) |010100 14 [ 6] - '!' ( 33) |11111110|00 3f8 [10] - '"' ( 34) |11111110|01 3f9 [10] - '#' ( 35) |11111111|1010 ffa [12] - '$' ( 36) |11111111|11001 1ff9 [13] - '%' ( 37) |010101 15 [ 6] - '&' ( 38) |11111000 f8 [ 8] - ''' ( 39) |11111111|010 7fa [11] - '(' ( 40) |11111110|10 3fa [10] - ')' ( 41) |11111110|11 3fb [10] - '*' ( 42) |11111001 f9 [ 8] - '+' ( 43) |11111111|011 7fb [11] - ',' ( 44) |11111010 fa [ 8] - '-' ( 45) |010110 16 [ 6] - '.' ( 46) |010111 17 [ 6] - '/' ( 47) |011000 18 [ 6] - '0' ( 48) |00000 0 [ 5] - '1' ( 49) |00001 1 [ 5] - '2' ( 50) |00010 2 [ 5] - '3' ( 51) |011001 19 [ 6] - '4' ( 52) |011010 1a [ 6] - '5' ( 53) |011011 1b [ 6] - '6' ( 54) |011100 1c [ 6] - '7' ( 55) |011101 1d [ 6] - '8' ( 56) |011110 1e [ 6] - '9' ( 57) |011111 1f [ 6] - ':' ( 58) |1011100 5c [ 7] - ';' ( 59) |11111011 fb [ 8] - '<' ( 60) |11111111|1111100 7ffc [15] - '=' ( 61) |100000 20 [ 6] - '>' ( 62) |11111111|1011 ffb [12] - '?' ( 63) |11111111|00 3fc [10] - '@' ( 64) |11111111|11010 1ffa [13] - 'A' ( 65) |100001 21 [ 6] - 'B' ( 66) |1011101 5d [ 7] - 'C' ( 67) |1011110 5e [ 7] - 'D' ( 68) |1011111 5f [ 7] - 'E' ( 69) |1100000 60 [ 7] - 'F' ( 70) |1100001 61 [ 7] - 'G' ( 71) |1100010 62 [ 7] - 'H' ( 72) |1100011 63 [ 7] - 'I' ( 73) |1100100 64 [ 7] - 'J' ( 74) |1100101 65 [ 7] - 'K' ( 75) |1100110 66 [ 7] - 'L' ( 76) |1100111 67 [ 7] - 'M' ( 77) |1101000 68 [ 7] - 'N' ( 78) |1101001 69 [ 7] - 'O' ( 79) |1101010 6a [ 7] - 'P' ( 80) |1101011 6b [ 7] - 'Q' ( 81) |1101100 6c [ 7] - 'R' ( 82) |1101101 6d [ 7] - 'S' ( 83) |1101110 6e [ 7] - 'T' ( 84) |1101111 6f [ 7] - 'U' ( 85) |1110000 70 [ 7] - 'V' ( 86) |1110001 71 [ 7] - 'W' ( 87) |1110010 72 [ 7] - 'X' ( 88) |11111100 fc [ 8] - 'Y' ( 89) |1110011 73 [ 7] - 'Z' ( 90) |11111101 fd [ 8] - '[' ( 91) |11111111|11011 1ffb [13] - '\' ( 92) |11111111|11111110|000 7fff0 [19] - ']' ( 93) |11111111|11100 1ffc [13] - '^' ( 94) |11111111|111100 3ffc [14] - '_' ( 95) |100010 22 [ 6] - '`' ( 96) |11111111|1111101 7ffd [15] - 'a' ( 97) |00011 3 [ 5] - 'b' ( 98) |100011 23 [ 6] - 'c' ( 99) |00100 4 [ 5] - 'd' (100) |100100 24 [ 6] - 'e' (101) |00101 5 [ 5] - 'f' (102) |100101 25 [ 6] - 'g' (103) |100110 26 [ 6] - 'h' (104) |100111 27 [ 6] - 'i' (105) |00110 6 [ 5] - 'j' (106) |1110100 74 [ 7] - 'k' (107) |1110101 75 [ 7] - 'l' (108) |101000 28 [ 6] - 'm' (109) |101001 29 [ 6] - 'n' (110) |101010 2a [ 6] - 'o' (111) |00111 7 [ 5] - 'p' (112) |101011 2b [ 6] - 'q' (113) |1110110 76 [ 7] - 'r' (114) |101100 2c [ 6] - 's' (115) |01000 8 [ 5] - 't' (116) |01001 9 [ 5] - 'u' (117) |101101 2d [ 6] - 'v' (118) |1110111 77 [ 7] - 'w' (119) |1111000 78 [ 7] - 'x' (120) |1111001 79 [ 7] - 'y' (121) |1111010 7a [ 7] - 'z' (122) |1111011 7b [ 7] - '{' (123) |11111111|1111110 7ffe [15] - '|' (124) |11111111|100 7fc [11] - '}' (125) |11111111|111101 3ffd [14] - '~' (126) |11111111|11101 1ffd [13] - (127) |11111111|11111111|11111111|1100 ffffffc [28] - (128) |11111111|11111110|0110 fffe6 [20] - (129) |11111111|11111111|010010 3fffd2 [22] - (130) |11111111|11111110|0111 fffe7 [20] - (131) |11111111|11111110|1000 fffe8 [20] - (132) |11111111|11111111|010011 3fffd3 [22] - (133) |11111111|11111111|010100 3fffd4 [22] - (134) |11111111|11111111|010101 3fffd5 [22] - (135) |11111111|11111111|1011001 7fffd9 [23] - (136) |11111111|11111111|010110 3fffd6 [22] - (137) |11111111|11111111|1011010 7fffda [23] - (138) |11111111|11111111|1011011 7fffdb [23] - (139) |11111111|11111111|1011100 7fffdc [23] - (140) |11111111|11111111|1011101 7fffdd [23] - (141) |11111111|11111111|1011110 7fffde [23] - (142) |11111111|11111111|11101011 ffffeb [24] - (143) |11111111|11111111|1011111 7fffdf [23] - (144) |11111111|11111111|11101100 ffffec [24] - (145) |11111111|11111111|11101101 ffffed [24] - (146) |11111111|11111111|010111 3fffd7 [22] - (147) |11111111|11111111|1100000 7fffe0 [23] - (148) |11111111|11111111|11101110 ffffee [24] - (149) |11111111|11111111|1100001 7fffe1 [23] - (150) |11111111|11111111|1100010 7fffe2 [23] - (151) |11111111|11111111|1100011 7fffe3 [23] - (152) |11111111|11111111|1100100 7fffe4 [23] - (153) |11111111|11111110|11100 1fffdc [21] - (154) |11111111|11111111|011000 3fffd8 [22] - (155) |11111111|11111111|1100101 7fffe5 [23] - (156) |11111111|11111111|011001 3fffd9 [22] - (157) |11111111|11111111|1100110 7fffe6 [23] - (158) |11111111|11111111|1100111 7fffe7 [23] - (159) |11111111|11111111|11101111 ffffef [24] - (160) |11111111|11111111|011010 3fffda [22] - (161) |11111111|11111110|11101 1fffdd [21] - (162) |11111111|11111110|1001 fffe9 [20] - (163) |11111111|11111111|011011 3fffdb [22] - (164) |11111111|11111111|011100 3fffdc [22] - (165) |11111111|11111111|1101000 7fffe8 [23] - (166) |11111111|11111111|1101001 7fffe9 [23] - (167) |11111111|11111110|11110 1fffde [21] - (168) |11111111|11111111|1101010 7fffea [23] - (169) |11111111|11111111|011101 3fffdd [22] - (170) |11111111|11111111|011110 3fffde [22] - (171) |11111111|11111111|11110000 fffff0 [24] - (172) |11111111|11111110|11111 1fffdf [21] - (173) |11111111|11111111|011111 3fffdf [22] - (174) |11111111|11111111|1101011 7fffeb [23] - (175) |11111111|11111111|1101100 7fffec [23] - (176) |11111111|11111111|00000 1fffe0 [21] - (177) |11111111|11111111|00001 1fffe1 [21] - (178) |11111111|11111111|100000 3fffe0 [22] - (179) |11111111|11111111|00010 1fffe2 [21] - (180) |11111111|11111111|1101101 7fffed [23] - (181) |11111111|11111111|100001 3fffe1 [22] - (182) |11111111|11111111|1101110 7fffee [23] - (183) |11111111|11111111|1101111 7fffef [23] - (184) |11111111|11111110|1010 fffea [20] - (185) |11111111|11111111|100010 3fffe2 [22] - (186) |11111111|11111111|100011 3fffe3 [22] - (187) |11111111|11111111|100100 3fffe4 [22] - (188) |11111111|11111111|1110000 7ffff0 [23] - (189) |11111111|11111111|100101 3fffe5 [22] - (190) |11111111|11111111|100110 3fffe6 [22] - (191) |11111111|11111111|1110001 7ffff1 [23] - (192) |11111111|11111111|11111000|00 3ffffe0 [26] - (193) |11111111|11111111|11111000|01 3ffffe1 [26] - (194) |11111111|11111110|1011 fffeb [20] - (195) |11111111|11111110|001 7fff1 [19] - (196) |11111111|11111111|100111 3fffe7 [22] - (197) |11111111|11111111|1110010 7ffff2 [23] - (198) |11111111|11111111|101000 3fffe8 [22] - (199) |11111111|11111111|11110110|0 1ffffec [25] - (200) |11111111|11111111|11111000|10 3ffffe2 [26] - (201) |11111111|11111111|11111000|11 3ffffe3 [26] - (202) |11111111|11111111|11111001|00 3ffffe4 [26] - (203) |11111111|11111111|11111011|110 7ffffde [27] - (204) |11111111|11111111|11111011|111 7ffffdf [27] - (205) |11111111|11111111|11111001|01 3ffffe5 [26] - (206) |11111111|11111111|11110001 fffff1 [24] - (207) |11111111|11111111|11110110|1 1ffffed [25] - (208) |11111111|11111110|010 7fff2 [19] - (209) |11111111|11111111|00011 1fffe3 [21] - (210) |11111111|11111111|11111001|10 3ffffe6 [26] - (211) |11111111|11111111|11111100|000 7ffffe0 [27] - (212) |11111111|11111111|11111100|001 7ffffe1 [27] - (213) |11111111|11111111|11111001|11 3ffffe7 [26] - (214) |11111111|11111111|11111100|010 7ffffe2 [27] - (215) |11111111|11111111|11110010 fffff2 [24] - (216) |11111111|11111111|00100 1fffe4 [21] - (217) |11111111|11111111|00101 1fffe5 [21] - (218) |11111111|11111111|11111010|00 3ffffe8 [26] - (219) |11111111|11111111|11111010|01 3ffffe9 [26] - (220) |11111111|11111111|11111111|1101 ffffffd [28] - (221) |11111111|11111111|11111100|011 7ffffe3 [27] - (222) |11111111|11111111|11111100|100 7ffffe4 [27] - (223) |11111111|11111111|11111100|101 7ffffe5 [27] - (224) |11111111|11111110|1100 fffec [20] - (225) |11111111|11111111|11110011 fffff3 [24] - (226) |11111111|11111110|1101 fffed [20] - (227) |11111111|11111111|00110 1fffe6 [21] - (228) |11111111|11111111|101001 3fffe9 [22] - (229) |11111111|11111111|00111 1fffe7 [21] - (230) |11111111|11111111|01000 1fffe8 [21] - (231) |11111111|11111111|1110011 7ffff3 [23] - (232) |11111111|11111111|101010 3fffea [22] - (233) |11111111|11111111|101011 3fffeb [22] - (234) |11111111|11111111|11110111|0 1ffffee [25] - (235) |11111111|11111111|11110111|1 1ffffef [25] - (236) |11111111|11111111|11110100 fffff4 [24] - (237) |11111111|11111111|11110101 fffff5 [24] - (238) |11111111|11111111|11111010|10 3ffffea [26] - (239) |11111111|11111111|1110100 7ffff4 [23] - (240) |11111111|11111111|11111010|11 3ffffeb [26] - (241) |11111111|11111111|11111100|110 7ffffe6 [27] - (242) |11111111|11111111|11111011|00 3ffffec [26] - (243) |11111111|11111111|11111011|01 3ffffed [26] - (244) |11111111|11111111|11111100|111 7ffffe7 [27] - (245) |11111111|11111111|11111101|000 7ffffe8 [27] - (246) |11111111|11111111|11111101|001 7ffffe9 [27] - (247) |11111111|11111111|11111101|010 7ffffea [27] - (248) |11111111|11111111|11111101|011 7ffffeb [27] - (249) |11111111|11111111|11111111|1110 ffffffe [28] - (250) |11111111|11111111|11111101|100 7ffffec [27] - (251) |11111111|11111111|11111101|101 7ffffed [27] - (252) |11111111|11111111|11111101|110 7ffffee [27] - (253) |11111111|11111111|11111101|111 7ffffef [27] - (254) |11111111|11111111|11111110|000 7fffff0 [27] - (255) |11111111|11111111|11111011|10 3ffffee [26] - EOS (256) |11111111|11111111|11111111|111111 3fffffff [30] diff --git a/bin/varnishtest/sequences b/bin/varnishtest/sequences deleted file mode 100644 index cf49a4961..000000000 --- a/bin/varnishtest/sequences +++ /dev/null @@ -1,121 +0,0 @@ -#- -# Copyright (c) 2008-2009 Ed Schouten -# All rights reserved. -# -# SPDX-License-Identifier: BSD-2-Clause -# -# Redistribution and use in source and binary forms, with or without -# modification, are permitted provided that the following conditions -# are met: -# 1. Redistributions of source code must retain the above copyright -# notice, this list of conditions and the following disclaimer. -# 2. Redistributions in binary form must reproduce the above copyright -# notice, this list of conditions and the following disclaimer in the -# documentation and/or other materials provided with the distribution. -# -# THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND -# ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -# ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE -# FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS -# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) -# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -# LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY -# OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF -# SUCH DAMAGE. -# -# $FreeBSD: head/sys/teken/sequences 334316 2018-05-29 08:41:44Z dumbbell $ - -# File format is as follows: -# Abbr Abbreviation of sequence name -# Name Sequence name (will be converted to C function name) -# Sequence Bytes that form the sequence -# Args Standard value of arguments passed to this sequence -# - `n' non-zero number (0 gets converted to 1) -# - `r' regular numeric argument -# - `v' means a variable number of arguments - -# Abbr Name Sequence Args -CBT Cursor Backward Tabulation ^[ [ Z n -CHT Cursor Forward Tabulation ^[ [ I n -CNL Cursor Next Line ^[ [ E n -CPL Cursor Previous Line ^[ [ F n -CPR Cursor Position Report ^[ [ n r -CUB Cursor Backward ^[ [ D n -CUD Cursor Down ^[ [ B n -CUD Cursor Down ^[ [ e n -CUF Cursor Forward ^[ [ C n -CUF Cursor Forward ^[ [ a n -CUP Cursor Position ^[ [ H n n -CUP Cursor Position ^[ [ f n n -CUU Cursor Up ^[ [ A n -DA1 Primary Device Attributes ^[ [ c r -DA2 Secondary Device Attributes ^[ [ > c r -DC Delete character ^[ [ P n -DCS Device Control String ^[ P -DECALN Alignment test ^[ # 8 -DECDHL Double Height Double Width Line Top ^[ # 3 -DECDHL Double Height Double Width Line Bottom ^[ # 4 -DECDWL Single Height Double Width Line ^[ # 6 -DECKPAM Keypad application mode ^[ = -DECKPNM Keypad numeric mode ^[ > -DECRC Restore cursor ^[ 8 -DECRC Restore cursor ^[ [ u -DECRM Reset DEC mode ^[ [ ? l r -DECSC Save cursor ^[ 7 -DECSC Save cursor ^[ [ s -DECSCUSR Set Cursor Style ^[ [ SP q r -DECSM Set DEC mode ^[ [ ? h r -DECSTBM Set top and bottom margins ^[ [ r r r -DECSWL Single Height Single Width Line ^[ # 5 -DL Delete line ^[ [ M n -DSR Device Status Report ^[ [ ? n r -ECH Erase character ^[ [ X n -ED Erase display ^[ [ J r -EL Erase line ^[ [ K r -G0SCS0 G0 SCS Special Graphics ^[ ( 0 -G0SCS1 G0 SCS US ASCII ^[ ( 1 -G0SCS2 G0 SCS Special Graphics ^[ ( 2 -G0SCSA G0 SCS UK National ^[ ( A -G0SCSB G0 SCS US ASCII ^[ ( B -G1SCS0 G1 SCS Special Graphics ^[ ) 0 -G1SCS1 G1 SCS US ASCII ^[ ) 1 -G1SCS2 G1 SCS Special Graphics ^[ ) 2 -G1SCSA G1 SCS UK National ^[ ) A -G1SCSB G1 SCS US ASCII ^[ ) B -HPA Horizontal Position Absolute ^[ [ G n -HPA Horizontal Position Absolute ^[ [ ` n -HTS Horizontal Tab Set ^[ H -ICH Insert character ^[ [ @ n -IL Insert line ^[ [ L n -IND Index ^[ D -NEL Next line ^[ E -OSC Operating System Command ^[ ] -RI Reverse index ^[ M -RIS Reset to Initial State ^[ c -RM Reset Mode ^[ [ l r -SD Pan Up ^[ [ T n -SGR Set Graphic Rendition ^[ [ m v -SM Set Mode ^[ [ h r -ST String Terminator ^[ \\ -SU Pan Down ^[ [ S n -TBC Tab Clear ^[ [ g r -VPA Vertical Position Absolute ^[ [ d n - -# Cons25 compatibility sequences -C25BLPD Cons25 set bell pitch duration ^[ [ = B r r -C25BORD Cons25 set border ^[ [ = A r -C25DBG Cons25 set default background ^[ [ = G r -C25DFG Cons25 set default foreground ^[ [ = F r -C25GCS Cons25 set global cursor shape ^[ [ = C v -C25LCT Cons25 set local cursor type ^[ [ = S r -C25MODE Cons25 set terminal mode ^[ [ = T r -C25SGR Cons25 set graphic rendition ^[ [ x r r -C25VTSW Cons25 switch virtual terminal ^[ [ z r - -# VT52 compatibility -#DECID VT52 DECID ^[ Z - -# ECMA-48 -REP Repeat last graphic char ^[ [ b n diff --git a/bin/varnishtest/teken.3 b/bin/varnishtest/teken.3 deleted file mode 100644 index 899186572..000000000 --- a/bin/varnishtest/teken.3 +++ /dev/null @@ -1,236 +0,0 @@ -.\" Copyright (c) 2011 Ed Schouten -.\" All rights reserved. -.\" -.\" SPDX-License-Identifier: BSD-2-Clause -.\" -.\" Redistribution and use in source and binary forms, with or without -.\" modification, are permitted provided that the following conditions -.\" are met: -.\" 1. Redistributions of source code must retain the above copyright -.\" notice, this list of conditions and the following disclaimer. -.\" 2. Redistributions in binary form must reproduce the above copyright -.\" notice, this list of conditions and the following disclaimer in the -.\" documentation and/or other materials provided with the distribution. -.\" -.\" THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND -.\" ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE -.\" IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE -.\" ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE -.\" FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -.\" DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS -.\" OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) -.\" HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -.\" LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY -.\" OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF -.\" SUCH DAMAGE. -.\" -.\" $FreeBSD: head/sys/teken/libteken/teken.3 315418 2017-03-16 16:40:54Z bde $ -.\" -.Dd Mar 13, 2017 -.Dt TEKEN 3 -.Os -.Sh NAME -.Nm teken -.Nd xterm-like terminal emulation interface -.Sh LIBRARY -.Lb libteken -.Sh SYNOPSIS -.In teken.h -.Ft void -.Fn teken_init "teken_t *t" "const teken_funcs_t *funcs" "void *thunk" -.Ft void -.Fn teken_input "teken_t *t" "const void *buf" "size_t nbytes" -.Ft const teken_pos_t * -.Fn teken_get_winsize "teken_t *t" -.Ft void -.Fn teken_set_winsize "teken_t *t" "const teken_pos_t *size" -.Ft const teken_pos_t * -.Fn teken_get_cursor "teken_t *t" -.Ft void -.Fn teken_set_cursor "teken_t *t" "const teken_pos_t *pos" -.Ft const teken_attr_t * -.Fn teken_get_curattr "teken_t *t" -.Ft void -.Fn teken_set_curattr "teken_t *t" "const teken_attr_t *attr" -.Ft const teken_attr_t * -.Fn teken_get_defattr "teken_t *t" -.Ft void -.Fn teken_set_defattr "teken_t *t" "const teken_attr_t *attr" -.Ft const char * -.Fn teken_get_sequence "teken_t *t" "unsigned int id" -.Ft teken_color_t -.Fn teken_256to16 "teken_color_t color" -.Ft teken_color_t -.Fn teken_256to8 "teken_color_t color" -.Ft void -.Fn teken_get_defattr_cons25 "teken_t *t" "int *fg" "int *bg" -.Ft void -.Fn teken_set_8bit "teken_t *t" -.Ft void -.Fn teken_set_cons25 "teken_t *t" -.Sh DESCRIPTION -The -.Nm -library implements the input parser of a 256-color xterm-like terminal. -It converts a stream of UTF-8 encoded characters into a series of -primitive drawing instructions that can be used by a console driver or -terminal emulator to render a terminal application. -.Pp -The -.Fn teken_init -function is used to initialize terminal state object -.Fa t , -having type -.Vt teken_t . -The supplied -.Vt teken_funcs_t -structure -.Fa funcs -contains a set of callback functions, which are called when supplying -data to -.Fn teken_input . -The -.Fa thunk -argument stores an arbitrary pointer, which is passed to each invocation -of the callback functions. -.Pp -The -.Vt teken_funcs_t -structure stores the following callbacks: -.Bd -literal -offset indent -typedef struct { - tf_bell_t *tf_bell; /* Audible/visible bell. */ - tf_cursor_t *tf_cursor; /* Move cursor to x/y. */ - tf_putchar_t *tf_putchar; /* Put Unicode character at x/y. */ - tf_fill_t *tf_fill; /* Fill rectangle with character. */ - tf_copy_t *tf_copy; /* Copy rectangle to new location. */ - tf_param_t *tf_param; /* Miscellaneous options. */ - tf_respond_t *tf_respond; /* Send response string to user. */ -} teken_funcs_t; -.Ed -.Pp -All callbacks must be provided, though unimplemented callbacks may some -times be sufficient. -The actual types of these callbacks can be found in -.In teken.h . -.Pp -By default, -.Fn teken_init -initializes the -.Vt teken_t -structure to emulate a terminal having 24 rows and 80 columns. -The -.Fn teken_get_winsize -and -.Fn teken_set_winsize -functions can be used to obtain and modify the dimensions of the -terminal. -.Pp -Even though the cursor position is normally controlled by input of data -through -.Fn teken_input -and returned by the -.Fn tf_cursor -callback, it can be obtained and modified manually using the -.Fn teken_get_cursor -and -.Fn teken_set_cursor -functions. -The same holds for -.Fn teken_get_curattr -and -.Fn teken_set_curattr , -which can be used to change the currently selected font attributes and -foreground and background color. -.Pp -By default, -.Nm -emulates a white-on-black terminal, which means the default foreground -color is white, while the background color is black. -These defaults can be modified using -.Fn teken_get_defattr -and -.Fn teken_set_defattr . -.Pp -The -.Fn teken_get_sequence -function is a utility function that can be used to obtain escape -sequences of special keyboard keys, generated by user input. -The -.Fa id -parameter must be one of the -.Dv TKEY_* -parameters listed in -.In teken.h . -.Sh LEGACY FEATURES -This library also provides a set of functions that shouldn't be used in -any modern applications. -.Pp -The -.Fn teken_256to16 -function converts an xterm-256 256-color code to an xterm 16-color code -whose color with default palettes is as similar as possible (not very -similar). -The lower 3 bits of the result are the ANSI color and the next lowest -bit is brightness. -Other layers (hardware and software) that only support 16 colors can use -this to avoid knowing the details of 256-color codes. -.Pp -The -.Fn teken_256to8 -function is similar to -.Fn teken_256to16 -except it converts to an ANSI 8-color code. -This is more accurate than discarding the brightness bit in the result of -.Fn teken_256to16 . -.Pp -The -.Fn teken_get_defattr_cons25 -function obtains the default terminal attributes as a pair of foreground -and background colors, using ANSI color numbering. -.Pp -The -.Fn teken_set_8bit -function disables UTF-8 processing and switches to 8-bit character mode, -which can be used to support character sets like CP437 and ISO-8859-1. -.Pp -The -.Fn teken_set_cons25 -function switches terminal emulation to -.Dv cons25 , -which is used by versions of -.Fx -prior to 9.0. -.Sh SEE ALSO -.Xr ncurses 3 , -.Xr termcap 3 , -.Xr syscons 4 -.Sh HISTORY -The -.Nm -library appeared in -.Fx 8.0 , -though it was only available and used inside the kernel. -In -.Fx 9.0 , -the -.Nm -library appeared in userspace. -.Sh AUTHORS -.An Ed Schouten Aq ed at FreeBSD.org -.Sh SECURITY CONSIDERATIONS -The -.Fn tf_respond -callback is used to respond to device status requests commands generated -by an application. -In the past, there have been various security issues, where a malicious -application sends a device status request before termination, causing -the generated response to be interpreted by applications such as -.Xr sh 1 . -.Pp -.Nm -only implements a small subset of responses which are unlikely to cause -any harm. -Still, it is advised to leave -.Fn tf_respond -unimplemented. diff --git a/bin/varnishtest/teken.c b/bin/varnishtest/teken.c deleted file mode 100644 index 48df7e066..000000000 --- a/bin/varnishtest/teken.c +++ /dev/null @@ -1,736 +0,0 @@ -/*- - * SPDX-License-Identifier: BSD-2-Clause-FreeBSD - * - * Copyright (c) 2008-2009 Ed Schouten - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * $FreeBSD: head/sys/teken/teken.c 333683 2018-05-16 18:12:49Z cem $ - */ - -#include "config.h" - -#include -#include -#include -#include -#include -#define teken_assert(x) assert(x) - -#include "vdef.h" -#include "vas.h" - -/* debug messages */ -#define teken_printf(...) - -/* Private flags for t_stateflags. */ -#define TS_FIRSTDIGIT 0x0001 /* First numeric digit in escape sequence. */ -#define TS_INSERT 0x0002 /* Insert mode. */ -#define TS_AUTOWRAP 0x0004 /* Autowrap. */ -#define TS_ORIGIN 0x0008 /* Origin mode. */ -#define TS_WRAPPED 0x0010 /* Next character should be printed on col 0. */ -#define TS_8BIT 0x0020 /* UTF-8 disabled. */ -#define TS_CONS25 0x0040 /* cons25 emulation. */ -#define TS_INSTRING 0x0080 /* Inside string. */ -#define TS_CURSORKEYS 0x0100 /* Cursor keys mode. */ - -/* Character that blanks a cell. */ -#define BLANK ' ' - -#include "teken.h" -#include "teken_wcwidth.h" -#include "teken_scs.h" - -static teken_state_t teken_state_init; - -/* - * Wrappers for hooks. - */ - -static inline void -teken_funcs_bell(const teken_t *t) -{ - - if (t->t_funcs->tf_bell != NULL) - t->t_funcs->tf_bell(t->t_softc); -} - -static inline void -teken_funcs_cursor(const teken_t *t) -{ - - teken_assert(t->t_cursor.tp_row < t->t_winsize.tp_row); - teken_assert(t->t_cursor.tp_col < t->t_winsize.tp_col); - - teken_assert(t->t_funcs->tf_cursor != NULL); - t->t_funcs->tf_cursor(t->t_softc, &t->t_cursor); -} - -static inline void -teken_funcs_putchar(const teken_t *t, const teken_pos_t *p, teken_char_t c, - const teken_attr_t *a) -{ - - teken_assert(p->tp_row < t->t_winsize.tp_row); - teken_assert(p->tp_col < t->t_winsize.tp_col); - - teken_assert(t->t_funcs->tf_putchar != NULL); - t->t_funcs->tf_putchar(t->t_softc, p, c, a); -} - -static inline void -teken_funcs_fill(const teken_t *t, const teken_rect_t *r, - const teken_char_t c, const teken_attr_t *a) -{ - - teken_assert(r->tr_end.tp_row > r->tr_begin.tp_row); - teken_assert(r->tr_end.tp_row <= t->t_winsize.tp_row); - teken_assert(r->tr_end.tp_col > r->tr_begin.tp_col); - teken_assert(r->tr_end.tp_col <= t->t_winsize.tp_col); - - teken_assert(t->t_funcs->tf_fill != NULL); - t->t_funcs->tf_fill(t->t_softc, r, c, a); -} - -static inline void -teken_funcs_copy(const teken_t *t, const teken_rect_t *r, const teken_pos_t *p) -{ - - teken_assert(r->tr_end.tp_row > r->tr_begin.tp_row); - teken_assert(r->tr_end.tp_row <= t->t_winsize.tp_row); - teken_assert(r->tr_end.tp_col > r->tr_begin.tp_col); - teken_assert(r->tr_end.tp_col <= t->t_winsize.tp_col); - teken_assert(p->tp_row + (r->tr_end.tp_row - r->tr_begin.tp_row) <= t->t_winsize.tp_row); - teken_assert(p->tp_col + (r->tr_end.tp_col - r->tr_begin.tp_col) <= t->t_winsize.tp_col); - - teken_assert(t->t_funcs->tf_copy != NULL); - t->t_funcs->tf_copy(t->t_softc, r, p); -} - -static inline void -teken_funcs_pre_input(const teken_t *t) -{ - - if (t->t_funcs->tf_pre_input != NULL) - t->t_funcs->tf_pre_input(t->t_softc); -} - -static inline void -teken_funcs_post_input(const teken_t *t) -{ - - if (t->t_funcs->tf_post_input != NULL) - t->t_funcs->tf_post_input(t->t_softc); -} - -static inline void -teken_funcs_param(const teken_t *t, int cmd, unsigned int value) -{ - - teken_assert(t->t_funcs->tf_param != NULL); - t->t_funcs->tf_param(t->t_softc, cmd, value); -} - -static inline void -teken_funcs_respond(const teken_t *t, const void *buf, size_t len) -{ - - teken_assert(t->t_funcs->tf_respond != NULL); - t->t_funcs->tf_respond(t->t_softc, buf, len); -} - -#include "teken_subr.h" -#include "teken_subr_compat.h" - -/* - * Programming interface. - */ - -void -teken_init(teken_t *t, const teken_funcs_t *tf, void *softc) -{ - teken_pos_t tp = { .tp_row = 24, .tp_col = 80 }; - - t->t_funcs = tf; - t->t_softc = softc; - - t->t_nextstate = teken_state_init; - t->t_stateflags = 0; - t->t_utf8_left = 0; - - t->t_defattr.ta_format = 0; - t->t_defattr.ta_fgcolor = TC_WHITE; - t->t_defattr.ta_bgcolor = TC_BLACK; - teken_subr_do_reset(t); - - teken_set_winsize(t, &tp); -} - -static void -teken_input_char(teken_t *t, teken_char_t c) -{ - - /* - * There is no support for DCS and OSC. Just discard strings - * until we receive characters that may indicate string - * termination. - */ - if (t->t_stateflags & TS_INSTRING) { - switch (c) { - case '\x1B': - t->t_stateflags &= ~TS_INSTRING; - break; - case '\a': - t->t_stateflags &= ~TS_INSTRING; - return; - default: - return; - } - } - - switch (c) { - case '\0': - break; - case '\a': - teken_subr_bell(t); - break; - case '\b': - teken_subr_backspace(t); - break; - case '\n': - case '\x0B': - teken_subr_newline(t); - break; - case '\x0C': - teken_subr_newpage(t); - break; - case '\x0E': - if (t->t_stateflags & TS_CONS25) - t->t_nextstate(t, c); - else - t->t_curscs = 1; - break; - case '\x0F': - if (t->t_stateflags & TS_CONS25) - t->t_nextstate(t, c); - else - t->t_curscs = 0; - break; - case '\r': - teken_subr_carriage_return(t); - break; - case '\t': - teken_subr_horizontal_tab(t); - break; - default: - t->t_nextstate(t, c); - break; - } - - /* Post-processing assertions. */ - teken_assert(t->t_cursor.tp_row >= t->t_originreg.ts_begin); - teken_assert(t->t_cursor.tp_row < t->t_originreg.ts_end); - teken_assert(t->t_cursor.tp_row < t->t_winsize.tp_row); - teken_assert(t->t_cursor.tp_col < t->t_winsize.tp_col); - teken_assert(t->t_saved_cursor.tp_row < t->t_winsize.tp_row); - teken_assert(t->t_saved_cursor.tp_col < t->t_winsize.tp_col); - teken_assert(t->t_scrollreg.ts_end <= t->t_winsize.tp_row); - teken_assert(t->t_scrollreg.ts_begin < t->t_scrollreg.ts_end); - /* Origin region has to be window size or the same as scrollreg. */ - teken_assert((t->t_originreg.ts_begin == t->t_scrollreg.ts_begin && - t->t_originreg.ts_end == t->t_scrollreg.ts_end) || - (t->t_originreg.ts_begin == 0 && - t->t_originreg.ts_end == t->t_winsize.tp_row)); -} - -static void -teken_input_byte(teken_t *t, unsigned char c) -{ - - /* - * UTF-8 handling. - */ - if ((c & 0x80) == 0x00 || t->t_stateflags & TS_8BIT) { - /* One-byte sequence. */ - t->t_utf8_left = 0; - teken_input_char(t, c); - } else if ((c & 0xe0) == 0xc0) { - /* Two-byte sequence. */ - t->t_utf8_left = 1; - t->t_utf8_partial = c & 0x1f; - } else if ((c & 0xf0) == 0xe0) { - /* Three-byte sequence. */ - t->t_utf8_left = 2; - t->t_utf8_partial = c & 0x0f; - } else if ((c & 0xf8) == 0xf0) { - /* Four-byte sequence. */ - t->t_utf8_left = 3; - t->t_utf8_partial = c & 0x07; - } else if ((c & 0xc0) == 0x80) { - if (t->t_utf8_left == 0) - return; - t->t_utf8_left--; - t->t_utf8_partial = (t->t_utf8_partial << 6) | (c & 0x3f); - if (t->t_utf8_left == 0) { - teken_printf("Got UTF-8 char %x\n", t->t_utf8_partial); - teken_input_char(t, t->t_utf8_partial); - } - } -} - -void -teken_input(teken_t *t, const void *buf, size_t len) -{ - const char *c = buf; - - teken_funcs_pre_input(t); - while (len-- > 0) - teken_input_byte(t, *c++); - teken_funcs_post_input(t); -} - -const teken_pos_t * -teken_get_cursor(const teken_t *t) -{ - - return (&t->t_cursor); -} - -void -teken_set_cursor(teken_t *t, const teken_pos_t *p) -{ - - /* XXX: bounds checking with originreg! */ - teken_assert(p->tp_row < t->t_winsize.tp_row); - teken_assert(p->tp_col < t->t_winsize.tp_col); - - t->t_cursor = *p; -} - -const teken_attr_t * -teken_get_curattr(const teken_t *t) -{ - - return (&t->t_curattr); -} - -void -teken_set_curattr(teken_t *t, const teken_attr_t *a) -{ - - t->t_curattr = *a; -} - -const teken_attr_t * -teken_get_defattr(const teken_t *t) -{ - - return (&t->t_defattr); -} - -void -teken_set_defattr(teken_t *t, const teken_attr_t *a) -{ - - t->t_curattr = t->t_saved_curattr = t->t_defattr = *a; -} - -const teken_pos_t * -teken_get_winsize(const teken_t *t) -{ - - return (&t->t_winsize); -} - -static void -teken_trim_cursor_pos(teken_t *t, const teken_pos_t *new) -{ - const teken_pos_t *cur; - - cur = &t->t_winsize; - - if (cur->tp_row < new->tp_row || cur->tp_col < new->tp_col) - return; - if (t->t_cursor.tp_row >= new->tp_row) - t->t_cursor.tp_row = new->tp_row - 1; - if (t->t_cursor.tp_col >= new->tp_col) - t->t_cursor.tp_col = new->tp_col - 1; -} - -void -teken_set_winsize(teken_t *t, const teken_pos_t *p) -{ - - teken_trim_cursor_pos(t, p); - t->t_winsize = *p; - teken_subr_do_reset(t); -} - -void -teken_set_winsize_noreset(teken_t *t, const teken_pos_t *p) -{ - - teken_trim_cursor_pos(t, p); - t->t_winsize = *p; - teken_subr_do_resize(t); -} - -void -teken_set_8bit(teken_t *t) -{ - - t->t_stateflags |= TS_8BIT; -} - -void -teken_set_cons25(teken_t *t) -{ - - t->t_stateflags |= TS_CONS25; -} - -/* - * State machine. - */ - -static void -teken_state_switch(teken_t *t, teken_state_t *s) -{ - - t->t_nextstate = s; - t->t_curnum = 0; - t->t_stateflags |= TS_FIRSTDIGIT; -} - -static int -teken_state_numbers(teken_t *t, teken_char_t c) -{ - - teken_assert(t->t_curnum < T_NUMSIZE); - - if (c >= '0' && c <= '9') { - if (t->t_stateflags & TS_FIRSTDIGIT) { - /* First digit. */ - t->t_stateflags &= ~TS_FIRSTDIGIT; - t->t_nums[t->t_curnum] = c - '0'; - } else if (t->t_nums[t->t_curnum] < UINT_MAX / 100) { - /* - * There is no need to continue parsing input - * once the value exceeds the size of the - * terminal. It would only allow for integer - * overflows when performing arithmetic on the - * cursor position. - * - * Ignore any further digits if the value is - * already UINT_MAX / 100. - */ - t->t_nums[t->t_curnum] = - t->t_nums[t->t_curnum] * 10 + c - '0'; - } - return (1); - } else if (c == ';') { - if (t->t_stateflags & TS_FIRSTDIGIT) - t->t_nums[t->t_curnum] = 0; - - /* Only allow a limited set of arguments. */ - if (++t->t_curnum == T_NUMSIZE) { - teken_state_switch(t, teken_state_init); - return (1); - } - - t->t_stateflags |= TS_FIRSTDIGIT; - return (1); - } else { - if (t->t_stateflags & TS_FIRSTDIGIT && t->t_curnum > 0) { - /* Finish off the last empty argument. */ - t->t_nums[t->t_curnum] = 0; - t->t_curnum++; - } else if ((t->t_stateflags & TS_FIRSTDIGIT) == 0) { - /* Also count the last argument. */ - t->t_curnum++; - } - } - - return (0); -} - -#define k TC_BLACK -#define b TC_BLUE -#define y TC_BROWN -#define c TC_CYAN -#define g TC_GREEN -#define m TC_MAGENTA -#define r TC_RED -#define w TC_WHITE -#define K (TC_BLACK | TC_LIGHT) -#define B (TC_BLUE | TC_LIGHT) -#define Y (TC_BROWN | TC_LIGHT) -#define C (TC_CYAN | TC_LIGHT) -#define G (TC_GREEN | TC_LIGHT) -#define M (TC_MAGENTA | TC_LIGHT) -#define R (TC_RED | TC_LIGHT) -#define W (TC_WHITE | TC_LIGHT) - -/** - * The xterm-256 color map has steps of 0x28 (in the range 0-0xff), except - * for the first step which is 0x5f. Scale to the range 0-6 by dividing - * by 0x28 and rounding down. The range of 0-5 cannot represent the - * larger first step. - * - * This table is generated by the follow rules: - * - if all components are equal, the result is black for (0, 0, 0) and - * (2, 2, 2), else white; otherwise: - * - subtract the smallest component from all components - * - if this gives only one nonzero component, then that is the color - * - else if one component is 2 or more larger than the other nonzero one, - * then that component gives the color - * - else there are 2 nonzero components. The color is that of a small - * equal mixture of these components (cyan, yellow or magenta). E.g., - * (0, 5, 6) (Turquoise2) is a much purer cyan than (0, 2, 3) - * (DeepSkyBlue4), but we map both to cyan since we can't represent - * delicate shades of either blue or cyan and blue would be worse. - * Here it is important that components of 1 never occur. Blue would - * be twice as large as green in (0, 1, 2). - */ -static const teken_color_t teken_256to8tab[] = { - /* xterm normal colors: */ - k, r, g, y, b, m, c, w, - - /* xterm bright colors: */ - k, r, g, y, b, m, c, w, - - /* Red0 submap. */ - k, b, b, b, b, b, - g, c, c, b, b, b, - g, c, c, c, b, b, - g, g, c, c, c, b, - g, g, g, c, c, c, - g, g, g, g, c, c, - - /* Red2 submap. */ - r, m, m, b, b, b, - y, k, b, b, b, b, - y, g, c, c, b, b, - g, g, c, c, c, b, - g, g, g, c, c, c, - g, g, g, g, c, c, - - /* Red3 submap. */ - r, m, m, m, b, b, - y, r, m, m, b, b, - y, y, w, b, b, b, - y, y, g, c, c, b, - g, g, g, c, c, c, - g, g, g, g, c, c, - - /* Red4 submap. */ - r, r, m, m, m, b, - r, r, m, m, m, b, - y, y, r, m, m, b, - y, y, y, w, b, b, - y, y, y, g, c, c, - g, g, g, g, c, c, - - /* Red5 submap. */ - r, r, r, m, m, m, - r, r, r, m, m, m, - r, r, r, m, m, m, - y, y, y, r, m, m, - y, y, y, y, w, b, - y, y, y, y, g, c, - - /* Red6 submap. */ - r, r, r, r, m, m, - r, r, r, r, m, m, - r, r, r, r, m, m, - r, r, r, r, m, m, - y, y, y, y, r, m, - y, y, y, y, y, w, - - /* Grey submap. */ - k, k, k, k, k, k, - k, k, k, k, k, k, - w, w, w, w, w, w, - w, w, w, w, w, w, -}; - -/* - * This table is generated from the previous one by setting TC_LIGHT for - * entries whose luminosity in the xterm256 color map is 60% or larger. - * Thus the previous table is currently not really needed. It will be - * used for different fine tuning of the tables. - */ -static const teken_color_t teken_256to16tab[] = { - /* xterm normal colors: */ - k, r, g, y, b, m, c, w, - - /* xterm bright colors: */ - K, R, G, Y, B, M, C, W, - - /* Red0 submap. */ - k, b, b, b, b, b, - g, c, c, b, b, b, - g, c, c, c, b, b, - g, g, c, c, c, b, - g, g, g, c, c, c, - g, g, g, g, c, c, - - /* Red2 submap. */ - r, m, m, b, b, b, - y, K, b, b, B, B, - y, g, c, c, B, B, - g, g, c, c, C, B, - g, G, G, C, C, C, - g, G, G, G, C, C, - - /* Red3 submap. */ - r, m, m, m, b, b, - y, r, m, m, B, B, - y, y, w, B, B, B, - y, y, G, C, C, B, - g, G, G, C, C, C, - g, G, G, G, C, C, - - /* Red4 submap. */ - r, r, m, m, m, b, - r, r, m, m, M, B, - y, y, R, M, M, B, - y, y, Y, W, B, B, - y, Y, Y, G, C, C, - g, G, G, G, C, C, - - /* Red5 submap. */ - r, r, r, m, m, m, - r, R, R, M, M, M, - r, R, R, M, M, M, - y, Y, Y, R, M, M, - y, Y, Y, Y, W, B, - y, Y, Y, Y, G, C, - - /* Red6 submap. */ - r, r, r, r, m, m, - r, R, R, R, M, M, - r, R, R, R, M, M, - r, R, R, R, M, M, - y, Y, Y, Y, R, M, - y, Y, Y, Y, Y, W, - - /* Grey submap. */ - k, k, k, k, k, k, - K, K, K, K, K, K, - w, w, w, w, w, w, - W, W, W, W, W, W, -}; - -#undef k -#undef b -#undef y -#undef c -#undef g -#undef m -#undef r -#undef w -#undef K -#undef B -#undef Y -#undef C -#undef G -#undef M -#undef R -#undef W - -teken_color_t -teken_256to8(teken_color_t c) -{ - - return (teken_256to8tab[c % 256]); -} - -teken_color_t -teken_256to16(teken_color_t c) -{ - - return (teken_256to16tab[c % 256]); -} - -static const char * const special_strings_cons25[] = { - [TKEY_UP] = "\x1B[A", [TKEY_DOWN] = "\x1B[B", - [TKEY_LEFT] = "\x1B[D", [TKEY_RIGHT] = "\x1B[C", - - [TKEY_HOME] = "\x1B[H", [TKEY_END] = "\x1B[F", - [TKEY_INSERT] = "\x1B[L", [TKEY_DELETE] = "\x7F", - [TKEY_PAGE_UP] = "\x1B[I", [TKEY_PAGE_DOWN] = "\x1B[G", - - [TKEY_F1] = "\x1B[M", [TKEY_F2] = "\x1B[N", - [TKEY_F3] = "\x1B[O", [TKEY_F4] = "\x1B[P", - [TKEY_F5] = "\x1B[Q", [TKEY_F6] = "\x1B[R", - [TKEY_F7] = "\x1B[S", [TKEY_F8] = "\x1B[T", - [TKEY_F9] = "\x1B[U", [TKEY_F10] = "\x1B[V", - [TKEY_F11] = "\x1B[W", [TKEY_F12] = "\x1B[X", -}; - -static const char * const special_strings_ckeys[] = { - [TKEY_UP] = "\x1BOA", [TKEY_DOWN] = "\x1BOB", - [TKEY_LEFT] = "\x1BOD", [TKEY_RIGHT] = "\x1BOC", - - [TKEY_HOME] = "\x1BOH", [TKEY_END] = "\x1BOF", -}; - -static const char * const special_strings_normal[] = { - [TKEY_UP] = "\x1B[A", [TKEY_DOWN] = "\x1B[B", - [TKEY_LEFT] = "\x1B[D", [TKEY_RIGHT] = "\x1B[C", - - [TKEY_HOME] = "\x1B[H", [TKEY_END] = "\x1B[F", - [TKEY_INSERT] = "\x1B[2~", [TKEY_DELETE] = "\x1B[3~", - [TKEY_PAGE_UP] = "\x1B[5~", [TKEY_PAGE_DOWN] = "\x1B[6~", - - [TKEY_F1] = "\x1BOP", [TKEY_F2] = "\x1BOQ", - [TKEY_F3] = "\x1BOR", [TKEY_F4] = "\x1BOS", - [TKEY_F5] = "\x1B[15~", [TKEY_F6] = "\x1B[17~", - [TKEY_F7] = "\x1B[18~", [TKEY_F8] = "\x1B[19~", - [TKEY_F9] = "\x1B[20~", [TKEY_F10] = "\x1B[21~", - [TKEY_F11] = "\x1B[23~", [TKEY_F12] = "\x1B[24~", -}; - -const char * -teken_get_sequence(const teken_t *t, unsigned int k) -{ - - /* Cons25 mode. */ - if (t->t_stateflags & TS_CONS25 && - k < sizeof special_strings_cons25 / sizeof(char *)) - return (special_strings_cons25[k]); - - /* Cursor keys mode. */ - if (t->t_stateflags & TS_CURSORKEYS && - k < sizeof special_strings_ckeys / sizeof(char *)) - return (special_strings_ckeys[k]); - - /* Default xterm sequences. */ - if (k < sizeof special_strings_normal / sizeof(char *)) - return (special_strings_normal[k]); - - return (NULL); -} - -#include "teken_state.h" diff --git a/bin/varnishtest/teken.h b/bin/varnishtest/teken.h deleted file mode 100644 index c08a2bbb8..000000000 --- a/bin/varnishtest/teken.h +++ /dev/null @@ -1,220 +0,0 @@ -/*- - * SPDX-License-Identifier: BSD-2-Clause-FreeBSD - * - * Copyright (c) 2008-2009 Ed Schouten - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * $FreeBSD: head/sys/teken/teken.h 333669 2018-05-16 09:01:02Z dumbbell $ - */ - -#ifndef _TEKEN_H_ -#define _TEKEN_H_ - -#include - -/* - * libteken: terminal emulation library. - * - * This library converts an UTF-8 stream of bytes to terminal drawing - * commands. - */ - -typedef uint32_t teken_char_t; -typedef unsigned short teken_unit_t; -typedef unsigned char teken_format_t; -#define TF_BOLD 0x01 /* Bold character. */ -#define TF_UNDERLINE 0x02 /* Underline character. */ -#define TF_BLINK 0x04 /* Blinking character. */ -#define TF_REVERSE 0x08 /* Reverse rendered character. */ -#define TF_CJK_RIGHT 0x10 /* Right-hand side of CJK character. */ -typedef unsigned char teken_color_t; -#define TC_BLACK 0 -#define TC_RED 1 -#define TC_GREEN 2 -#define TC_BROWN 3 -#define TC_BLUE 4 -#define TC_MAGENTA 5 -#define TC_CYAN 6 -#define TC_WHITE 7 -#define TC_NCOLORS 8 -#define TC_LIGHT 8 /* ORed with the others. */ - -typedef struct { - teken_unit_t tp_row; - teken_unit_t tp_col; -} teken_pos_t; -typedef struct { - teken_pos_t tr_begin; - teken_pos_t tr_end; -} teken_rect_t; -typedef struct { - teken_format_t ta_format; - teken_color_t ta_fgcolor; - teken_color_t ta_bgcolor; -} teken_attr_t; -typedef struct { - teken_unit_t ts_begin; - teken_unit_t ts_end; -} teken_span_t; - -typedef struct __teken teken_t; - -typedef void teken_state_t(teken_t *, teken_char_t); - -/* - * Drawing routines supplied by the user. - */ - -typedef void tf_bell_t(void *); -typedef void tf_cursor_t(void *, const teken_pos_t *); -typedef void tf_putchar_t(void *, const teken_pos_t *, teken_char_t, - const teken_attr_t *); -typedef void tf_fill_t(void *, const teken_rect_t *, teken_char_t, - const teken_attr_t *); -typedef void tf_copy_t(void *, const teken_rect_t *, const teken_pos_t *); -typedef void tf_pre_input_t(void *); -typedef void tf_post_input_t(void *); -typedef void tf_param_t(void *, int, unsigned int); -#define TP_SHOWCURSOR 0 -#define TP_KEYPADAPP 1 -#define TP_AUTOREPEAT 2 -#define TP_SWITCHVT 3 -#define TP_132COLS 4 -#define TP_SETBELLPD 5 -#define TP_SETBELLPD_PITCH(pd) ((pd) >> 16) -#define TP_SETBELLPD_DURATION(pd) ((pd) & 0xffff) -#define TP_MOUSE 6 -#define TP_SETBORDER 7 -#define TP_SETLOCALCURSOR 8 -#define TP_SETGLOBALCURSOR 9 -typedef void tf_respond_t(void *, const void *, size_t); - -typedef struct { - tf_bell_t *tf_bell; - tf_cursor_t *tf_cursor; - tf_putchar_t *tf_putchar; - tf_fill_t *tf_fill; - tf_copy_t *tf_copy; - tf_pre_input_t *tf_pre_input; - tf_post_input_t *tf_post_input; - tf_param_t *tf_param; - tf_respond_t *tf_respond; -} teken_funcs_t; - -typedef teken_char_t teken_scs_t(const teken_t *, teken_char_t); - -/* - * Terminal state. - */ - -struct __teken { - const teken_funcs_t *t_funcs; - void *t_softc; - - teken_state_t *t_nextstate; - unsigned int t_stateflags; - -#define T_NUMSIZE 8 - unsigned int t_nums[T_NUMSIZE]; - unsigned int t_curnum; - - teken_pos_t t_cursor; - teken_attr_t t_curattr; - teken_pos_t t_saved_cursor; - teken_attr_t t_saved_curattr; - - teken_attr_t t_defattr; - teken_pos_t t_winsize; - - /* For DECSTBM. */ - teken_span_t t_scrollreg; - /* For DECOM. */ - teken_span_t t_originreg; - -#define T_NUMCOL 160 - unsigned int t_tabstops[T_NUMCOL / (sizeof(unsigned int) * 8)]; - - unsigned int t_utf8_left; - teken_char_t t_utf8_partial; - teken_char_t t_last; - - unsigned int t_curscs; - teken_scs_t *t_saved_curscs; - teken_scs_t *t_scs[2]; -}; - -/* Initialize teken structure. */ -void teken_init(teken_t *, const teken_funcs_t *, void *); - -/* Deliver character input. */ -void teken_input(teken_t *, const void *, size_t); - -/* Get/set teken attributes. */ -const teken_pos_t *teken_get_cursor(const teken_t *); -const teken_attr_t *teken_get_curattr(const teken_t *); -const teken_attr_t *teken_get_defattr(const teken_t *); -void teken_get_defattr_cons25(const teken_t *, int *, int *); -const teken_pos_t *teken_get_winsize(const teken_t *); -void teken_set_cursor(teken_t *, const teken_pos_t *); -void teken_set_curattr(teken_t *, const teken_attr_t *); -void teken_set_defattr(teken_t *, const teken_attr_t *); -void teken_set_winsize(teken_t *, const teken_pos_t *); -void teken_set_winsize_noreset(teken_t *, const teken_pos_t *); - -/* Key input escape sequences. */ -#define TKEY_UP 0x00 -#define TKEY_DOWN 0x01 -#define TKEY_LEFT 0x02 -#define TKEY_RIGHT 0x03 - -#define TKEY_HOME 0x04 -#define TKEY_END 0x05 -#define TKEY_INSERT 0x06 -#define TKEY_DELETE 0x07 -#define TKEY_PAGE_UP 0x08 -#define TKEY_PAGE_DOWN 0x09 - -#define TKEY_F1 0x0a -#define TKEY_F2 0x0b -#define TKEY_F3 0x0c -#define TKEY_F4 0x0d -#define TKEY_F5 0x0e -#define TKEY_F6 0x0f -#define TKEY_F7 0x10 -#define TKEY_F8 0x11 -#define TKEY_F9 0x12 -#define TKEY_F10 0x13 -#define TKEY_F11 0x14 -#define TKEY_F12 0x15 -const char *teken_get_sequence(const teken_t *, unsigned int); - -/* Legacy features. */ -void teken_set_8bit(teken_t *); -void teken_set_cons25(teken_t *); - -/* Color conversion. */ -teken_color_t teken_256to16(teken_color_t); -teken_color_t teken_256to8(teken_color_t); - -#endif /* !_TEKEN_H_ */ diff --git a/bin/varnishtest/teken_scs.h b/bin/varnishtest/teken_scs.h deleted file mode 100644 index 719f2a98e..000000000 --- a/bin/varnishtest/teken_scs.h +++ /dev/null @@ -1,83 +0,0 @@ -/*- - * SPDX-License-Identifier: BSD-2-Clause-FreeBSD - * - * Copyright (c) 2009 Ed Schouten - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * $FreeBSD: head/sys/teken/teken_scs.h 332297 2018-04-08 19:23:50Z phk $ - */ - -static inline teken_char_t -teken_scs_process(const teken_t *t, teken_char_t c) -{ - - return (t->t_scs[t->t_curscs](t, c)); -} - -/* Unicode points for VT100 box drawing. */ -static const uint16_t teken_boxdrawing_unicode[31] = { - 0x25c6, 0x2592, 0x2409, 0x240c, 0x240d, 0x240a, 0x00b0, 0x00b1, - 0x2424, 0x240b, 0x2518, 0x2510, 0x250c, 0x2514, 0x253c, 0x23ba, - 0x23bb, 0x2500, 0x23bc, 0x23bd, 0x251c, 0x2524, 0x2534, 0x252c, - 0x2502, 0x2264, 0x2265, 0x03c0, 0x2260, 0x00a3, 0x00b7 -}; - -/* ASCII points for VT100 box drawing. */ -static const uint8_t teken_boxdrawing_8bit[31] = { - '?', '?', 'H', 'F', 'C', 'L', '?', '?', - 'N', 'V', '+', '+', '+', '+', '+', '-', - '-', '-', '-', '-', '+', '+', '+', '+', - '|', '?', '?', '?', '?', '?', '?', -}; - -static teken_char_t -teken_scs_special_graphics(const teken_t *t, teken_char_t c) -{ - - /* Box drawing. */ - if (c >= '`' && c <= '~') - return (t->t_stateflags & TS_8BIT ? - teken_boxdrawing_8bit[c - '`'] : - teken_boxdrawing_unicode[c - '`']); - return (c); -} - -static teken_char_t -teken_scs_uk_national(const teken_t *t, teken_char_t c) -{ - - /* Pound sign. */ - if (c == '#') - return (t->t_stateflags & TS_8BIT ? 0x9c : 0xa3); - return (c); -} - -static teken_char_t -teken_scs_us_ascii(const teken_t *t, teken_char_t c) -{ - - /* No processing. */ - (void)t; - return (c); -} diff --git a/bin/varnishtest/teken_subr.h b/bin/varnishtest/teken_subr.h deleted file mode 100644 index 644e502f6..000000000 --- a/bin/varnishtest/teken_subr.h +++ /dev/null @@ -1,1346 +0,0 @@ -/*- - * SPDX-License-Identifier: BSD-2-Clause-FreeBSD - * - * Copyright (c) 2008-2009 Ed Schouten - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * $FreeBSD: head/sys/teken/teken_subr.h 333995 2018-05-21 20:35:16Z dumbbell $ - */ - -static void teken_subr_cursor_up(teken_t *, unsigned int); -static void teken_subr_erase_line(const teken_t *, unsigned int); -static void teken_subr_regular_character(teken_t *, teken_char_t); -static void teken_subr_reset_to_initial_state(teken_t *); -static void teken_subr_save_cursor(teken_t *); - -static inline int -teken_tab_isset(const teken_t *t, unsigned int col) -{ - unsigned int b, o; - - if (col >= T_NUMCOL) - return ((col % 8) == 0); - - b = col / (sizeof(unsigned int) * 8); - o = col % (sizeof(unsigned int) * 8); - - return (t->t_tabstops[b] & (1U << o)); -} - -static inline void -teken_tab_clear(teken_t *t, unsigned int col) -{ - unsigned int b, o; - - if (col >= T_NUMCOL) - return; - - b = col / (sizeof(unsigned int) * 8); - o = col % (sizeof(unsigned int) * 8); - - t->t_tabstops[b] &= ~(1U << o); -} - -static inline void -teken_tab_set(teken_t *t, unsigned int col) -{ - unsigned int b, o; - - if (col >= T_NUMCOL) - return; - - b = col / (sizeof(unsigned int) * 8); - o = col % (sizeof(unsigned int) * 8); - - t->t_tabstops[b] |= 1U << o; -} - -static void -teken_tab_default(teken_t *t) -{ - unsigned int i; - - memset(t->t_tabstops, 0, T_NUMCOL / 8); - - for (i = 8; i < T_NUMCOL; i += 8) - teken_tab_set(t, i); -} - -static void -teken_subr_do_scroll(const teken_t *t, int amount) -{ - teken_rect_t tr; - teken_pos_t tp; - - teken_assert(t->t_cursor.tp_row <= t->t_winsize.tp_row); - teken_assert(t->t_scrollreg.ts_end <= t->t_winsize.tp_row); - teken_assert(amount != 0); - - /* Copy existing data 1 line up. */ - if (amount > 0) { - /* Scroll down. */ - - /* Copy existing data up. */ - if (t->t_scrollreg.ts_begin + amount < t->t_scrollreg.ts_end) { - tr.tr_begin.tp_row = t->t_scrollreg.ts_begin + amount; - tr.tr_begin.tp_col = 0; - tr.tr_end.tp_row = t->t_scrollreg.ts_end; - tr.tr_end.tp_col = t->t_winsize.tp_col; - tp.tp_row = t->t_scrollreg.ts_begin; - tp.tp_col = 0; - teken_funcs_copy(t, &tr, &tp); - - tr.tr_begin.tp_row = t->t_scrollreg.ts_end - amount; - } else { - tr.tr_begin.tp_row = t->t_scrollreg.ts_begin; - } - - /* Clear the last lines. */ - tr.tr_begin.tp_col = 0; - tr.tr_end.tp_row = t->t_scrollreg.ts_end; - tr.tr_end.tp_col = t->t_winsize.tp_col; - teken_funcs_fill(t, &tr, BLANK, &t->t_curattr); - } else { - /* Scroll up. */ - amount = -amount; - - /* Copy existing data down. */ - if (t->t_scrollreg.ts_begin + amount < t->t_scrollreg.ts_end) { - tr.tr_begin.tp_row = t->t_scrollreg.ts_begin; - tr.tr_begin.tp_col = 0; - tr.tr_end.tp_row = t->t_scrollreg.ts_end - amount; - tr.tr_end.tp_col = t->t_winsize.tp_col; - tp.tp_row = t->t_scrollreg.ts_begin + amount; - tp.tp_col = 0; - teken_funcs_copy(t, &tr, &tp); - - tr.tr_end.tp_row = t->t_scrollreg.ts_begin + amount; - } else { - tr.tr_end.tp_row = t->t_scrollreg.ts_end; - } - - /* Clear the first lines. */ - tr.tr_begin.tp_row = t->t_scrollreg.ts_begin; - tr.tr_begin.tp_col = 0; - tr.tr_end.tp_col = t->t_winsize.tp_col; - teken_funcs_fill(t, &tr, BLANK, &t->t_curattr); - } -} - -static ssize_t -teken_subr_do_cpr(const teken_t *t, unsigned int cmd, char response[16]) -{ - - switch (cmd) { - case 5: /* Operating status. */ - strcpy(response, "0n"); - return (2); - case 6: { /* Cursor position. */ - int len; - - len = snprintf(response, 16, "%u;%uR", - (t->t_cursor.tp_row - t->t_originreg.ts_begin) + 1, - t->t_cursor.tp_col + 1); - - if (len >= 16) - return (-1); - return (len); - } - case 15: /* Printer status. */ - strcpy(response, "13n"); - return (3); - case 25: /* UDK status. */ - strcpy(response, "20n"); - return (3); - case 26: /* Keyboard status. */ - strcpy(response, "27;1n"); - return (5); - default: - teken_printf("Unknown DSR\n"); - return (-1); - } -} - -static void -teken_subr_alignment_test(teken_t *t) -{ - teken_rect_t tr; - - t->t_cursor.tp_row = t->t_cursor.tp_col = 0; - t->t_scrollreg.ts_begin = 0; - t->t_scrollreg.ts_end = t->t_winsize.tp_row; - t->t_originreg = t->t_scrollreg; - t->t_stateflags &= ~(TS_WRAPPED|TS_ORIGIN); - teken_funcs_cursor(t); - - tr.tr_begin.tp_row = 0; - tr.tr_begin.tp_col = 0; - tr.tr_end = t->t_winsize; - teken_funcs_fill(t, &tr, 'E', &t->t_defattr); -} - -static void -teken_subr_backspace(teken_t *t) -{ - - if (t->t_stateflags & TS_CONS25) { - if (t->t_cursor.tp_col == 0) { - if (t->t_cursor.tp_row == t->t_originreg.ts_begin) - return; - t->t_cursor.tp_row--; - t->t_cursor.tp_col = t->t_winsize.tp_col - 1; - } else { - t->t_cursor.tp_col--; - } - } else { - if (t->t_cursor.tp_col == 0) - return; - - t->t_cursor.tp_col--; - t->t_stateflags &= ~TS_WRAPPED; - } - - teken_funcs_cursor(t); -} - -static void -teken_subr_bell(const teken_t *t) -{ - - teken_funcs_bell(t); -} - -static void -teken_subr_carriage_return(teken_t *t) -{ - - t->t_cursor.tp_col = 0; - t->t_stateflags &= ~TS_WRAPPED; - teken_funcs_cursor(t); -} - -static void -teken_subr_cursor_backward(teken_t *t, unsigned int ncols) -{ - - if (ncols > t->t_cursor.tp_col) - t->t_cursor.tp_col = 0; - else - t->t_cursor.tp_col -= ncols; - t->t_stateflags &= ~TS_WRAPPED; - teken_funcs_cursor(t); -} - -static void -teken_subr_cursor_backward_tabulation(teken_t *t, unsigned int ntabs) -{ - - do { - /* Stop when we've reached the beginning of the line. */ - if (t->t_cursor.tp_col == 0) - break; - - t->t_cursor.tp_col--; - - /* Tab marker set. */ - if (teken_tab_isset(t, t->t_cursor.tp_col)) - ntabs--; - } while (ntabs > 0); - - teken_funcs_cursor(t); -} - -static void -teken_subr_cursor_down(teken_t *t, unsigned int nrows) -{ - - if (t->t_cursor.tp_row + nrows >= t->t_scrollreg.ts_end) - t->t_cursor.tp_row = t->t_scrollreg.ts_end - 1; - else - t->t_cursor.tp_row += nrows; - t->t_stateflags &= ~TS_WRAPPED; - teken_funcs_cursor(t); -} - -static void -teken_subr_cursor_forward(teken_t *t, unsigned int ncols) -{ - - if (t->t_cursor.tp_col + ncols >= t->t_winsize.tp_col) - t->t_cursor.tp_col = t->t_winsize.tp_col - 1; - else - t->t_cursor.tp_col += ncols; - t->t_stateflags &= ~TS_WRAPPED; - teken_funcs_cursor(t); -} - -static void -teken_subr_cursor_forward_tabulation(teken_t *t, unsigned int ntabs) -{ - - do { - /* Stop when we've reached the end of the line. */ - if (t->t_cursor.tp_col == t->t_winsize.tp_col - 1) - break; - - t->t_cursor.tp_col++; - - /* Tab marker set. */ - if (teken_tab_isset(t, t->t_cursor.tp_col)) - ntabs--; - } while (ntabs > 0); - - teken_funcs_cursor(t); -} - -static void -teken_subr_cursor_next_line(teken_t *t, unsigned int ncols) -{ - - t->t_cursor.tp_col = 0; - teken_subr_cursor_down(t, ncols); -} - -static void -teken_subr_cursor_position(teken_t *t, unsigned int row, unsigned int col) -{ - - row = (row - 1) + t->t_originreg.ts_begin; - t->t_cursor.tp_row = row < t->t_originreg.ts_end ? - row : t->t_originreg.ts_end - 1; - - col--; - t->t_cursor.tp_col = col < t->t_winsize.tp_col ? - col : t->t_winsize.tp_col - 1; - - t->t_stateflags &= ~TS_WRAPPED; - teken_funcs_cursor(t); -} - -static void -teken_subr_cursor_position_report(const teken_t *t, unsigned int cmd) -{ - char response[18] = "\x1B["; - ssize_t len; - - len = teken_subr_do_cpr(t, cmd, response + 2); - if (len < 0) - return; - - teken_funcs_respond(t, response, len + 2); -} - -static void -teken_subr_cursor_previous_line(teken_t *t, unsigned int ncols) -{ - - t->t_cursor.tp_col = 0; - teken_subr_cursor_up(t, ncols); -} - -static void -teken_subr_cursor_up(teken_t *t, unsigned int nrows) -{ - - if (t->t_scrollreg.ts_begin + nrows >= t->t_cursor.tp_row) - t->t_cursor.tp_row = t->t_scrollreg.ts_begin; - else - t->t_cursor.tp_row -= nrows; - t->t_stateflags &= ~TS_WRAPPED; - teken_funcs_cursor(t); -} - -static void -teken_subr_set_cursor_style(teken_t *t, unsigned int style) -{ - - /* TODO */ - (void)t; - (void)style; - - /* - * CSI Ps SP q - * Set cursor style (DECSCUSR), VT520. - * Ps = 0 -> blinking block. - * Ps = 1 -> blinking block (default). - * Ps = 2 -> steady block. - * Ps = 3 -> blinking underline. - * Ps = 4 -> steady underline. - * Ps = 5 -> blinking bar (xterm). - * Ps = 6 -> steady bar (xterm). - */ -} - -static void -teken_subr_delete_character(const teken_t *t, unsigned int ncols) -{ - teken_rect_t tr; - - tr.tr_begin.tp_row = t->t_cursor.tp_row; - tr.tr_end.tp_row = t->t_cursor.tp_row + 1; - tr.tr_end.tp_col = t->t_winsize.tp_col; - - if (t->t_cursor.tp_col + ncols >= t->t_winsize.tp_col) { - tr.tr_begin.tp_col = t->t_cursor.tp_col; - } else { - /* Copy characters to the left. */ - tr.tr_begin.tp_col = t->t_cursor.tp_col + ncols; - teken_funcs_copy(t, &tr, &t->t_cursor); - - tr.tr_begin.tp_col = t->t_winsize.tp_col - ncols; - } - - /* Blank trailing columns. */ - teken_funcs_fill(t, &tr, BLANK, &t->t_curattr); -} - -static void -teken_subr_delete_line(const teken_t *t, unsigned int nrows) -{ - teken_rect_t tr; - - /* Ignore if outside scrolling region. */ - if (t->t_cursor.tp_row < t->t_scrollreg.ts_begin || - t->t_cursor.tp_row >= t->t_scrollreg.ts_end) - return; - - tr.tr_begin.tp_col = 0; - tr.tr_end.tp_row = t->t_scrollreg.ts_end; - tr.tr_end.tp_col = t->t_winsize.tp_col; - - if (t->t_cursor.tp_row + nrows >= t->t_scrollreg.ts_end) { - tr.tr_begin.tp_row = t->t_cursor.tp_row; - } else { - teken_pos_t tp; - - /* Copy rows up. */ - tr.tr_begin.tp_row = t->t_cursor.tp_row + nrows; - tp.tp_row = t->t_cursor.tp_row; - tp.tp_col = 0; - teken_funcs_copy(t, &tr, &tp); - - tr.tr_begin.tp_row = t->t_scrollreg.ts_end - nrows; - } - - /* Blank trailing rows. */ - teken_funcs_fill(t, &tr, BLANK, &t->t_curattr); -} - -static void -teken_subr_device_control_string(teken_t *t) -{ - - teken_printf("Unsupported device control string\n"); - t->t_stateflags |= TS_INSTRING; -} - -static void -teken_subr_device_status_report(const teken_t *t, unsigned int cmd) -{ - char response[19] = "\x1B[?"; - ssize_t len; - - len = teken_subr_do_cpr(t, cmd, response + 3); - if (len < 0) - return; - - teken_funcs_respond(t, response, len + 3); -} - -static void -teken_subr_double_height_double_width_line_top(const teken_t *t) -{ - - (void)t; - teken_printf("double height double width top\n"); -} - -static void -teken_subr_double_height_double_width_line_bottom(const teken_t *t) -{ - - (void)t; - teken_printf("double height double width bottom\n"); -} - -static void -teken_subr_erase_character(const teken_t *t, unsigned int ncols) -{ - teken_rect_t tr; - - tr.tr_begin = t->t_cursor; - tr.tr_end.tp_row = t->t_cursor.tp_row + 1; - - if (t->t_cursor.tp_col + ncols >= t->t_winsize.tp_col) - tr.tr_end.tp_col = t->t_winsize.tp_col; - else - tr.tr_end.tp_col = t->t_cursor.tp_col + ncols; - - teken_funcs_fill(t, &tr, BLANK, &t->t_curattr); -} - -static void -teken_subr_erase_display(const teken_t *t, unsigned int mode) -{ - teken_rect_t r; - - r.tr_begin.tp_col = 0; - r.tr_end.tp_col = t->t_winsize.tp_col; - - switch (mode) { - case 1: /* Erase from the top to the cursor. */ - teken_subr_erase_line(t, 1); - - /* Erase lines above. */ - if (t->t_cursor.tp_row == 0) - return; - r.tr_begin.tp_row = 0; - r.tr_end.tp_row = t->t_cursor.tp_row; - break; - case 2: /* Erase entire display. */ - r.tr_begin.tp_row = 0; - r.tr_end.tp_row = t->t_winsize.tp_row; - break; - default: /* Erase from cursor to the bottom. */ - teken_subr_erase_line(t, 0); - - /* Erase lines below. */ - if (t->t_cursor.tp_row == t->t_winsize.tp_row - 1) - return; - r.tr_begin.tp_row = t->t_cursor.tp_row + 1; - r.tr_end.tp_row = t->t_winsize.tp_row; - break; - } - - teken_funcs_fill(t, &r, BLANK, &t->t_curattr); -} - -static void -teken_subr_erase_line(const teken_t *t, unsigned int mode) -{ - teken_rect_t r; - - r.tr_begin.tp_row = t->t_cursor.tp_row; - r.tr_end.tp_row = t->t_cursor.tp_row + 1; - - switch (mode) { - case 1: /* Erase from the beginning of the line to the cursor. */ - r.tr_begin.tp_col = 0; - r.tr_end.tp_col = t->t_cursor.tp_col + 1; - break; - case 2: /* Erase entire line. */ - r.tr_begin.tp_col = 0; - r.tr_end.tp_col = t->t_winsize.tp_col; - break; - default: /* Erase from cursor to the end of the line. */ - r.tr_begin.tp_col = t->t_cursor.tp_col; - r.tr_end.tp_col = t->t_winsize.tp_col; - break; - } - - teken_funcs_fill(t, &r, BLANK, &t->t_curattr); -} - -static void -teken_subr_g0_scs_special_graphics(teken_t *t) -{ - - t->t_scs[0] = teken_scs_special_graphics; -} - -static void -teken_subr_g0_scs_uk_national(teken_t *t) -{ - - t->t_scs[0] = teken_scs_uk_national; -} - -static void -teken_subr_g0_scs_us_ascii(teken_t *t) -{ - - t->t_scs[0] = teken_scs_us_ascii; -} - -static void -teken_subr_g1_scs_special_graphics(teken_t *t) -{ - - t->t_scs[1] = teken_scs_special_graphics; -} - -static void -teken_subr_g1_scs_uk_national(teken_t *t) -{ - - t->t_scs[1] = teken_scs_uk_national; -} - -static void -teken_subr_g1_scs_us_ascii(teken_t *t) -{ - - t->t_scs[1] = teken_scs_us_ascii; -} - -static void -teken_subr_horizontal_position_absolute(teken_t *t, unsigned int col) -{ - - col--; - t->t_cursor.tp_col = col < t->t_winsize.tp_col ? - col : t->t_winsize.tp_col - 1; - - t->t_stateflags &= ~TS_WRAPPED; - teken_funcs_cursor(t); -} - -static void -teken_subr_horizontal_tab(teken_t *t) -{ - - teken_subr_cursor_forward_tabulation(t, 1); -} - -static void -teken_subr_horizontal_tab_set(teken_t *t) -{ - - teken_tab_set(t, t->t_cursor.tp_col); -} - -static void -teken_subr_index(teken_t *t) -{ - - if (t->t_cursor.tp_row < t->t_scrollreg.ts_end - 1) { - t->t_cursor.tp_row++; - t->t_stateflags &= ~TS_WRAPPED; - teken_funcs_cursor(t); - } else { - teken_subr_do_scroll(t, 1); - } -} - -static void -teken_subr_insert_character(const teken_t *t, unsigned int ncols) -{ - teken_rect_t tr; - - tr.tr_begin = t->t_cursor; - tr.tr_end.tp_row = t->t_cursor.tp_row + 1; - - if (t->t_cursor.tp_col + ncols >= t->t_winsize.tp_col) { - tr.tr_end.tp_col = t->t_winsize.tp_col; - } else { - teken_pos_t tp; - - /* Copy characters to the right. */ - tr.tr_end.tp_col = t->t_winsize.tp_col - ncols; - tp.tp_row = t->t_cursor.tp_row; - tp.tp_col = t->t_cursor.tp_col + ncols; - teken_funcs_copy(t, &tr, &tp); - - tr.tr_end.tp_col = t->t_cursor.tp_col + ncols; - } - - /* Blank current location. */ - teken_funcs_fill(t, &tr, BLANK, &t->t_curattr); -} - -static void -teken_subr_insert_line(const teken_t *t, unsigned int nrows) -{ - teken_rect_t tr; - - /* Ignore if outside scrolling region. */ - if (t->t_cursor.tp_row < t->t_scrollreg.ts_begin || - t->t_cursor.tp_row >= t->t_scrollreg.ts_end) - return; - - tr.tr_begin.tp_row = t->t_cursor.tp_row; - tr.tr_begin.tp_col = 0; - tr.tr_end.tp_col = t->t_winsize.tp_col; - - if (t->t_cursor.tp_row + nrows >= t->t_scrollreg.ts_end) { - tr.tr_end.tp_row = t->t_scrollreg.ts_end; - } else { - teken_pos_t tp; - - /* Copy lines down. */ - tr.tr_end.tp_row = t->t_scrollreg.ts_end - nrows; - tp.tp_row = t->t_cursor.tp_row + nrows; - tp.tp_col = 0; - teken_funcs_copy(t, &tr, &tp); - - tr.tr_end.tp_row = t->t_cursor.tp_row + nrows; - } - - /* Blank current location. */ - teken_funcs_fill(t, &tr, BLANK, &t->t_curattr); -} - -static void -teken_subr_keypad_application_mode(const teken_t *t) -{ - - teken_funcs_param(t, TP_KEYPADAPP, 1); -} - -static void -teken_subr_keypad_numeric_mode(const teken_t *t) -{ - - teken_funcs_param(t, TP_KEYPADAPP, 0); -} - -static void -teken_subr_newline(teken_t *t) -{ - - t->t_cursor.tp_row++; - - if (t->t_cursor.tp_row >= t->t_scrollreg.ts_end) { - teken_subr_do_scroll(t, 1); - t->t_cursor.tp_row = t->t_scrollreg.ts_end - 1; - } - - t->t_stateflags &= ~TS_WRAPPED; - teken_funcs_cursor(t); -} - -static void -teken_subr_newpage(teken_t *t) -{ - - if (t->t_stateflags & TS_CONS25) { - teken_rect_t tr; - - /* Clear screen. */ - tr.tr_begin.tp_row = t->t_originreg.ts_begin; - tr.tr_begin.tp_col = 0; - tr.tr_end.tp_row = t->t_originreg.ts_end; - tr.tr_end.tp_col = t->t_winsize.tp_col; - teken_funcs_fill(t, &tr, BLANK, &t->t_curattr); - - /* Cursor at top left. */ - t->t_cursor.tp_row = t->t_originreg.ts_begin; - t->t_cursor.tp_col = 0; - t->t_stateflags &= ~TS_WRAPPED; - teken_funcs_cursor(t); - } else { - teken_subr_newline(t); - } -} - -static void -teken_subr_next_line(teken_t *t) -{ - - t->t_cursor.tp_col = 0; - teken_subr_newline(t); -} - -static void -teken_subr_operating_system_command(teken_t *t) -{ - - teken_printf("Unsupported operating system command\n"); - t->t_stateflags |= TS_INSTRING; -} - -static void -teken_subr_pan_down(const teken_t *t, unsigned int nrows) -{ - - teken_subr_do_scroll(t, (int)nrows); -} - -static void -teken_subr_pan_up(const teken_t *t, unsigned int nrows) -{ - - teken_subr_do_scroll(t, -(int)nrows); -} - -static void -teken_subr_primary_device_attributes(const teken_t *t, unsigned int request) -{ - - if (request == 0) { - const char response[] = "\x1B[?1;2c"; - - teken_funcs_respond(t, response, sizeof response - 1); - } else { - teken_printf("Unknown DA1\n"); - } -} - -static void -teken_subr_do_putchar(teken_t *t, const teken_pos_t *tp, teken_char_t c, - int width) -{ - - t->t_last = c; - if (t->t_stateflags & TS_INSERT && - tp->tp_col < t->t_winsize.tp_col - width) { - teken_rect_t ctr; - teken_pos_t ctp; - - /* Insert mode. Move existing characters to the right. */ - ctr.tr_begin = *tp; - ctr.tr_end.tp_row = tp->tp_row + 1; - ctr.tr_end.tp_col = t->t_winsize.tp_col - width; - ctp.tp_row = tp->tp_row; - ctp.tp_col = tp->tp_col + width; - teken_funcs_copy(t, &ctr, &ctp); - } - - teken_funcs_putchar(t, tp, c, &t->t_curattr); - - if (width == 2 && tp->tp_col + 1 < t->t_winsize.tp_col) { - teken_pos_t tp2; - teken_attr_t attr; - - /* Print second half of CJK fullwidth character. */ - tp2.tp_row = tp->tp_row; - tp2.tp_col = tp->tp_col + 1; - attr = t->t_curattr; - attr.ta_format |= TF_CJK_RIGHT; - teken_funcs_putchar(t, &tp2, c, &attr); - } -} - -static void -teken_subr_regular_character(teken_t *t, teken_char_t c) -{ - int width; - - if (t->t_stateflags & TS_8BIT) { - if (!(t->t_stateflags & TS_CONS25) && (c <= 0x1b || c == 0x7f)) - return; - c = teken_scs_process(t, c); - width = 1; - } else { - c = teken_scs_process(t, c); - width = teken_wcwidth(c); - /* XXX: Don't process zero-width characters yet. */ - if (width <= 0) - return; - } - - if (t->t_stateflags & TS_CONS25) { - teken_subr_do_putchar(t, &t->t_cursor, c, width); - t->t_cursor.tp_col += width; - - if (t->t_cursor.tp_col >= t->t_winsize.tp_col) { - if (t->t_cursor.tp_row == t->t_scrollreg.ts_end - 1) { - /* Perform scrolling. */ - teken_subr_do_scroll(t, 1); - } else { - /* No scrolling needed. */ - if (t->t_cursor.tp_row < - t->t_winsize.tp_row - 1) - t->t_cursor.tp_row++; - } - t->t_cursor.tp_col = 0; - } - } else if (t->t_stateflags & TS_AUTOWRAP && - ((t->t_stateflags & TS_WRAPPED && - t->t_cursor.tp_col + 1 == t->t_winsize.tp_col) || - t->t_cursor.tp_col + width > t->t_winsize.tp_col)) { - teken_pos_t tp; - - /* - * Perform line wrapping, if: - * - Autowrapping is enabled, and - * - We're in the wrapped state at the last column, or - * - The character to be printed does not fit anymore. - */ - if (t->t_cursor.tp_row == t->t_scrollreg.ts_end - 1) { - /* Perform scrolling. */ - teken_subr_do_scroll(t, 1); - tp.tp_row = t->t_scrollreg.ts_end - 1; - } else { - /* No scrolling needed. */ - tp.tp_row = t->t_cursor.tp_row + 1; - if (tp.tp_row == t->t_winsize.tp_row) { - /* - * Corner case: regular character - * outside scrolling region, but at the - * bottom of the screen. - */ - teken_subr_do_putchar(t, &t->t_cursor, - c, width); - return; - } - } - - tp.tp_col = 0; - teken_subr_do_putchar(t, &tp, c, width); - - t->t_cursor.tp_row = tp.tp_row; - t->t_cursor.tp_col = width; - t->t_stateflags &= ~TS_WRAPPED; - } else { - /* No line wrapping needed. */ - teken_subr_do_putchar(t, &t->t_cursor, c, width); - t->t_cursor.tp_col += width; - - if (t->t_cursor.tp_col >= t->t_winsize.tp_col) { - t->t_stateflags |= TS_WRAPPED; - t->t_cursor.tp_col = t->t_winsize.tp_col - 1; - } else { - t->t_stateflags &= ~TS_WRAPPED; - } - } - - teken_funcs_cursor(t); -} - -static void -teken_subr_reset_dec_mode(teken_t *t, unsigned int cmd) -{ - - switch (cmd) { - case 1: /* Cursor keys mode. */ - t->t_stateflags &= ~TS_CURSORKEYS; - break; - case 2: /* DECANM: ANSI/VT52 mode. */ - teken_printf("DECRST VT52\n"); - break; - case 3: /* 132 column mode. */ - teken_funcs_param(t, TP_132COLS, 0); - teken_subr_reset_to_initial_state(t); - break; - case 5: /* Inverse video. */ - teken_printf("DECRST inverse video\n"); - break; - case 6: /* Origin mode. */ - t->t_stateflags &= ~TS_ORIGIN; - t->t_originreg.ts_begin = 0; - t->t_originreg.ts_end = t->t_winsize.tp_row; - t->t_cursor.tp_row = t->t_cursor.tp_col = 0; - t->t_stateflags &= ~TS_WRAPPED; - teken_funcs_cursor(t); - break; - case 7: /* Autowrap mode. */ - t->t_stateflags &= ~TS_AUTOWRAP; - break; - case 8: /* Autorepeat mode. */ - teken_funcs_param(t, TP_AUTOREPEAT, 0); - break; - case 25: /* Hide cursor. */ - teken_funcs_param(t, TP_SHOWCURSOR, 0); - break; - case 40: /* Disallow 132 columns. */ - teken_printf("DECRST allow 132\n"); - break; - case 45: /* Disable reverse wraparound. */ - teken_printf("DECRST reverse wraparound\n"); - break; - case 47: /* Switch to alternate buffer. */ - teken_printf("Switch to alternate buffer\n"); - break; - case 1000: /* Mouse input. */ - teken_funcs_param(t, TP_MOUSE, 0); - break; - default: - teken_printf("Unknown DECRST: %u\n", cmd); - } -} - -static void -teken_subr_reset_mode(teken_t *t, unsigned int cmd) -{ - - switch (cmd) { - case 4: - t->t_stateflags &= ~TS_INSERT; - break; - default: - teken_printf("Unknown reset mode: %u\n", cmd); - } -} - -static void -teken_subr_do_resize(teken_t *t) -{ - - t->t_scrollreg.ts_begin = 0; - t->t_scrollreg.ts_end = t->t_winsize.tp_row; - t->t_originreg = t->t_scrollreg; -} - -static void -teken_subr_do_reset(teken_t *t) -{ - - t->t_curattr = t->t_defattr; - t->t_cursor.tp_row = t->t_cursor.tp_col = 0; - t->t_scrollreg.ts_begin = 0; - t->t_scrollreg.ts_end = t->t_winsize.tp_row; - t->t_originreg = t->t_scrollreg; - t->t_stateflags &= TS_8BIT|TS_CONS25; - t->t_stateflags |= TS_AUTOWRAP; - - t->t_scs[0] = teken_scs_us_ascii; - t->t_scs[1] = teken_scs_us_ascii; - t->t_curscs = 0; - - teken_subr_save_cursor(t); - teken_tab_default(t); -} - -static void -teken_subr_reset_to_initial_state(teken_t *t) -{ - - teken_subr_do_reset(t); - teken_subr_erase_display(t, 2); - teken_funcs_param(t, TP_SHOWCURSOR, 1); - teken_funcs_cursor(t); -} - -static void -teken_subr_restore_cursor(teken_t *t) -{ - - t->t_cursor = t->t_saved_cursor; - t->t_curattr = t->t_saved_curattr; - t->t_scs[t->t_curscs] = t->t_saved_curscs; - t->t_stateflags &= ~TS_WRAPPED; - - /* Get out of origin mode when the cursor is moved outside. */ - if (t->t_cursor.tp_row < t->t_originreg.ts_begin || - t->t_cursor.tp_row >= t->t_originreg.ts_end) { - t->t_stateflags &= ~TS_ORIGIN; - t->t_originreg.ts_begin = 0; - t->t_originreg.ts_end = t->t_winsize.tp_row; - } - - teken_funcs_cursor(t); -} - -static void -teken_subr_reverse_index(teken_t *t) -{ - - if (t->t_cursor.tp_row > t->t_scrollreg.ts_begin) { - t->t_cursor.tp_row--; - t->t_stateflags &= ~TS_WRAPPED; - teken_funcs_cursor(t); - } else { - teken_subr_do_scroll(t, -1); - } -} - -static void -teken_subr_save_cursor(teken_t *t) -{ - - t->t_saved_cursor = t->t_cursor; - t->t_saved_curattr = t->t_curattr; - t->t_saved_curscs = t->t_scs[t->t_curscs]; -} - -static void -teken_subr_secondary_device_attributes(const teken_t *t, unsigned int request) -{ - - if (request == 0) { - const char response[] = "\x1B[>0;10;0c"; - teken_funcs_respond(t, response, sizeof response - 1); - } else { - teken_printf("Unknown DA2\n"); - } -} - -static void -teken_subr_set_dec_mode(teken_t *t, unsigned int cmd) -{ - - switch (cmd) { - case 1: /* Cursor keys mode. */ - t->t_stateflags |= TS_CURSORKEYS; - break; - case 2: /* DECANM: ANSI/VT52 mode. */ - teken_printf("DECSET VT52\n"); - break; - case 3: /* 132 column mode. */ - teken_funcs_param(t, TP_132COLS, 1); - teken_subr_reset_to_initial_state(t); - break; - case 5: /* Inverse video. */ - teken_printf("DECSET inverse video\n"); - break; - case 6: /* Origin mode. */ - t->t_stateflags |= TS_ORIGIN; - t->t_originreg = t->t_scrollreg; - t->t_cursor.tp_row = t->t_scrollreg.ts_begin; - t->t_cursor.tp_col = 0; - t->t_stateflags &= ~TS_WRAPPED; - teken_funcs_cursor(t); - break; - case 7: /* Autowrap mode. */ - t->t_stateflags |= TS_AUTOWRAP; - break; - case 8: /* Autorepeat mode. */ - teken_funcs_param(t, TP_AUTOREPEAT, 1); - break; - case 25: /* Display cursor. */ - teken_funcs_param(t, TP_SHOWCURSOR, 1); - break; - case 40: /* Allow 132 columns. */ - teken_printf("DECSET allow 132\n"); - break; - case 45: /* Enable reverse wraparound. */ - teken_printf("DECSET reverse wraparound\n"); - break; - case 47: /* Switch to alternate buffer. */ - teken_printf("Switch away from alternate buffer\n"); - break; - case 1000: /* Mouse input. */ - teken_funcs_param(t, TP_MOUSE, 1); - break; - default: - teken_printf("Unknown DECSET: %u\n", cmd); - } -} - -static void -teken_subr_set_mode(teken_t *t, unsigned int cmd) -{ - - switch (cmd) { - case 4: - teken_printf("Insert mode\n"); - t->t_stateflags |= TS_INSERT; - break; - default: - teken_printf("Unknown set mode: %u\n", cmd); - } -} - -static void -teken_subr_set_graphic_rendition(teken_t *t, unsigned int ncmds, - const unsigned int cmds[]) -{ - unsigned int i, n; - - /* No attributes means reset. */ - if (ncmds == 0) { - t->t_curattr = t->t_defattr; - return; - } - - for (i = 0; i < ncmds; i++) { - n = cmds[i]; - - switch (n) { - case 0: /* Reset. */ - t->t_curattr = t->t_defattr; - break; - case 1: /* Bold. */ - t->t_curattr.ta_format |= TF_BOLD; - break; - case 4: /* Underline. */ - t->t_curattr.ta_format |= TF_UNDERLINE; - break; - case 5: /* Blink. */ - t->t_curattr.ta_format |= TF_BLINK; - break; - case 7: /* Reverse. */ - t->t_curattr.ta_format |= TF_REVERSE; - break; - case 22: /* Remove bold. */ - t->t_curattr.ta_format &= ~TF_BOLD; - break; - case 24: /* Remove underline. */ - t->t_curattr.ta_format &= ~TF_UNDERLINE; - break; - case 25: /* Remove blink. */ - t->t_curattr.ta_format &= ~TF_BLINK; - break; - case 27: /* Remove reverse. */ - t->t_curattr.ta_format &= ~TF_REVERSE; - break; - case 30: /* Set foreground color: black */ - case 31: /* Set foreground color: red */ - case 32: /* Set foreground color: green */ - case 33: /* Set foreground color: brown */ - case 34: /* Set foreground color: blue */ - case 35: /* Set foreground color: magenta */ - case 36: /* Set foreground color: cyan */ - case 37: /* Set foreground color: white */ - t->t_curattr.ta_fgcolor = n - 30; - break; - case 38: /* Set foreground color: 256 color mode */ - if (i + 2 >= ncmds || cmds[i + 1] != 5) - continue; - t->t_curattr.ta_fgcolor = cmds[i + 2]; - i += 2; - break; - case 39: /* Set default foreground color. */ - t->t_curattr.ta_fgcolor = t->t_defattr.ta_fgcolor; - break; - case 40: /* Set background color: black */ - case 41: /* Set background color: red */ - case 42: /* Set background color: green */ - case 43: /* Set background color: brown */ - case 44: /* Set background color: blue */ - case 45: /* Set background color: magenta */ - case 46: /* Set background color: cyan */ - case 47: /* Set background color: white */ - t->t_curattr.ta_bgcolor = n - 40; - break; - case 48: /* Set background color: 256 color mode */ - if (i + 2 >= ncmds || cmds[i + 1] != 5) - continue; - t->t_curattr.ta_bgcolor = cmds[i + 2]; - i += 2; - break; - case 49: /* Set default background color. */ - t->t_curattr.ta_bgcolor = t->t_defattr.ta_bgcolor; - break; - case 90: /* Set bright foreground color: black */ - case 91: /* Set bright foreground color: red */ - case 92: /* Set bright foreground color: green */ - case 93: /* Set bright foreground color: brown */ - case 94: /* Set bright foreground color: blue */ - case 95: /* Set bright foreground color: magenta */ - case 96: /* Set bright foreground color: cyan */ - case 97: /* Set bright foreground color: white */ - t->t_curattr.ta_fgcolor = (n - 90) + 8; - break; - case 100: /* Set bright background color: black */ - case 101: /* Set bright background color: red */ - case 102: /* Set bright background color: green */ - case 103: /* Set bright background color: brown */ - case 104: /* Set bright background color: blue */ - case 105: /* Set bright background color: magenta */ - case 106: /* Set bright background color: cyan */ - case 107: /* Set bright background color: white */ - t->t_curattr.ta_bgcolor = (n - 100) + 8; - break; - default: - teken_printf("unsupported attribute %u\n", n); - } - } -} - -static void -teken_subr_set_top_and_bottom_margins(teken_t *t, unsigned int top, - unsigned int bottom) -{ - - /* Adjust top row number. */ - if (top > 0) - top--; - /* Adjust bottom row number. */ - if (bottom == 0 || bottom > t->t_winsize.tp_row) - bottom = t->t_winsize.tp_row; - - /* Invalid arguments. */ - if (top >= bottom - 1) { - top = 0; - bottom = t->t_winsize.tp_row; - } - - /* Apply scrolling region. */ - t->t_scrollreg.ts_begin = top; - t->t_scrollreg.ts_end = bottom; - if (t->t_stateflags & TS_ORIGIN) - t->t_originreg = t->t_scrollreg; - - /* Home cursor to the top left of the scrolling region. */ - t->t_cursor.tp_row = t->t_originreg.ts_begin; - t->t_cursor.tp_col = 0; - t->t_stateflags &= ~TS_WRAPPED; - teken_funcs_cursor(t); -} - -static void -teken_subr_single_height_double_width_line(const teken_t *t) -{ - - (void)t; - teken_printf("single height double width???\n"); -} - -static void -teken_subr_single_height_single_width_line(const teken_t *t) -{ - - (void)t; - teken_printf("single height single width???\n"); -} - -static void -teken_subr_string_terminator(const teken_t *t) -{ - - (void)t; - /* - * Strings are already terminated in teken_input_char() when ^[ - * is inserted. - */ -} - -static void -teken_subr_tab_clear(teken_t *t, unsigned int cmd) -{ - - switch (cmd) { - case 0: - teken_tab_clear(t, t->t_cursor.tp_col); - break; - case 3: - memset(t->t_tabstops, 0, T_NUMCOL / 8); - break; - default: - break; - } -} - -static void -teken_subr_vertical_position_absolute(teken_t *t, unsigned int row) -{ - - row = (row - 1) + t->t_originreg.ts_begin; - t->t_cursor.tp_row = row < t->t_originreg.ts_end ? - row : t->t_originreg.ts_end - 1; - - t->t_stateflags &= ~TS_WRAPPED; - teken_funcs_cursor(t); -} - -static void -teken_subr_repeat_last_graphic_char(teken_t *t, unsigned int rpts) -{ - - for (; t->t_last != 0 && rpts > 0; rpts--) - teken_subr_regular_character(t, t->t_last); -} - diff --git a/bin/varnishtest/teken_subr_compat.h b/bin/varnishtest/teken_subr_compat.h deleted file mode 100644 index 7db4d858d..000000000 --- a/bin/varnishtest/teken_subr_compat.h +++ /dev/null @@ -1,153 +0,0 @@ -/*- - * SPDX-License-Identifier: BSD-2-Clause-FreeBSD - * - * Copyright (c) 2008-2009 Ed Schouten - * All rights reserved. - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * $FreeBSD: head/sys/teken/teken_subr_compat.h 332297 2018-04-08 19:23:50Z phk $ - */ - -static void -teken_subr_cons25_set_border(const teken_t *t, unsigned int c) -{ - - teken_funcs_param(t, TP_SETBORDER, c); -} - -static void -teken_subr_cons25_set_global_cursor_shape(const teken_t *t, unsigned int ncmds, - const unsigned int cmds[]) -{ - unsigned int code, i; - - /* - * Pack the args to work around API deficiencies. This requires - * knowing too much about the low level to be fully compatible. - * Returning when ncmds > 3 is necessary and happens to be - * compatible. Discarding high bits is necessary and happens to - * be incompatible only for invalid args when ncmds == 3. - */ - if (ncmds > 3) - return; - code = 0; - for (i = ncmds; i > 0; i--) - code = (code << 8) | (cmds[i - 1] & 0xff); - code = (code << 8) | ncmds; - teken_funcs_param(t, TP_SETGLOBALCURSOR, code); -} - -static void -teken_subr_cons25_set_local_cursor_type(const teken_t *t, unsigned int type) -{ - - teken_funcs_param(t, TP_SETLOCALCURSOR, type); -} - -static const teken_color_t cons25_colors[8] = { TC_BLACK, TC_BLUE, - TC_GREEN, TC_CYAN, TC_RED, TC_MAGENTA, TC_BROWN, TC_WHITE }; - -static void -teken_subr_cons25_set_default_background(teken_t *t, unsigned int c) -{ - - t->t_defattr.ta_bgcolor = cons25_colors[c % 8] | (c & 8); - t->t_curattr.ta_bgcolor = cons25_colors[c % 8] | (c & 8); -} - -static void -teken_subr_cons25_set_default_foreground(teken_t *t, unsigned int c) -{ - - t->t_defattr.ta_fgcolor = cons25_colors[c % 8] | (c & 8); - t->t_curattr.ta_fgcolor = cons25_colors[c % 8] | (c & 8); -} - -static const teken_color_t cons25_revcolors[8] = { 0, 4, 2, 6, 1, 5, 3, 7 }; - -void -teken_get_defattr_cons25(const teken_t *t, int *fg, int *bg) -{ - - *fg = cons25_revcolors[teken_256to8(t->t_defattr.ta_fgcolor)]; - if (t->t_defattr.ta_format & TF_BOLD) - *fg += 8; - *bg = cons25_revcolors[teken_256to8(t->t_defattr.ta_bgcolor)]; -} - -static void -teken_subr_cons25_switch_virtual_terminal(const teken_t *t, unsigned int vt) -{ - - teken_funcs_param(t, TP_SWITCHVT, vt); -} - -static void -teken_subr_cons25_set_bell_pitch_duration(const teken_t *t, unsigned int pitch, - unsigned int duration) -{ - - teken_funcs_param(t, TP_SETBELLPD, (pitch << 16) | - (duration & 0xffff)); -} - -static void -teken_subr_cons25_set_graphic_rendition(teken_t *t, unsigned int cmd, - unsigned int param) -{ - - (void)param; - switch (cmd) { - case 0: /* Reset. */ - t->t_curattr = t->t_defattr; - break; - default: - teken_printf("unsupported attribute %u\n", cmd); - } -} - -static void -teken_subr_cons25_set_terminal_mode(teken_t *t, unsigned int mode) -{ - - switch (mode) { - case 0: /* Switch terminal to xterm. */ - t->t_stateflags &= ~TS_CONS25; - break; - case 1: /* Switch terminal to cons25. */ - t->t_stateflags |= TS_CONS25; - break; - default: - break; - } -} - -#if 0 -static void -teken_subr_vt52_decid(teken_t *t) -{ - const char response[] = "\x1B/Z"; - - teken_funcs_respond(t, response, sizeof response - 1); -} -#endif diff --git a/bin/varnishtest/teken_wcwidth.h b/bin/varnishtest/teken_wcwidth.h deleted file mode 100644 index 70d92060f..000000000 --- a/bin/varnishtest/teken_wcwidth.h +++ /dev/null @@ -1,120 +0,0 @@ -/* - * Markus Kuhn -- 2007-05-26 (Unicode 5.0) - * - * Permission to use, copy, modify, and distribute this software - * for any purpose and without fee is hereby granted. The author - * disclaims all warranties with regard to this software. - * - * Latest version: http://www.cl.cam.ac.uk/~mgk25/ucs/wcwidth.c - * - * $FreeBSD: head/sys/teken/teken_wcwidth.h 332297 2018-04-08 19:23:50Z phk $ - */ - -struct interval { - teken_char_t first; - teken_char_t last; -}; - -/* auxiliary function for binary search in interval table */ -static int bisearch(teken_char_t ucs, const struct interval *table, int max) { - int min = 0; - int mid; - - if (ucs < table[0].first || ucs > table[max].last) - return 0; - while (max >= min) { - mid = (min + max) / 2; - if (ucs > table[mid].last) - min = mid + 1; - else if (ucs < table[mid].first) - max = mid - 1; - else - return 1; - } - - return 0; -} - -static int teken_wcwidth(teken_char_t ucs) -{ - /* sorted list of non-overlapping intervals of non-spacing characters */ - /* generated by "uniset +cat=Me +cat=Mn +cat=Cf -00AD +1160-11FF +200B c" */ - static const struct interval combining[] = { - { 0x0300, 0x036F }, { 0x0483, 0x0486 }, { 0x0488, 0x0489 }, - { 0x0591, 0x05BD }, { 0x05BF, 0x05BF }, { 0x05C1, 0x05C2 }, - { 0x05C4, 0x05C5 }, { 0x05C7, 0x05C7 }, { 0x0600, 0x0603 }, - { 0x0610, 0x0615 }, { 0x064B, 0x065E }, { 0x0670, 0x0670 }, - { 0x06D6, 0x06E4 }, { 0x06E7, 0x06E8 }, { 0x06EA, 0x06ED }, - { 0x070F, 0x070F }, { 0x0711, 0x0711 }, { 0x0730, 0x074A }, - { 0x07A6, 0x07B0 }, { 0x07EB, 0x07F3 }, { 0x0901, 0x0902 }, - { 0x093C, 0x093C }, { 0x0941, 0x0948 }, { 0x094D, 0x094D }, - { 0x0951, 0x0954 }, { 0x0962, 0x0963 }, { 0x0981, 0x0981 }, - { 0x09BC, 0x09BC }, { 0x09C1, 0x09C4 }, { 0x09CD, 0x09CD }, - { 0x09E2, 0x09E3 }, { 0x0A01, 0x0A02 }, { 0x0A3C, 0x0A3C }, - { 0x0A41, 0x0A42 }, { 0x0A47, 0x0A48 }, { 0x0A4B, 0x0A4D }, - { 0x0A70, 0x0A71 }, { 0x0A81, 0x0A82 }, { 0x0ABC, 0x0ABC }, - { 0x0AC1, 0x0AC5 }, { 0x0AC7, 0x0AC8 }, { 0x0ACD, 0x0ACD }, - { 0x0AE2, 0x0AE3 }, { 0x0B01, 0x0B01 }, { 0x0B3C, 0x0B3C }, - { 0x0B3F, 0x0B3F }, { 0x0B41, 0x0B43 }, { 0x0B4D, 0x0B4D }, - { 0x0B56, 0x0B56 }, { 0x0B82, 0x0B82 }, { 0x0BC0, 0x0BC0 }, - { 0x0BCD, 0x0BCD }, { 0x0C3E, 0x0C40 }, { 0x0C46, 0x0C48 }, - { 0x0C4A, 0x0C4D }, { 0x0C55, 0x0C56 }, { 0x0CBC, 0x0CBC }, - { 0x0CBF, 0x0CBF }, { 0x0CC6, 0x0CC6 }, { 0x0CCC, 0x0CCD }, - { 0x0CE2, 0x0CE3 }, { 0x0D41, 0x0D43 }, { 0x0D4D, 0x0D4D }, - { 0x0DCA, 0x0DCA }, { 0x0DD2, 0x0DD4 }, { 0x0DD6, 0x0DD6 }, - { 0x0E31, 0x0E31 }, { 0x0E34, 0x0E3A }, { 0x0E47, 0x0E4E }, - { 0x0EB1, 0x0EB1 }, { 0x0EB4, 0x0EB9 }, { 0x0EBB, 0x0EBC }, - { 0x0EC8, 0x0ECD }, { 0x0F18, 0x0F19 }, { 0x0F35, 0x0F35 }, - { 0x0F37, 0x0F37 }, { 0x0F39, 0x0F39 }, { 0x0F71, 0x0F7E }, - { 0x0F80, 0x0F84 }, { 0x0F86, 0x0F87 }, { 0x0F90, 0x0F97 }, - { 0x0F99, 0x0FBC }, { 0x0FC6, 0x0FC6 }, { 0x102D, 0x1030 }, - { 0x1032, 0x1032 }, { 0x1036, 0x1037 }, { 0x1039, 0x1039 }, - { 0x1058, 0x1059 }, { 0x1160, 0x11FF }, { 0x135F, 0x135F }, - { 0x1712, 0x1714 }, { 0x1732, 0x1734 }, { 0x1752, 0x1753 }, - { 0x1772, 0x1773 }, { 0x17B4, 0x17B5 }, { 0x17B7, 0x17BD }, - { 0x17C6, 0x17C6 }, { 0x17C9, 0x17D3 }, { 0x17DD, 0x17DD }, - { 0x180B, 0x180D }, { 0x18A9, 0x18A9 }, { 0x1920, 0x1922 }, - { 0x1927, 0x1928 }, { 0x1932, 0x1932 }, { 0x1939, 0x193B }, - { 0x1A17, 0x1A18 }, { 0x1B00, 0x1B03 }, { 0x1B34, 0x1B34 }, - { 0x1B36, 0x1B3A }, { 0x1B3C, 0x1B3C }, { 0x1B42, 0x1B42 }, - { 0x1B6B, 0x1B73 }, { 0x1DC0, 0x1DCA }, { 0x1DFE, 0x1DFF }, - { 0x200B, 0x200F }, { 0x202A, 0x202E }, { 0x2060, 0x2063 }, - { 0x206A, 0x206F }, { 0x20D0, 0x20EF }, { 0x302A, 0x302F }, - { 0x3099, 0x309A }, { 0xA806, 0xA806 }, { 0xA80B, 0xA80B }, - { 0xA825, 0xA826 }, { 0xFB1E, 0xFB1E }, { 0xFE00, 0xFE0F }, - { 0xFE20, 0xFE23 }, { 0xFEFF, 0xFEFF }, { 0xFFF9, 0xFFFB }, - { 0x10A01, 0x10A03 }, { 0x10A05, 0x10A06 }, { 0x10A0C, 0x10A0F }, - { 0x10A38, 0x10A3A }, { 0x10A3F, 0x10A3F }, { 0x1D167, 0x1D169 }, - { 0x1D173, 0x1D182 }, { 0x1D185, 0x1D18B }, { 0x1D1AA, 0x1D1AD }, - { 0x1D242, 0x1D244 }, { 0xE0001, 0xE0001 }, { 0xE0020, 0xE007F }, - { 0xE0100, 0xE01EF } - }; - - /* test for 8-bit control characters */ - if (ucs == 0) - return 0; - if (ucs < 32 || (ucs >= 0x7f && ucs < 0xa0)) - return -1; - - /* binary search in table of non-spacing characters */ - if (bisearch(ucs, combining, - sizeof(combining) / sizeof(struct interval) - 1)) - return 0; - - /* if we arrive here, ucs is not a combining or C0/C1 control character */ - - return 1 + - (int)(ucs >= 0x1100 && - (ucs <= 0x115f || /* Hangul Jamo init. consonants */ - ucs == 0x2329 || ucs == 0x232a || - (ucs >= 0x2e80 && ucs <= 0xa4cf && - ucs != 0x303f) || /* CJK ... Yi */ - (ucs >= 0xac00 && ucs <= 0xd7a3) || /* Hangul Syllables */ - (ucs >= 0xf900 && ucs <= 0xfaff) || /* CJK Compatibility Ideographs */ - (ucs >= 0xfe10 && ucs <= 0xfe19) || /* Vertical forms */ - (ucs >= 0xfe30 && ucs <= 0xfe6f) || /* CJK Compatibility Forms */ - (ucs >= 0xff00 && ucs <= 0xff60) || /* Fullwidth Forms */ - (ucs >= 0xffe0 && ucs <= 0xffe6) || - (ucs >= 0x20000 && ucs <= 0x2fffd) || - (ucs >= 0x30000 && ucs <= 0x3fffd))); -} diff --git a/bin/varnishtest/tests/a00000.vtc b/bin/varnishtest/tests/a00000.vtc deleted file mode 100644 index 916a7a3bc..000000000 --- a/bin/varnishtest/tests/a00000.vtc +++ /dev/null @@ -1,170 +0,0 @@ -vtest "Test vtest itself" - -shell -exit 1 -match {v.*test \[options]} {vtest -h} - -shell -exit 1 -match {-D.*Define macro} {vtest -h} - -shell { - pwd - echo 'notvtest foo bar' > _.vtc - echo 'shell "exit 9"' >> _.vtc -} - -shell -exit 2 -expect {doesn't start with 'vtest' or 'varnishtest} { - vtest -v _.vtc -} - -shell -exit 77 -expect {0 tests failed, 1 tests skipped, 0 tests passed} { - unset TMPDIR - vtest -k _.vtc -} - -# Test external macro-def with a two-turtle test -shell -expect {__=barf} { - echo vtest foo > _.vtc - printf 'shell {echo %c{foobar} > ${tmpdir}/__}' '$' >> _.vtc - vtest -q -Dfoobar=barf _.vtc - echo __=`cat __` -} - -# Test a test failure -shell -exit 2 -expect {TEST _.vtc FAILED} { - echo vtest foo > _.vtc - echo 'shell {false}' >> _.vtc - exec vtest -v _.vtc || true -} - -# Test a test skip -shell -exit 77 -expect {TEST _.vtc skipped} { - echo vtest foo > _.vtc - echo 'feature cmd false' >> _.vtc - exec vtest -v _.vtc || true -} - -# Simple process tests - -process p1 "cat" -start -process p1 -writeln "foo" -process p1 -expect-text 2 1 foo -process p1 -stop -process p1 -wait -shell "grep -q foo ${p1_out}" -shell "test -f ${p1_err} -a ! -s ${p1_err}" - -process p2 -log "cat" -start -process p2 -writeln "bar" -process p2 -expect-text 2 1 bar -process p2 -write "\x04" -process p2 -wait -shell "grep -q bar ${p2_out}" -shell "test -f ${p2_err} -a ! -s ${p2_err}" - -process p3 -dump "cat" -start -process p3 -writeln "baz" -process p3 -expect-text 2 1 baz -process p3 -kill KILL -process p3 -wait -shell "grep -q baz ${p3_out}" -shell "test -f ${p3_err} -a ! -s ${p3_err}" - -process p4 -hexdump "cat" -start -process p4 -writeln "b\001z" -process p4 -expect-text 2 1 "b" -process p4 -kill TERM -process p4 -wait -screen_dump - -# Curses process tests - -process p5 "ps -lw | grep '[p][s]' ; tty ; echo @" -start -process p5 -expect-text 0 0 {@} -screen_dump -wait - -process p6 "stty -a ; echo '*'" -start -process p6 -expect-text 0 0 {*} -screen_dump -wait - -process p7 -hexdump {stty raw -echo; stty -a ; echo "*" ; cat} -start -process p7 -expect-text 0 0 "*" -screen_dump - -process p7 -write "\x1b[H\x1b[2Jzzzzzzz" -process p7 -write "\x0c1\x1b[79C2\x08>\x1b[25;1H3\x1b[25;80H" -process p7 -write "\x1b[H\x1b[2J1\x1b[79C2\x08>\x1b[25;1H3\x1b[25;80H" -process p7 -write "4\x08>\x1b[A\x1b[Cv\x1b[22A^\x1b[79D^\x1b[;2H<\n\n\n\n" -process p7 -write "\n\n\n\n\n\n\n\n\x1b[B\x1b[11B\x08<\x1b[24;Hv\x1b[12;1H" -process p7 -write "111111112222222333333\x0d\x0a111111112" -process p7 -write "222222333333\x0d\x0a111111112222222333333 UTF8: " -process p7 -writehex {c2 a2 20} -process p7 -writehex {e2 82 ac 20} -process p7 -writehex {f0 90 80 80 20} -process p7 -writehex {f0 9f 90 b0 20} -process p7 -writehex {f0 a0 80 80 20} -process p7 -writehex {f0 b0 80 80 20} -process p7 -write "\x1b[22;24;25;27;30;47;49;97;107m" -process p7 -write "\x1b[22;24;25;27;30m" -process p7 -write "\x1b[47;49;97;107m" -process p7 -write "\x0d\x0a111111112222222333333\x0d\x0a\x1b[12" -process p7 -write ";12H\x1b[K\x1b[13;12H\x1b[0K\x1b[14;12H\x1b[1K\x1b" -process p7 -write "[15;12H\x1b[2K\x1b[3;1Hline3 <\x0d\x0a" - -process p7 -need-bytes 310 -expect-text 3 1 "line3 <" -process p7 -expect-cursor 4 1 -process p7 -expect-cursor 4 0 -process p7 -expect-cursor 0 1 -process p7 -screen-dump - -# Also exercise CONS25 mode -process p7 -write "\x1b[=1T" -process p7 -write "\x1b[=2T" -process p7 -write "\x1b[8z" -process p7 -write "\x1b[0x" -process p7 -write "\x1b[=1A" -process p7 -write "\x1b[=1;2B" -process p7 -write "\x1b[=1;2;3C" -process p7 -write "\x1b[=1;2;3;4C" -process p7 -write "\x1b[=1F" -process p7 -write "\x1b[=1G" -process p7 -write "\x1b[=1S" -process p7 -writehex {0c 08 40 0d 0a 08} - -process p7 -expect-text 1 1 "@" -process p7 -expect-cursor 1 80 -process p7 -writehex "0c 41 0e 42 0f" -process p7 -expect-text 1 1 "A" -process p7 -expect-text 0 0 "B" -process p7 -write "\x1b[=0T" - -process p7 -writehex "0c 0a 0d 43 0a 08 08 0e 44 0f" - -process p7 -expect-text 3 1 "C" -process p7 -expect-text 4 1 "D" -process p7 -write "\x1b[2T" -process p7 -expect-text 5 1 "C" -process p7 -expect-text 6 1 "D" -process p7 -write "\x1b[3S" -process p7 -expect-text 3 1 "D" - -process p7 -write "\x1b[4;200H%" -process p7 -expect-text 4 80 "%" - -process p7 -write "\x1b[7;7H\x09X\x09Y\x09Z\x1b[2ZW\x1b[2Ew\x1b[F*" - -process p7 -expect-text 7 17 "W" -process p7 -expect-text 9 1 "w" -process p7 -expect-text 8 1 "*" - -process p7 -write "\x1b[10;4HABCDEFGHIJKLMN\x1b(A#$%\x1b)A" -process p7 -write "\x1b[8G\x1b[2X>" -process p7 -expect-text 10 8 ">" -process p7 -screen-dump - -# Test responses -process p7 -write "\x1b[3;1HA\x1b[5n" -process p7 -write "\x1b[4;1HB\x1b[6n" -process p7 -write "\x1b[5;1HC\x1b[15n" -process p7 -write "\x1b[6;1HD\x1b[25n" -process p7 -write "\x1b[7;1HE\x1b[26n" -process p7 -write "\x1b[8;1HF\x1b[?26n" -process p7 -write "\x1b[9;1HG\x1bPfutfutfut\x01" -process p7 -write "\x1b[10;1HH\x1b]futfutfut\x01" -process p7 -write "\x1b[11;1HI\x1b[>c" -process p7 -write "\x1b[24;1HW" -process p7 -expect-text 24 1 "W" -process p7 -screen-dump diff --git a/bin/varnishtest/tests/a00001.vtc b/bin/varnishtest/tests/a00001.vtc deleted file mode 100644 index 3647e01e3..000000000 --- a/bin/varnishtest/tests/a00001.vtc +++ /dev/null @@ -1,233 +0,0 @@ -vtest "Test Teken terminal emulator" - -feature cmd "vttest --version 2>&1 | grep -q Usage" - -process p4 {vttest} -ansi-response -start - -process p4 -expect-text 21 11 "Enter choice number (0 - 12):" -process p4 -screen_dump - -# 1. Test of cursor movements -process p4 -writehex "31 0d" -process p4 -expect-text 14 61 "RETURN" -process p4 -screen_dump - -process p4 -writehex 0d -process p4 -expect-text 14 87 "RETURN" -process p4 -screen_dump - -process p4 -writehex 0d -process p4 -expect-text 22 7 "RETURN" -process p4 -screen_dump - -process p4 -writehex 0d -process p4 -expect-text 3 132 "i" -process p4 -expect-text 22 7 "RETURN" -process p4 -screen_dump - -process p4 -writehex 0d -process p4 -expect-text 9 7 "RETURN" -process p4 -screen_dump - -process p4 -writehex 0d -process p4 -expect-text 4 1 "This is a correct sentence" -process p4 -expect-text 20 7 "RETURN" -process p4 -screen_dump - -process p4 -writehex 0d -process p4 -expect-text 21 11 "Enter choice number (0 - 12):" -process p4 -screen_dump - -# 2. Test of screen features -process p4 -writehex "32 0d" -process p4 -expect-text 8 1 "Push " -process p4 -screen_dump - -process p4 -writehex 0d -process p4 -expect-text 5 1 "should look the same. Push " -process p4 -screen_dump - -process p4 -writehex 0d -process p4 -expect-text 0 0 "This is 132 column mode, light background.Push " -process p4 -screen_dump - -process p4 -writehex 0d -process p4 -expect-text 0 0 "This is 80 column mode, light background.Push " -process p4 -screen_dump - -process p4 -writehex 0d -process p4 -expect-text 0 0 "This is 132 column mode, dark background.Push " -process p4 -screen_dump - -process p4 -writehex 0d -process p4 -expect-text 0 0 "This is 80 column mode, dark background.Push " -process p4 -screen_dump - -process p4 -writehex 0d -process p4 -expect-text 0 1 "Push " -process p4 -screen_dump - -process p4 -writehex 0d -process p4 -expect-text 3 1 "Soft scroll down region [1..24] size 24 Line 28" -process p4 -expect-text 1 1 "Push " -process p4 -screen_dump - -process p4 -writehex 0d -process p4 -expect-text 0 1 "Jump scroll down region [12..13] size 2 Line 29" -process p4 -expect-text 0 1 "Push " -process p4 -screen_dump - -process p4 -writehex 0d -process p4 -expect-text 3 1 "Jump scroll down region [1..24] size 24 Line 28" -process p4 -expect-text 1 1 "Push " -process p4 -screen_dump - -process p4 -writehex 0d -process p4 -expect-text 23 0 "Push " -process p4 -screen_dump - -process p4 -writehex 0d -process p4 -expect-text 1 0 "Push " -process p4 -screen_dump - -process p4 -writehex 0d -process p4 -expect-text 23 1 "Dark background. Push " -process p4 -screen_dump - -process p4 -writehex 0d -process p4 -expect-text 23 1 "Light background. Push " -process p4 -screen_dump - -process p4 -writehex 0d -process p4 -expect-text 24 1 "Push " -process p4 -screen_dump - -process p4 -writehex 0d -process p4 -expect-text 21 11 "Enter choice number (0 - 12):" -process p4 -screen_dump - -# 4. Test of double-sized characters -process p4 -writehex "34 0d" -process p4 -expect-text 21 1 "This is not a double-width line" -process p4 -expect-text 23 1 "Push " -process p4 -screen_dump - -process p4 -writehex "0d" -process p4 -expect-text 21 1 "This **is** a double-width line" -process p4 -expect-text 23 1 "Push " -process p4 -screen_dump - -process p4 -writehex "0d" -process p4 -expect-text 21 1 "This is not a double-width line" -process p4 -expect-text 23 1 "Push " -process p4 -screen_dump - -process p4 -writehex "0d" -process p4 -expect-text 21 1 "This **is** a double-width line" -process p4 -expect-text 23 1 "Push " -process p4 -screen_dump - -process p4 -writehex "0d" -delay 2 -process p4 -expect-text 23 41 "Push " -process p4 -screen_dump - -process p4 -writehex "0d" -delay 2 -process p4 -expect-text 1 1 "Exactly half of the box should remain. Push " -process p4 -screen_dump - -process p4 -writehex 0d -process p4 -expect-text 21 11 "Enter choice number (0 - 12):" -process p4 -screen_dump - -# 8. Test of VT102 features (Insert/Delete Char/Line) -process p4 -writehex "38 0d" -process p4 -expect-text 4 1 "Screen accordion test (Insert & Delete Line). Push D" -process p4 -screen_dump - -process p4 -writehex 0d -process p4 -expect-text 2 45 "nothing more. Push " -process p4 -screen_dump - -process p4 -writehex 0d -process p4 -expect-text 4 59 "*B'. Push " -process p4 -screen_dump - -process p4 -writehex 0d -process p4 -expect-text 4 52 "'AB'. Push " -process p4 -screen_dump - -process p4 -writehex 0d -process p4 -expect-text 5 1 "by one. Push E" -process p4 -screen_dump - -process p4 -writehex 0d -process p4 -expect-text 5 1 "by one. Push EEEEEEEEEEEEE " -process p4 -screen_dump - -process p4 -writehex 0d -process p4 -expect-text 10 1 "Push " -process p4 -screen_dump - -process p4 -writehex 0d -process p4 -expect-text 4 1 "Screen accordion test (Insert & Delete Line). Push D" -process p4 -screen_dump - -process p4 -writehex 0d -process p4 -expect-text 2 45 "nothing more. Push " -process p4 -screen_dump - -process p4 -writehex 0d -process p4 -expect-text 4 59 "*B'. Push " -process p4 -screen_dump - -process p4 -writehex 0d -process p4 -expect-text 4 52 "'AB'. Push " -process p4 -screen_dump - -process p4 -writehex 0d -process p4 -expect-text 5 1 "by one. Push E" -process p4 -screen_dump - - -process p4 -writehex 0d -process p4 -expect-text 5 59 "EEE " -process p4 -expect-text 5 1 "by one. Push E" -process p4 -screen_dump - -process p4 -writehex 0d -process p4 -expect-text 10 1 "Push " -process p4 -screen_dump - -process p4 -writehex 0d -process p4 -expect-text 21 11 "Enter choice number (0 - 12):" -process p4 -screen_dump - -# 11. Test non-VT100 (e.g., VT220, XTERM) terminals -process p4 -writehex "31 31 0d" -process p4 -expect-text 0 0 "Menu 11: Non-VT100 Tests" - -process p4 -writehex "37 0d" -process p4 -expect-text 0 0 "Menu 11.7: Miscellaneous ISO-6429 (ECMA-48) Tests" - -process p4 -writehex "32 0d" -process p4 -expect-text 0 0 "Push " -process p4 -screen_dump -process p4 -expect-text 20 1 "Test Repeat (REP)" -process p4 -expect-text 1 1 " ++ " -process p4 -expect-text 2 2 " ++ " -process p4 -expect-text 17 17 " ++ " -process p4 -expect-text 18 18 "*++*" -process p4 -writehex "0d" -process p4 -expect-text 0 0 "Menu 11.7: Miscellaneous ISO-6429 (ECMA-48) Tests" -process p4 -writehex "30 0d" -process p4 -expect-text 0 0 "Menu 11: Non-VT100 Tests" -process p4 -writehex "30 0d" - -# 0. Exit -process p4 -writehex "30 0d" -process p4 -expect-text 12 30 "That's all, folks!" -process p4 -screen_dump - -process p4 -wait diff --git a/bin/varnishtest/tests/a00002.vtc b/bin/varnishtest/tests/a00002.vtc deleted file mode 100644 index 83c00a008..000000000 --- a/bin/varnishtest/tests/a00002.vtc +++ /dev/null @@ -1,49 +0,0 @@ -vtest "basic default HTTP transactions with expect and options" - -server s1 { - rxreq - expect req.method == PUT - expect req.proto == HTTP/1.0 - expect req.url == "/foo" - txresp -proto HTTP/1.2 -status 201 -reason Foo -} - -server s1 -start - -client c1 -connect ${s1_sock} { - txreq -method PUT -proto HTTP/1.0 -url /foo - rxresp - expect resp.proto == HTTP/1.2 - expect resp.status == 201 - expect resp.reason == Foo - expect resp.status -lt 202 - expect resp.status -le 201 - expect resp.status -eq 201 - expect resp.status -ge 201 - expect resp.status -gt 0xc8 -} - -client c1 -run - -server s1 -wait - -# The same tests with unix domain sockets -server s2 -listen "${tmpdir}/s2.sock" { - rxreq - expect req.method == PUT - expect req.proto == HTTP/1.0 - expect req.url == "/foo" - txresp -proto HTTP/1.2 -status 201 -reason Foo -} -start - -client c2 -connect ${s2_sock} { - txreq -req PUT -proto HTTP/1.0 -url /foo - rxresp - expect resp.proto == HTTP/1.2 - expect resp.status == 201 - expect resp.reason == Foo -} - -client c2 -run - -server s2 -wait diff --git a/bin/varnishtest/tests/a00003.vtc b/bin/varnishtest/tests/a00003.vtc deleted file mode 100644 index 794ce7640..000000000 --- a/bin/varnishtest/tests/a00003.vtc +++ /dev/null @@ -1,90 +0,0 @@ -vtest "dual independent HTTP transactions" - -server s1 { - rxreq - expect req.method == PUT - expect req.proto == HTTP/1.0 - expect req.url == "/foo" - txresp -proto HTTP/1.2 -status 201 -reason Foo -} - -server s2 { - rxreq - expect req.method == GET - expect req.proto == HTTP/1.1 - expect req.url == "/" - txresp -} - -server s1 -start -server s2 -start - -client c1 -connect ${s1_sock} { - txreq -req PUT -proto HTTP/1.0 -url /foo - rxresp - expect resp.proto == HTTP/1.2 - expect resp.status == 201 - expect resp.reason == Foo -} - -client c2 -connect ${s2_sock} { - txreq - rxresp - expect resp.proto == HTTP/1.1 - expect resp.status == 200 - expect resp.reason == OK -} - -client c1 -start -client c2 -start - -client c1 -wait -client c2 -wait - -server s1 -wait -server s2 -wait - -# The same tests with Unix domain sockets -server s3 -listen "${tmpdir}/s3.sock" { - rxreq - expect req.method == PUT - expect req.proto == HTTP/1.0 - expect req.url == "/foo" - txresp -proto HTTP/1.2 -status 201 -reason Foo -} - -server s4 -listen "${tmpdir}/s4.sock" { - rxreq - expect req.method == GET - expect req.proto == HTTP/1.1 - expect req.url == "/" - txresp -} - -server s3 -start -server s4 -start - -client c3 -connect ${s3_sock} { - txreq -req PUT -proto HTTP/1.0 -url /foo - rxresp - expect resp.proto == HTTP/1.2 - expect resp.status == 201 - expect resp.reason == Foo -} - -client c4 -connect ${s4_sock} { - txreq - rxresp - expect resp.proto == HTTP/1.1 - expect resp.status == 200 - expect resp.reason == OK -} - -client c3 -start -client c4 -start - -client c3 -wait -client c4 -wait - -server s3 -wait -server s4 -wait diff --git a/bin/varnishtest/tests/a00004.vtc b/bin/varnishtest/tests/a00004.vtc deleted file mode 100644 index d37288bc1..000000000 --- a/bin/varnishtest/tests/a00004.vtc +++ /dev/null @@ -1,70 +0,0 @@ -vtest "dual shared server HTTP transactions" - -server s1 -repeat 2 { - rxreq - expect req.method == PUT - expect req.proto == HTTP/1.0 - expect req.url == "/foo" - txresp -proto HTTP/1.2 -status 201 -reason Foo -} - -server s1 -start - -client c1 -connect ${s1_sock} { - txreq -req PUT -proto HTTP/1.0 -url /foo - rxresp - expect resp.proto == HTTP/1.2 - expect resp.status == 201 - expect resp.reason == Foo -} - -client c2 -connect ${s1_sock} { - txreq -req PUT -proto HTTP/1.0 -url /foo - rxresp - expect resp.proto == HTTP/1.2 - expect resp.status == 201 - expect resp.reason == Foo -} - -client c1 -start -client c2 -start - -client c1 -wait -client c2 -wait - -server s1 -wait - -# The same tests with Unix domain sockets -server s2 -repeat 2 -listen "${tmpdir}/s2.sock" { - rxreq - expect req.method == PUT - expect req.proto == HTTP/1.0 - expect req.url == "/foo" - txresp -proto HTTP/1.2 -status 201 -reason Foo -} - -server s2 -start - -client c3 -connect ${s2_sock} { - txreq -req PUT -proto HTTP/1.0 -url /foo - rxresp - expect resp.proto == HTTP/1.2 - expect resp.status == 201 - expect resp.reason == Foo -} - -client c4 -connect ${s2_sock} { - txreq -req PUT -proto HTTP/1.0 -url /foo - rxresp - expect resp.proto == HTTP/1.2 - expect resp.status == 201 - expect resp.reason == Foo -} - -client c3 -start -client c4 -start - -client c3 -wait -client c4 -wait - -server s2 -wait diff --git a/bin/varnishtest/tests/a00005.vtc b/bin/varnishtest/tests/a00005.vtc deleted file mode 100644 index ede67c529..000000000 --- a/bin/varnishtest/tests/a00005.vtc +++ /dev/null @@ -1,86 +0,0 @@ -vtest "dual shared client HTTP transactions" - -server s1 { - rxreq - expect req.method == PUT - expect req.proto == HTTP/1.0 - expect req.url == "/foo" - txresp -proto HTTP/1.2 -status 201 -reason Foo -} - -server s2 { - rxreq - expect req.method == GET - expect req.proto == HTTP/1.1 - expect req.url == "/" - txresp -} - -server s1 -start -server s2 -start - -client c1 -connect ${s1_sock} { - txreq -req PUT -proto HTTP/1.0 -url /foo - rxresp - expect resp.proto == HTTP/1.2 - expect resp.status == 201 - expect resp.reason == Foo -} - -client c1 -run - -client c1 -connect ${s2_sock} { - txreq - rxresp - expect resp.proto == HTTP/1.1 - expect resp.status == 200 - expect resp.reason == OK -} - -client c1 -run - -server s1 -wait -server s2 -wait - -# The same tests with unix domain sockets -server s3 -listen "${tmpdir}/s3.sock" { - rxreq - expect req.method == PUT - expect req.proto == HTTP/1.0 - expect req.url == "/foo" - txresp -proto HTTP/1.2 -status 201 -reason Foo -} - -server s4 -listen "${tmpdir}/s4.sock" { - rxreq - expect req.method == GET - expect req.proto == HTTP/1.1 - expect req.url == "/" - txresp -} - -server s3 -start -server s4 -start - -client c2 -connect ${s3_sock} { - txreq -req PUT -proto HTTP/1.0 -url /foo - rxresp - expect resp.proto == HTTP/1.2 - expect resp.status == 201 - expect resp.reason == Foo -} - -client c2 -run - -client c2 -connect ${s4_sock} { - txreq - rxresp - expect resp.proto == HTTP/1.1 - expect resp.status == 200 - expect resp.reason == OK -} - -client c2 -run - -server s3 -wait -server s4 -wait diff --git a/bin/varnishtest/tests/a00006.vtc b/bin/varnishtest/tests/a00006.vtc deleted file mode 100644 index 7f1448747..000000000 --- a/bin/varnishtest/tests/a00006.vtc +++ /dev/null @@ -1,50 +0,0 @@ -vtest "bidirectional message bodies" - -server s1 { - rxreq - expect req.method == PUT - expect req.proto == HTTP/1.0 - expect req.url == "/foo" - txresp -proto HTTP/1.2 -status 201 -reason Foo \ - -body "987654321\n" -} - -server s1 -start - -client c1 -connect ${s1_sock} { - txreq -req PUT -proto HTTP/1.0 -url /foo \ - -body "123456789\n" - rxresp - expect resp.proto == HTTP/1.2 - expect resp.status == 201 - expect resp.reason == Foo -} - -client c1 -run - -server s1 -wait - -# The same tests with Unix domain sockets -server s2 -listen "${tmpdir}/s2.sock" { - rxreq - expect req.method == PUT - expect req.proto == HTTP/1.0 - expect req.url == "/foo" - txresp -proto HTTP/1.2 -status 201 -reason Foo \ - -body "987654321\n" -} - -server s2 -start - -client c2 -connect ${s2_sock} { - txreq -req PUT -proto HTTP/1.0 -url /foo \ - -body "123456789\n" - rxresp - expect resp.proto == HTTP/1.2 - expect resp.status == 201 - expect resp.reason == Foo -} - -client c2 -run - -server s2 -wait diff --git a/bin/varnishtest/tests/a00007.vtc b/bin/varnishtest/tests/a00007.vtc deleted file mode 100644 index fb971dba0..000000000 --- a/bin/varnishtest/tests/a00007.vtc +++ /dev/null @@ -1,23 +0,0 @@ -vtest "TCP reuse" - -server s1 { - rxreq - expect req.url == "/1" - txresp -body "123456789\n" - rxreq - expect req.url == "/2" - txresp -body "987654321\n" -} - -server s1 -start - -client c1 -connect ${s1_sock} { - txreq -url "/1" -req "POST" -body "abcdefghi\n" - rxresp - txreq -url "/2" -req "POST" -body "ihgfedcba\n" - rxresp -} - -client c1 -run - -server s1 -wait diff --git a/bin/varnishtest/tests/a00008.vtc b/bin/varnishtest/tests/a00008.vtc deleted file mode 100644 index 9cf37d9f4..000000000 --- a/bin/varnishtest/tests/a00008.vtc +++ /dev/null @@ -1,62 +0,0 @@ -vtest "Barrier operations" - -# bs -> server, bc -> client, bb -> both -barrier bs cond 4 -barrier bc cond 4 -barrier bb cond 4 -cyclic - -server s1 { - rxreq - barrier bs sync - barrier bb sync - delay .9 - txresp -} -start - -server s2 { - rxreq - barrier bs sync - barrier bb sync - delay .6 - txresp -} -start - -server s3 { - rxreq - barrier bs sync - barrier bb sync - delay .2 - txresp -} -start - -client c1 -connect ${s1_sock} { - delay .2 - txreq - rxresp - barrier bc sync - barrier bb sync -} -start - -client c2 -connect ${s2_sock} { - delay .6 - txreq - rxresp - barrier bc sync - barrier bb sync -} -start - -client c3 -connect ${s3_sock} { - delay .9 - txreq - rxresp - barrier bc sync - barrier bb sync -} -start - -# Wait for all servers to have received requests -barrier bs sync -barrier bb sync - -# Wait for all clients to have received responses -barrier bc sync -barrier bb sync diff --git a/bin/varnishtest/tests/a00009.vtc b/bin/varnishtest/tests/a00009.vtc deleted file mode 100644 index 902869c41..000000000 --- a/bin/varnishtest/tests/a00009.vtc +++ /dev/null @@ -1,34 +0,0 @@ -vtest "VTC process: match text" - -process p1 { - echo 0123 - echo 4567 - echo 89AB - echo CDEF -} -run -screen-dump - -# y == 0, x == 0 -process p1 -match-text 0 0 "0123" -process p1 -match-text 0 0 "0.*3" -process p1 -match-text 0 0 "0123\(.|\n)*^CDEF" -process p1 -match-text 0 0 "0123\(.|\n)*^89AB\(.|\n)*F$" - -# y != 0, x == 0 -process p1 -match-text 1 0 "4567" -process p1 -match-text 2 0 "4567" -process p1 -match-text 2 0 "4567\(.|\n)*^9" -process p1 -match-text 3 0 "89AB" -process p1 -match-text 4 0 "C.*F" - -# y == 0, x != 0 -process p1 -match-text 0 1 "4567" -process p1 -match-text 0 2 "567" -process p1 -match-text 0 2 "123\(.|\n)*^5" -process p1 -match-text 0 2 "567\(.|\n)*^9" - -# y != 0, x != 0 -process p1 -match-text 1 1 "4567\(.|\n)*^89" -process p1 -match-text 2 2 "567\(.|\n)*^9" -process p1 -match-text 3 4 "B\(.|\n)*^F" -process p1 -match-text 4 3 "EF" - diff --git a/bin/varnishtest/tests/a00010.vtc b/bin/varnishtest/tests/a00010.vtc deleted file mode 100644 index d29628ec7..000000000 --- a/bin/varnishtest/tests/a00010.vtc +++ /dev/null @@ -1,20 +0,0 @@ -vtest "simply test that the framework support \0" - -server s1 { - rxreq - expect req.url == "/" - txresp -body {a\0bc} -} - -server s1 -start - -client c1 -connect ${s1_sock} { - - txreq - rxresp - expect resp.bodylen == 4 -} - -client c1 -run - -server s1 -wait diff --git a/bin/varnishtest/tests/a00011.vtc b/bin/varnishtest/tests/a00011.vtc deleted file mode 100644 index 9819e4934..000000000 --- a/bin/varnishtest/tests/a00011.vtc +++ /dev/null @@ -1,21 +0,0 @@ -vtest "test vtc gzip support" - -server s1 { - rxreq - expect req.http.content-length == "26" - expect req.bodylen == "26" - gunzip - expect req.bodylen == "3" - expect req.http.content-encoding == "gzip" - txresp -gzipbody FOO -gziplevel 9 -} -start - -client c1 -connect ${s1_sock} { - txreq -gzipbody FOO - rxresp - expect resp.http.content-length == "26" - expect resp.bodylen == "26" - gunzip - expect resp.bodylen == "3" - expect resp.http.content-encoding == "gzip" -} -run diff --git a/bin/varnishtest/tests/a00012.vtc b/bin/varnishtest/tests/a00012.vtc deleted file mode 100644 index 66b7f1f0b..000000000 --- a/bin/varnishtest/tests/a00012.vtc +++ /dev/null @@ -1,13 +0,0 @@ -vtest "Ensure that we can test non-existence of headers (#1062)" - -server s1 { - rxreq - txresp -} -start - -client c1 -connect ${s1_sock} { - txreq - rxresp - expect resp.http.X-Test == -} -run - diff --git a/bin/varnishtest/tests/a00013.vtc b/bin/varnishtest/tests/a00013.vtc deleted file mode 100644 index d95ee8725..000000000 --- a/bin/varnishtest/tests/a00013.vtc +++ /dev/null @@ -1,64 +0,0 @@ -vtest "Barrier operations" - -# same as a00008.vtc, with socket barriers instead - -# bs -> server, bc -> client, bb -> both -barrier bs sock 4 -barrier bc sock 4 -barrier bb sock 4 -cyclic - -server s1 { - rxreq - barrier bs sync - barrier bb sync - delay .9 - txresp -} -start - -server s2 { - rxreq - barrier bs sync - barrier bb sync - delay .6 - txresp -} -start - -server s3 { - rxreq - barrier bs sync - barrier bb sync - delay .2 - txresp -} -start - -client c1 -connect ${s1_sock} { - delay .2 - txreq - rxresp - barrier bc sync - barrier bb sync -} -start - -client c2 -connect ${s2_sock} { - delay .6 - txreq - rxresp - barrier bc sync - barrier bb sync -} -start - -client c3 -connect ${s3_sock} { - delay .9 - txreq - rxresp - barrier bc sync - barrier bb sync -} -start - -# Wait for all servers to have received requests -barrier bs sync -barrier bb sync - -# Wait for all clients to have received responses -barrier bc sync -barrier bb sync diff --git a/bin/varnishtest/tests/a00014.vtc b/bin/varnishtest/tests/a00014.vtc deleted file mode 100644 index 0207a0fae..000000000 --- a/bin/varnishtest/tests/a00014.vtc +++ /dev/null @@ -1,6 +0,0 @@ -vtest "Custom feature verification" - -feature cmd true -feature cmd false - -this is an invalid vtest command diff --git a/bin/varnishtest/tests/a00015.vtc b/bin/varnishtest/tests/a00015.vtc deleted file mode 100644 index f3fcfdc5a..000000000 --- a/bin/varnishtest/tests/a00015.vtc +++ /dev/null @@ -1,25 +0,0 @@ -vtest "Write a body to a file" - -server s1 { - # First, HTTP checks - rxreq - expect req.http.Content-Type == "text/plain" - - # Then, payload checks - write_body req.txt - shell {grep -q request req.txt} - - txresp -hdr "Content-Type: text/plain" -body response -} -start - -client c1 -connect ${s1_sock} { - txreq -req POST -hdr "Content-Type: text/plain" -body request - - # First, HTTP checks - rxresp - expect resp.http.Content-Type == "text/plain" - - # Then, payload checks - write_body resp.txt - shell {grep -q response resp.txt} -} -run diff --git a/bin/varnishtest/tests/a00016.vtc b/bin/varnishtest/tests/a00016.vtc deleted file mode 100644 index 2e4f90417..000000000 --- a/bin/varnishtest/tests/a00016.vtc +++ /dev/null @@ -1,36 +0,0 @@ -vtest "Shutdown" - -barrier b1 cond 2 - -server s1 { - rxreq - shutdown -read - txresp -} -start - -server s2 { - rxreq - txresp - shutdown - barrier b1 sync -} -start - -client c1 -connect "${s1_sock}" { - txreq - shutdown -write - rxresp - expect resp.status == 200 -} -run - -client c2 -connect "${s2_sock}" { - txreq - rxresp - expect resp.status == 200 - - txreq - non_fatal - rxresp - fatal - expect resp.status == - barrier b1 sync -} -run diff --git a/bin/varnishtest/tests/a00018.vtc b/bin/varnishtest/tests/a00018.vtc deleted file mode 100644 index 45d1675e9..000000000 --- a/bin/varnishtest/tests/a00018.vtc +++ /dev/null @@ -1,16 +0,0 @@ -vtest "feature ignore_unknown_macro" - -feature ignore_unknown_macro - -server s1 { - rxreq - expect req.http.Foo == "${foo}" - txresp -hdr "Bar: ${bar}" -} -start - -client c1 -connect ${s1_sock} { - txreq -hdr "Foo: ${foo}" - rxresp - expect resp.status == 200 - expect resp.http.Bar == "${bar}" -} -run diff --git a/bin/varnishtest/tests/a00019.vtc b/bin/varnishtest/tests/a00019.vtc deleted file mode 100644 index 727c33ca9..000000000 --- a/bin/varnishtest/tests/a00019.vtc +++ /dev/null @@ -1,64 +0,0 @@ -vtest "Check rxrespbody -max" - -server s1 { - rxreq - txresp -bodylen 65536 - rxreq - txresp -} -start - -server s2 { - rxreq - txresp -nolen -hdr "Transfer-Encoding: chunked" - chunkedlen 8192 - chunkedlen 4096 - chunkedlen 4096 - chunkedlen 16384 - chunkedlen 16384 - chunkedlen 16384 - chunkedlen 0 - rxreq - txresp -} -start - -server s3 { - rxreq - txresp -nolen -bodylen 65536 -} -start - -client c1 -connect ${s1_sock} { - txreq - rxresphdrs - rxrespbody -max 8192 - expect resp.bodylen == 8192 - rxrespbody -max 8192 - expect resp.bodylen == 16384 - rxrespbody - expect resp.bodylen == 65536 - txreq - rxresp -} -run - -client c2 -connect ${s2_sock} { - txreq - rxresphdrs - rxrespbody -max 8192 - expect resp.bodylen == 8192 - rxrespbody -max 8192 - expect resp.bodylen == 16384 - rxrespbody - expect resp.bodylen == 65536 - txreq - rxresp -} -run - -client c3 -connect ${s3_sock} { - txreq - rxresphdrs - rxrespbody -max 8192 - expect resp.bodylen == 8192 - rxrespbody -max 8192 - expect resp.bodylen == 16384 - rxrespbody - expect resp.bodylen == 65536 -} -run diff --git a/bin/varnishtest/tests/a00020.vtc b/bin/varnishtest/tests/a00020.vtc deleted file mode 100644 index 73d21dadd..000000000 --- a/bin/varnishtest/tests/a00020.vtc +++ /dev/null @@ -1,14 +0,0 @@ -vtest "Test -bodyfrom" - -server s1 { - rxreq - expect req.bodylen == 235 - txresp -bodyfrom ${testdir}/a00020.vtc -} -start - -client c1 -connect ${s1_sock} { - txreq -bodyfrom ${testdir}/a00020.vtc - rxresp - expect resp.bodylen == 235 -} -run - diff --git a/bin/varnishtest/tests/a00021.vtc b/bin/varnishtest/tests/a00021.vtc deleted file mode 100644 index e57f89d35..000000000 --- a/bin/varnishtest/tests/a00021.vtc +++ /dev/null @@ -1,46 +0,0 @@ -vtest "tunnel basic coverage" - -barrier b1 cond 2 -barrier b2 cond 2 -barrier b3 cond 2 - -server s1 { - rxreq - barrier b2 sync - barrier b3 sync - txresp -} -start - -tunnel t1 -connect "${s1_sock}" { - pause - barrier b1 sync - send 10 - resume - barrier b2 sync - pause - barrier b3 sync - recv 10 - resume -} -start - -client c1 -connect "${t1_sock}" { - barrier b1 sync - txreq - rxresp -} -run - -tunnel t1 -wait - -server s2 { - rxreq - txresp -} -start - -tunnel t2 -connect "${s2_sock}" { - resume -} -start+pause - -client c2 -connect "${t2_sock}" { - txreq - rxresp -} -run diff --git a/bin/varnishtest/tests/a00022.vtc b/bin/varnishtest/tests/a00022.vtc deleted file mode 100644 index adf522935..000000000 --- a/bin/varnishtest/tests/a00022.vtc +++ /dev/null @@ -1,10 +0,0 @@ -vtest "Test setenv" - -setenv FOO "BAR BAZ" -shell -expect "BAR BAZ" {echo $FOO} - -setenv -ifunset FOO QUUX -shell -expect "BAR BAZ" {echo $FOO} - -setenv FOO QUUX -shell -expect QUUX {echo $FOO} diff --git a/bin/varnishtest/tests/a00023.vtc b/bin/varnishtest/tests/a00023.vtc deleted file mode 100644 index a8b8c245f..000000000 --- a/bin/varnishtest/tests/a00023.vtc +++ /dev/null @@ -1,29 +0,0 @@ -vtest "Run server -dispatch more than once" - -feature ignore_unknown_macro - -shell { - cat <<-'EOF' >_.vtc - vtest "Run server -dispatch more than once (nested)" - - server s0 { - rxreq - txresp - } -dispatch - - client c1 -connect ${s0_sock} { - txreq - rxresp - } -run - - server s0 -break - server s0 -dispatch - - client c1 -run - EOF - - vtest -v _.vtc >_.log -} - -shell -match "s1 +rxhdr" { cat _.log } -shell -match "s2 +rxhdr" { cat _.log } diff --git a/bin/varnishtest/tests/a00024.vtc b/bin/varnishtest/tests/a00024.vtc deleted file mode 100644 index efab48af0..000000000 --- a/bin/varnishtest/tests/a00024.vtc +++ /dev/null @@ -1,59 +0,0 @@ -vtest "test -nouseragent and -noserver" - -server s1 { - rxreq - # by default, User-Agent header is set to cNAME - expect req.http.User-Agent == "c101" - txresp - rxreq - # when specified with -hdr, it overrides the default - expect req.http.User-Agent == "not-c101" - txresp -hdr "Server: not-s1" -} -start - -server s2 { - rxreq - expect req.http.User-Agent == "c202" - txresp - rxreq - # default User-Agent header is not included when -nouseragent is specified - expect req.http.User-Agent == - txresp -noserver -} -start - - -varnish v1 -vcl+backend { - sub vcl_recv { - if (req.url == "/home") { - set req.backend_hint = s1; - } else { - set req.backend_hint = s2; - } - } - - sub vcl_backend_response { - set Beresp.uncacheable = true; - } -} -start - - -client c101 { - txreq -url "/home" - rxresp - # by default, Server header is set to sNAME - expect resp.http.Server == "s1" - txreq -url "/home" -hdr "User-Agent: not-c101" - rxresp - # when specified with -hdr, it overrides the default - expect resp.http.Server == "not-s1" -} -run - -client c202 { - txreq - rxresp - expect resp.http.Server == "s2" - txreq -nouseragent - rxresp - # default Server header is not included when -noserver is specified - expect resp.http.Server == -} -run diff --git a/bin/varnishtest/tests/a00025.vtc b/bin/varnishtest/tests/a00025.vtc deleted file mode 100644 index f2e105ed0..000000000 --- a/bin/varnishtest/tests/a00025.vtc +++ /dev/null @@ -1,26 +0,0 @@ -vtest "Basic VSM status check" - -# just launch varnishd -varnish v1 -cliok ping -vsm m1 -expect-status mgt-running - -varnish v1 -vcl "backend be none;" -vsm m1 -expect-status mgt-running - -varnish v1 -start -vsm m1 -expect-status \ - mgt-running,mgt-changed,wrk-running,wrk-changed,wrk-restarted - -varnish v1 -vcl "backend be none;" -vsm m1 -expect-status mgt-running,wrk-running,wrk-changed - -# The order of status flags does not matter, the next one is equivalent. -varnish v1 -vcl "backend be none;" -vsm m1 -expect-status wrk-changed,wrk-running,mgt-running - -varnish v1 -stop -vsm m1 -expect-status mgt-running,mgt-changed - -varnish v1 -start -vsm m1 -expect-status \ - mgt-running,mgt-changed,wrk-running,wrk-changed,wrk-restarted diff --git a/bin/varnishtest/tests/a00026.vtc b/bin/varnishtest/tests/a00026.vtc deleted file mode 100644 index bb0b88cd5..000000000 --- a/bin/varnishtest/tests/a00026.vtc +++ /dev/null @@ -1,28 +0,0 @@ -varnishtest "Test VTC includes" - -shell { - cat >${tmpdir}/f1 <<-EOF - rxreq - EOF -} - -shell { - cat >${tmpdir}/f2 <<-EOF - txresp - EOF -} - -shell { - cat >${tmpdir}/f3 <<-EOF - server s1 { - include "${tmpdir}/f1" "${tmpdir}/f2" - } -start - EOF -} - -include "${tmpdir}/f3" - -client c1 -connect "${s1_sock}" { - txreq - rxresp -} -run diff --git a/bin/varnishtest/tests/a02000.vtc b/bin/varnishtest/tests/a02000.vtc deleted file mode 100644 index faed96df3..000000000 --- a/bin/varnishtest/tests/a02000.vtc +++ /dev/null @@ -1,50 +0,0 @@ -vtest "Close/accept after H/2 upgrade" - -server s1 { - stream 1 { - rxreq - txresp - } -run - - expect_close - accept - - rxreq - txresp - - close - accept - - rxreq - txresp - - stream 1 { - rxreq - txresp - } -run -} -start - -client c1 -connect ${s1_sock} { - stream 1 { - txreq - rxresp - } -run -} -run - -client c1 -connect ${s1_sock} { - txreq - rxresp - -} -run - -client c1 -connect ${s1_sock} { - txreq - rxresp - - stream 1 { - txreq - rxresp - } -run -} -run - -server s1 -wait diff --git a/bin/varnishtest/tests/a02001.vtc b/bin/varnishtest/tests/a02001.vtc deleted file mode 100644 index 1cab65db5..000000000 --- a/bin/varnishtest/tests/a02001.vtc +++ /dev/null @@ -1,98 +0,0 @@ -vtest "Quickly test all frames" - -server s1 { - rxpri - stream 0 { -# PRIO - txprio -stream 23456 -weight 123 - -# RST - txrst -err 2 - -# SETTINGS - txsettings -push true -hdrtbl 11111111 -maxstreams 222222 -winsize 333333 -framesize 444444 -hdrsize 555555 - txsettings -ack - -# PING - txping -data "01234567" - txping -data "abcdefgh" -ack - -# GOAWAY - txgoaway -laststream 17432423 -err 12 -debug "kangaroo" - -# WINUP - txwinup -size 500 - - -# FRAME - txresp -body "floubidou" - - -# FRAME - txresp -body "tata" - } -run -} -start - -client c1 -connect ${s1_sock} { - txpri - stream 0 { -# PRIO - rxprio - expect prio.stream == 23456 - expect prio.weight == 123 - -# RST - rxrst - expect rst.err >= 2 - expect rst.err < 3 - -# SETTINGS - rxsettings - expect settings.hdrtbl == 11111111 - expect settings.maxstreams == 222222 - expect settings.winsize == 333333 - expect settings.framesize == 444444 - expect settings.hdrsize == 555555 - - rxsettings - expect settings.ack == true - expect settings.hdrtbl == - expect settings.maxstreams == - expect settings.winsize == - expect settings.framesize == - expect settings.hdrsize == - -# PING - rxping - expect ping.ack == "false" - expect ping.data == "01234567" - expect ping.data != "O1234567" - rxping - expect ping.ack == "true" - expect ping.data == "abcdefgh" - expect ping.data != "abcdefgt" - -# GOAWAY - rxgoaway - expect goaway.err == 12 - expect goaway.laststream == 17432423 - expect goaway.debug == "kangaroo" - -# WINUP - rxwinup - expect winup.size == 500 - -# FRAME - rxhdrs - rxdata - expect frame.data == "floubidou" - expect frame.type == 0 - expect frame.size == 9 - expect frame.stream == 0 - - rxresp - expect resp.body == "floubidoutata" - } -run -} -run - -server s1 -wait diff --git a/bin/varnishtest/tests/a02002.vtc b/bin/varnishtest/tests/a02002.vtc deleted file mode 100644 index 14125d55b..000000000 --- a/bin/varnishtest/tests/a02002.vtc +++ /dev/null @@ -1,22 +0,0 @@ -vtest "Trigger a compression error via bad index" - -server s1 { - non_fatal - stream 1 { - rxreq - expect req.http.foo == - txgoaway -laststream 0 -err 9 -debug "compression_error" - } -run -} -start - -client c1 -connect ${s1_sock} { - stream 1 { - txreq -idxHdr 100 -litHdr inc plain "foo" plain "bar" - rxgoaway - expect goaway.err == 9 - expect goaway.laststream == 0 - expect goaway.debug == "compression_error" - } -run -} -run - -server s1 -wait diff --git a/bin/varnishtest/tests/a02003.vtc b/bin/varnishtest/tests/a02003.vtc deleted file mode 100644 index abda36db9..000000000 --- a/bin/varnishtest/tests/a02003.vtc +++ /dev/null @@ -1,19 +0,0 @@ -vtest "Check bodylen" - -server s1 { - stream 1 { - rxreq - expect req.bodylen == 3 - txresp -bodylen 7 - } -run -} -start - -client c1 -connect ${s1_sock} { - stream 1 { - txreq -bodylen 3 - rxresp - expect resp.bodylen == 7 - } -run -} -run - -server s1 -wait diff --git a/bin/varnishtest/tests/a02004.vtc b/bin/varnishtest/tests/a02004.vtc deleted file mode 100644 index 90e22d55f..000000000 --- a/bin/varnishtest/tests/a02004.vtc +++ /dev/null @@ -1,18 +0,0 @@ -vtest "Simple request with body" - -server s1 { - stream 1 { - rxreq - txresp -body "bob" - } -run -} -start - -client c1 -connect ${s1_sock} { - stream 1 { - txreq - rxresp - expect resp.bodylen == 3 - } -run -} -run - -server s1 -wait diff --git a/bin/varnishtest/tests/a02005.vtc b/bin/varnishtest/tests/a02005.vtc deleted file mode 100644 index e596f8381..000000000 --- a/bin/varnishtest/tests/a02005.vtc +++ /dev/null @@ -1,36 +0,0 @@ -vtest "Continuation frames" - -server s1 { - stream 1 { - rxreq - txresp -nohdrend - txcont -nohdrend -hdr "foo" "bar" - txcont -hdr "baz" "qux" - } -run - stream 3 { - rxreq - txresp -nohdrend - txcont -nohdrend -hdr "foo2" "bar2" - txcont -hdr "baz2" "qux2" - } -run -} -start - -client c1 -connect ${s1_sock} { - stream 1 { - txreq - rxhdrs -all - expect resp.http.foo == "bar" - expect resp.http.baz == "qux" - } -run - stream 3 { - txreq - rxhdrs -some 2 - expect resp.http.foo2 == - expect resp.http.baz2 == - rxcont - expect resp.http.foo2 == "bar2" - expect resp.http.baz2 == "qux2" - } -run -} -run - -server s1 -wait diff --git a/bin/varnishtest/tests/a02006.vtc b/bin/varnishtest/tests/a02006.vtc deleted file mode 100644 index 9e18ef9d0..000000000 --- a/bin/varnishtest/tests/a02006.vtc +++ /dev/null @@ -1,61 +0,0 @@ -vtest "Keep track of window credits" - -server s1 { - stream 2 { - rxreq - txresp -nohdrend - txcont -nohdrend -hdr "foo" "bar" - txcont -hdr "baz" "qux" - txdata -data "foo" - txdata -data "bar" - expect stream.peer_window == 65529 - rxreq - txresp -bodylen 529 - expect stream.peer_window == 65000 - rxwinup - expect stream.peer_window == 65200 - } -run - stream 0 { - expect stream.peer_window == 65000 - rxwinup - expect stream.peer_window == 66000 - } -run - - -} -start - -client c1 -connect ${s1_sock} { - stream 0 { - expect stream.window == 65535 - } -run - stream 2 { - expect stream.window == 65535 - txreq - rxhdrs - rxcont - rxcont - expect resp.http.:status == "200" - expect resp.http.foo == "bar" - expect stream.window == 65535 - rxdata - expect stream.window == 65532 - rxdata - expect stream.window == 65529 - expect resp.body == "foobar" - expect resp.http.baz == "qux" - } -run - stream 0 { - expect stream.window == 65529 - } -run - stream 2 { - txreq - rxresp - txwinup -size 200 - } -run - stream 0 { - txwinup -size 1000 - } -run - -} -run - -server s1 -wait diff --git a/bin/varnishtest/tests/a02007.vtc b/bin/varnishtest/tests/a02007.vtc deleted file mode 100644 index 5cd3e72aa..000000000 --- a/bin/varnishtest/tests/a02007.vtc +++ /dev/null @@ -1,79 +0,0 @@ -vtest "HPACK test" - -server s1 { - stream 1 { - rxreq - expect tbl.dec.size == 57 - expect tbl.dec[1].key == ":authority" - expect tbl.dec[1].value == "www.example.com" - txresp - } -run - - stream 3 { - rxreq - expect tbl.dec[1].key == "cache-control" - expect tbl.dec[1].value == "no-cache" - expect tbl.dec[2].key == ":authority" - expect tbl.dec[2].value == "www.example.com" - expect tbl.dec.size == 110 - txresp - } -run - - stream 5 { - rxreq - expect tbl.dec[1].key == "custom-key" - expect tbl.dec[1].value == "custom-value" - expect tbl.dec[2].key == "cache-control" - expect tbl.dec[2].value == "no-cache" - expect tbl.dec[3].key == ":authority" - expect tbl.dec[3].value == "www.example.com" - expect tbl.dec.size == 164 - txresp - } -run -} -start - -client c1 -connect ${s1_sock} { - stream 1 { - - txreq -idxHdr 2 \ - -idxHdr 6 \ - -idxHdr 4 \ - -litIdxHdr inc 1 huf "www.example.com" - expect tbl.enc[1].key == ":authority" - expect tbl.enc[1].value == "www.example.com" - rxresp - } -run - - stream 3 { - txreq -idxHdr 2 \ - -idxHdr 6 \ - -idxHdr 4 \ - -idxHdr 62 \ - -litIdxHdr inc 24 huf no-cache - expect tbl.enc[1].key == "cache-control" - expect tbl.enc[1].value == "no-cache" - expect tbl.enc[2].key == ":authority" - expect tbl.enc[2].value == "www.example.com" - expect tbl.enc.size == 110 - rxresp - } -run - - stream 5 { - txreq -idxHdr 2 \ - -idxHdr 7 \ - -idxHdr 5 \ - -idxHdr 63 \ - -litHdr inc huf "custom-key" huf "custom-value" - expect tbl.enc[1].key == "custom-key" - expect tbl.enc[1].value == "custom-value" - expect tbl.enc[2].key == "cache-control" - expect tbl.enc[2].value == "no-cache" - expect tbl.enc[3].key == ":authority" - expect tbl.enc[3].value == "www.example.com" - expect tbl.enc.size == 164 - rxresp - } -run - -} -run - -server s1 -wait diff --git a/bin/varnishtest/tests/a02008.vtc b/bin/varnishtest/tests/a02008.vtc deleted file mode 100644 index 0e7143144..000000000 --- a/bin/varnishtest/tests/a02008.vtc +++ /dev/null @@ -1,118 +0,0 @@ -vtest "HPACK test" - -server s1 { - stream 0 { - txsettings -hdrtbl 256 - rxsettings - txsettings -ack - rxsettings - expect settings.ack == true - } -run - - stream 1 { - rxreq - expect tbl.dec[1].key == "location" - expect tbl.dec[1].value == "https://www.example.com" - expect tbl.dec[2].key == "date" - expect tbl.dec[2].value == "Mon, 21 Oct 2013 20:13:21 GMT" - expect tbl.dec[3].key == "cache-control" - expect tbl.dec[3].value == "private" - expect tbl.dec[4].key == ":status" - expect tbl.dec[4].value == "302" - expect tbl.dec.size == 222 - txresp - } -run - - stream 3 { - rxreq - expect tbl.dec[1].key == ":status" - expect tbl.dec[1].value == "307" - expect tbl.dec[2].key == "location" - expect tbl.dec[2].value == "https://www.example.com" - expect tbl.dec[3].key == "date" - expect tbl.dec[3].value == "Mon, 21 Oct 2013 20:13:21 GMT" - expect tbl.dec[4].key == "cache-control" - expect tbl.dec[4].value == "private" - expect tbl.dec.size == 222 - txresp - } -run - - stream 5 { - rxreq - expect tbl.dec[1].key == "set-cookie" - expect tbl.dec[1].value == "foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU; max-age=3600; version=1" - expect tbl.dec[2].key == "content-encoding" - expect tbl.dec[2].value == "gzip" - expect tbl.dec[3].key == "date" - expect tbl.dec[3].value == "Mon, 21 Oct 2013 20:13:22 GMT" - expect tbl.dec.size == 215 - txresp - } -run -} -start - -client c1 -connect ${s1_sock} { - stream 0 { - txsettings -hdrtbl 256 - rxsettings - txsettings -ack - rxsettings - expect settings.ack == true - } -run - - stream 1 { - - txreq \ - -litIdxHdr inc 8 plain "302" \ - -litIdxHdr inc 24 plain "private" \ - -litIdxHdr inc 33 plain "Mon, 21 Oct 2013 20:13:21 GMT" \ - -litIdxHdr inc 46 plain "https://www.example.com" - expect tbl.enc[1].key == "location" - expect tbl.enc[1].value == "https://www.example.com" - expect tbl.enc[2].key == "date" - expect tbl.enc[2].value == "Mon, 21 Oct 2013 20:13:21 GMT" - expect tbl.enc[3].key == "cache-control" - expect tbl.enc[3].value == "private" - expect tbl.enc[4].key == ":status" - expect tbl.enc[4].value == "302" - expect tbl.enc.size == 222 - rxresp - } -run - - stream 3 { - txreq \ - -litIdxHdr inc 8 huf "307" \ - -idxHdr 65 \ - -idxHdr 64 \ - -idxHdr 63 - expect tbl.enc[1].key == ":status" - expect tbl.enc[1].value == "307" - expect tbl.enc[2].key == "location" - expect tbl.enc[2].value == "https://www.example.com" - expect tbl.enc[3].key == "date" - expect tbl.enc[3].value == "Mon, 21 Oct 2013 20:13:21 GMT" - expect tbl.enc[4].key == "cache-control" - expect tbl.enc[4].value == "private" - expect tbl.enc.size == 222 - rxresp - } -run - - stream 5 { - txreq -idxHdr 8 \ - -idxHdr 65 \ - -litIdxHdr inc 33 plain "Mon, 21 Oct 2013 20:13:22 GMT" \ - -idxHdr 64 \ - -litIdxHdr inc 26 plain "gzip" \ - -litIdxHdr inc 55 plain "foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU; max-age=3600; version=1" - expect tbl.enc[1].key == "set-cookie" - expect tbl.enc[1].value == "foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU; max-age=3600; version=1" - expect tbl.enc[2].key == "content-encoding" - expect tbl.enc[2].value == "gzip" - expect tbl.enc[3].key == "date" - expect tbl.enc[3].value == "Mon, 21 Oct 2013 20:13:22 GMT" - expect tbl.enc.size == 215 - rxresp - } -run - -} -run - -server s1 -wait diff --git a/bin/varnishtest/tests/a02009.vtc b/bin/varnishtest/tests/a02009.vtc deleted file mode 100644 index 37de4e6d7..000000000 --- a/bin/varnishtest/tests/a02009.vtc +++ /dev/null @@ -1,78 +0,0 @@ -vtest "More HPACK tests" -server s1 { - stream 1 { - rxreq - expect tbl.dec.size == 57 - expect tbl.dec[1].key == ":authority" - expect tbl.dec[1].value == "www.example.com" - txresp - } -run - - stream 3 { - rxreq - expect tbl.dec[1].key == "cache-control" - expect tbl.dec[1].value == "no-cache" - expect tbl.dec[2].key == ":authority" - expect tbl.dec[2].value == "www.example.com" - expect tbl.dec.size == 110 - txresp - } -run - - stream 5 { - rxreq - expect tbl.dec[1].key == "custom-key" - expect tbl.dec[1].value == "custom-value" - expect tbl.dec[2].key == "cache-control" - expect tbl.dec[2].value == "no-cache" - expect tbl.dec[3].key == ":authority" - expect tbl.dec[3].value == "www.example.com" - expect tbl.dec.size == 164 - txresp - } -run -} -start - -client c1 -connect ${s1_sock} { - stream 1 { - - txreq -idxHdr 2 \ - -idxHdr 6 \ - -idxHdr 4 \ - -litIdxHdr inc 1 huf "www.example.com" - expect tbl.enc[1].key == ":authority" - expect tbl.enc[1].value == "www.example.com" - rxresp - } -run - - stream 3 { - txreq -idxHdr 2 \ - -idxHdr 6 \ - -idxHdr 4 \ - -idxHdr 62 \ - -litIdxHdr inc 24 huf no-cache - expect tbl.enc[1].key == "cache-control" - expect tbl.enc[1].value == "no-cache" - expect tbl.enc[2].key == ":authority" - expect tbl.enc[2].value == "www.example.com" - expect tbl.enc.size == 110 - rxresp - } -run - - stream 5 { - txreq -idxHdr 2 \ - -idxHdr 7 \ - -idxHdr 5 \ - -idxHdr 63 \ - -litHdr inc huf "custom-key" huf "custom-value" - expect tbl.enc[1].key == "custom-key" - expect tbl.enc[1].value == "custom-value" - expect tbl.enc[2].key == "cache-control" - expect tbl.enc[2].value == "no-cache" - expect tbl.enc[3].key == ":authority" - expect tbl.enc[3].value == "www.example.com" - expect tbl.enc.size == 164 - rxresp - } -run - -} -run - -server s1 -wait diff --git a/bin/varnishtest/tests/a02010.vtc b/bin/varnishtest/tests/a02010.vtc deleted file mode 100644 index 151f64e24..000000000 --- a/bin/varnishtest/tests/a02010.vtc +++ /dev/null @@ -1,46 +0,0 @@ -vtest "Verify the initial window size" - -server s1 { - stream 0 { - expect stream.peer_window == 65535 - rxsettings - txsettings -ack - } -run - stream 1 { - expect stream.peer_window == 128 - rxreq - txresp -bodylen 100 - expect stream.peer_window == 28 - } -run - stream 0 { - rxsettings - txsettings -ack - } -run - stream 1 { - expect stream.peer_window == -36 - } -run -} -start - -client c1 -connect ${s1_sock} { - stream 0 { - txsettings -winsize 128 - rxsettings - } -run - stream 1 { - txreq - rxresp - expect resp.bodylen == 100 - expect stream.window == 28 - } -run - stream 0 { - txsettings -winsize 64 - rxsettings - - expect stream.window == 65435 - } -run - stream 1 { - expect stream.window == -36 - } -run -} -run - -server s1 -wait diff --git a/bin/varnishtest/tests/a02011.vtc b/bin/varnishtest/tests/a02011.vtc deleted file mode 100644 index d0123c66d..000000000 --- a/bin/varnishtest/tests/a02011.vtc +++ /dev/null @@ -1,24 +0,0 @@ -vtest "overflow" - -server s1 { - stream 1 { - rxreq - txresp -hdr long-header-original1 original1 \ - -hdr long-header-original2 original2 \ - -hdr long-header-original3 original3 \ - -hdr long-header-original4 original4 - } -run -} -start - -client c1 -connect ${s1_sock} { - stream 1 { - txreq -req GET \ - -url / \ - -hdr :scheme http \ - -hdr :authority localhost - rxresp - expect resp.http.:status == 200 - } -run -} -run - -server s1 -wait diff --git a/bin/varnishtest/tests/a02012.vtc b/bin/varnishtest/tests/a02012.vtc deleted file mode 100644 index b560cd4b3..000000000 --- a/bin/varnishtest/tests/a02012.vtc +++ /dev/null @@ -1,58 +0,0 @@ -vtest "padded DATA frames" - -server s1 { - stream 1 { - rxreq - # HDR indexed ":status: 200" + 2 padding bytes - sendhex "00 00 04 01 0c 00 00 00 01 02 88 12 34" - # DATA "foo" + 4 padding bytes - sendhex "00 00 08 00 09 00 00 00 01 04 66 6f 6f 6e 6e 6e 6e" - - } -run - - stream 3 { - rxreq - txresp -nostrend - txdata -data "bull" -pad "frog" -nostrend - txdata -data "terrier" -padlen 17 - txdata -datalen 4 -padlen 2 - } -run - - stream 5 { - rxreq - txresp -pad "pwepew" - } -run -} -start - -client c1 -connect ${s1_sock} { - stream 1 { - txreq - rxresp - expect resp.bodylen == 3 - expect resp.body == "foo" - } -run - - stream 3 { - txreq - rxhdrs - - rxdata - expect frame.size == 9 - expect resp.body == "bull" - - rxdata - expect frame.size == 25 - expect resp.body == "bullterrier" - - rxdata - expect frame.size == 7 - } -run - - stream 5 { - txreq - rxresp - expect frame.padding == 6 - } -run -} -run - -server s1 -wait diff --git a/bin/varnishtest/tests/a02013.vtc b/bin/varnishtest/tests/a02013.vtc deleted file mode 100644 index 48edfb69f..000000000 --- a/bin/varnishtest/tests/a02013.vtc +++ /dev/null @@ -1,15 +0,0 @@ -vtest "H/2 state after sending/receiving preface" - -server s1 { - expect h2.state == false - rxpri - expect h2.state == true -} -start - -client c1 -connect ${s1_sock} { - expect h2.state == false - txpri - expect h2.state == true -} -start - -server s1 -wait diff --git a/bin/varnishtest/tests/a02014.vtc b/bin/varnishtest/tests/a02014.vtc deleted file mode 100644 index c58e158db..000000000 --- a/bin/varnishtest/tests/a02014.vtc +++ /dev/null @@ -1,54 +0,0 @@ -vtest "priority" - -server s1 { - stream 1 { - rxreq - txresp - expect stream.weight == 16 - expect stream.dependency == 0 - } -run - stream 3 { - rxreq - txresp - expect stream.weight == 123 - expect stream.dependency == 5 - - rxprio - expect prio.weight == 10 - expect prio.stream == 7 - expect stream.weight == 10 - expect stream.dependency == 7 - } -run -} -start - -client c1 -connect ${s1_sock} { - stream 1 { - txreq -method GET -url /1 \ - -hdr :scheme http -hdr :authority localhost - rxresp - expect stream.weight == 16 - expect stream.dependency == 0 - } -run - stream 3 { - txreq -req GET -url /3 \ - -hdr :scheme http -hdr :authority localhost \ - -weight 123 -ex -dep 5 - rxresp - expect stream.weight == 123 - expect stream.dependency == 5 - - txprio -weight 10 -stream 7 - expect stream.weight == 10 - expect stream.dependency == 7 - } -run - stream 5 { - expect stream.weight == 16 - expect stream.dependency == 0 - } -run - stream 0 { - expect stream.weight == - expect stream.dependency == - } -run -} -run - -server s1 -wait diff --git a/bin/varnishtest/tests/a02015.vtc b/bin/varnishtest/tests/a02015.vtc deleted file mode 100644 index a38100fb7..000000000 --- a/bin/varnishtest/tests/a02015.vtc +++ /dev/null @@ -1,64 +0,0 @@ -vtest "exclusive dependency" - -server s1 { - stream 1 { - rxreq - txresp - } -run - stream 3 { - rxreq - txresp - } -run - stream 5 { - rxreq - txresp - expect stream.dependency == 0 - } -run - - stream 1 { - expect stream.dependency == 5 - } -run - stream 3 { - expect stream.dependency == 5 - } -run - - stream 1 { - rxprio - } -run - stream 5 { - expect stream.dependency == 1 - } -run - -} -start - -client c1 -connect ${s1_sock} { - stream 1 { - txreq - rxresp - } -run - stream 3 { - txreq - rxresp - } -run - stream 5 { - txreq -req GET -ex - expect stream.dependency == 0 - rxresp - } -run - - stream 1 { - expect stream.dependency == 5 - } -run - stream 3 { - expect stream.dependency == 5 - } -run - - stream 1 { - txprio -stream 0 -ex - } -run - stream 5 { - expect stream.dependency == 1 - } -run -} -run - -server s1 -wait diff --git a/bin/varnishtest/tests/a02016.vtc b/bin/varnishtest/tests/a02016.vtc deleted file mode 100644 index e75dddc5d..000000000 --- a/bin/varnishtest/tests/a02016.vtc +++ /dev/null @@ -1,37 +0,0 @@ -vtest "Test pseudo-headers inspection" - -server s1 { - stream 1 { - rxreq - - expect req.url == "/foo" - expect req.http.:path == "/foo" - - expect req.method == "NOTGET" - expect req.http.:method == "NOTGET" - - expect req.authority == "bar" - expect req.http.:authority == "bar" - - expect req.scheme == "baz" - expect req.http.:scheme == "baz" - - txresp -status 123 - } -run -} -start - -client c1 -connect ${s1_sock} { - stream 1 { - txreq -url "/foo" \ - -req "NOTGET" \ - -hdr ":authority" "bar" \ - -scheme "baz" - - rxresp - - expect resp.status == 123 - expect resp.http.:status == 123 - } -run -} -start - -server s1 -wait diff --git a/bin/varnishtest/tests/a02017.vtc b/bin/varnishtest/tests/a02017.vtc deleted file mode 100644 index c917fcf9c..000000000 --- a/bin/varnishtest/tests/a02017.vtc +++ /dev/null @@ -1,22 +0,0 @@ -vtest "Push promise" - -server s1 { - stream 1 { - rxreq - txpush -promised 2 -url "/hereyougo" - txresp - } -run -} -start - -client c1 -connect ${s1_sock} { - stream 1 { - txreq - rxpush - expect push.id == 2 - expect req.url == "/hereyougo" - expect req.method == "GET" - rxresp - } -run -} -run - -server s1 -wait diff --git a/bin/varnishtest/tests/a02018.vtc b/bin/varnishtest/tests/a02018.vtc deleted file mode 100644 index c3da45ddf..000000000 --- a/bin/varnishtest/tests/a02018.vtc +++ /dev/null @@ -1,21 +0,0 @@ -vtest "H/2 state after sending/receiving preface" - -server s1 { - expect h2.state == "false" - stream 1 { - rxreq - txresp - } -run - expect h2.state == "true" -} -start - -client c1 -connect ${s1_sock} { - expect h2.state == "false" - stream 1 { - txreq - rxresp - } -run - expect h2.state == "true" -} -start - -server s1 -wait diff --git a/bin/varnishtest/tests/a02019.vtc b/bin/varnishtest/tests/a02019.vtc deleted file mode 100644 index 299c61baa..000000000 --- a/bin/varnishtest/tests/a02019.vtc +++ /dev/null @@ -1,24 +0,0 @@ -vtest "Static table encoding" - -server s1 { - stream 1 { - rxreq - expect req.http.:path == "/index.html" - txresp - } -run - -} -start - -client c1 -connect ${s1_sock} { - stream 1 { - - txreq -idxHdr 2 \ - -idxHdr 6 \ - -idxHdr 5 - rxresp - } -run - - -} -run - -server s1 -wait diff --git a/bin/varnishtest/tests/a02020.vtc b/bin/varnishtest/tests/a02020.vtc deleted file mode 100644 index 962dcd0e9..000000000 --- a/bin/varnishtest/tests/a02020.vtc +++ /dev/null @@ -1,43 +0,0 @@ -vtest "Reduce dynamic table while incoming headers are flying" - -server s1 { - stream 1 { - rxreq - txresp -litHdr inc plain hoge plain fuga - expect tbl.enc[1].key == "hoge" - expect tbl.enc[1].value == "fuga" - expect tbl.enc.size == 40 - - } -run - - - stream 3 { - rxreq - txresp -idxHdr 62 -litHdr inc plain "foo" plain "bar" - } -run - - stream 0 { rxsettings } -run -} -start - -client c1 -connect ${s1_sock} { - stream 1 { - txreq - rxresp - expect tbl.dec[1].key == "hoge" - expect tbl.dec[1].value == "fuga" - expect tbl.dec.size == 40 - expect tbl.dec.length == 1 - } -run - - stream 3 { txreq } -run - stream 0 { txsettings -hdrtbl 0 } -run - - non_fatal - stream 3 { - rxresp - expect resp.http.foo == - } -run - -} -run - -server s1 -wait diff --git a/bin/varnishtest/tests/a02021.vtc b/bin/varnishtest/tests/a02021.vtc deleted file mode 100644 index 8116e1542..000000000 --- a/bin/varnishtest/tests/a02021.vtc +++ /dev/null @@ -1,52 +0,0 @@ -vtest "Reduce dynamic table size" - -server s1 { - stream 1 { - rxreq - txresp -litHdr inc plain hoge plain fuga - expect tbl.dec.size == 57 - expect tbl.enc.size == 40 - } -run - - stream 0 { - rxsettings - txsettings -ack - } -run - - stream 3 { - rxreq - expect tbl.dec.size == 110 - expect tbl.enc.size == 0 - txresp - } -run -} -start - -client c1 -connect ${s1_sock} { - stream 1 { - txreq -idxHdr 2 \ - -idxHdr 6 \ - -idxHdr 4 \ - -litIdxHdr inc 1 huf "www.example.com" - rxresp - expect tbl.dec.size == 40 - expect tbl.enc.size == 57 - } -run - - stream 0 { - txsettings -hdrtbl 0 - rxsettings - } -run - - stream 3 { - txreq -idxHdr 2 \ - -idxHdr 6 \ - -idxHdr 4 \ - -idxHdr 62 \ - -litIdxHdr inc 24 huf no-cache - expect tbl.enc.size == 110 - expect tbl.dec.size == 0 - rxresp - } -run -} -run - -server s1 -wait diff --git a/bin/varnishtest/tests/a02022.vtc b/bin/varnishtest/tests/a02022.vtc deleted file mode 100644 index c0aef0e19..000000000 --- a/bin/varnishtest/tests/a02022.vtc +++ /dev/null @@ -1,25 +0,0 @@ -vtest "H/1 -> H/2 upgrade" - -feature cmd "nghttp --version | grep -q 'nghttp2/[1-9]'" - -server s1 { - rxreq - upgrade - - stream 1 { rxprio } -start - stream 3 { rxprio } -start - stream 5 { rxprio } -start - stream 7 { rxprio } -start - stream 9 { rxprio } -start - stream 11 { rxprio } -start - - stream 0 -wait - stream 1 -wait { txresp } -run - - stream 0 { rxgoaway } -run - -} -start - -shell { nghttp http://${s1_sock} -nu } - -server s1 -wait diff --git a/bin/varnishtest/tests/a02023.vtc b/bin/varnishtest/tests/a02023.vtc deleted file mode 100644 index e545b0631..000000000 --- a/bin/varnishtest/tests/a02023.vtc +++ /dev/null @@ -1,44 +0,0 @@ -vtest "Window update" - -server s1 { - stream 1 { - rxreq - txresp -body "bob" - } -run - stream 3 { - rxreq - txresp -nohdrend - txcont -nohdrend -hdr "foo" "bar" - txcont -hdr "baz" "qux" - txdata -data "foo" - txdata -data "bar" - } -run - -} -start - -client c1 -connect ${s1_sock} { - stream 1 { - txreq - rxresp - expect resp.bodylen == 3 - } -run - stream 3 { - txreq - rxhdrs - rxcont - rxcont - expect resp.http.:status == "200" - expect resp.http.foo == "bar" - expect stream.window == 65535 - - rxdata - expect stream.window == 65532 - - rxdata - expect stream.window == 65529 - expect resp.body == "foobar" - expect resp.http.baz == "qux" - } -run -} -run - -server s1 -wait diff --git a/bin/varnishtest/tests/a02024.vtc b/bin/varnishtest/tests/a02024.vtc deleted file mode 100644 index 3e702af10..000000000 --- a/bin/varnishtest/tests/a02024.vtc +++ /dev/null @@ -1,34 +0,0 @@ -vtest "Write a body to a file" - -server s1 { - fatal - stream 1 { - # First, HTTP checks - rxreq - expect req.http.Content-Type == "text/plain" - - # Then, payload checks - write_body req.txt - shell {grep -q request req.txt} - - txresp -hdr Content-Type text/plain -body response - } -run - -} -start - -client c1 -connect ${s1_sock} { - fatal - stream 1 { - txreq -req POST -hdr Content-Type text/plain -body request - - # First, HTTP checks - rxresp - expect resp.http.Content-Type == "text/plain" - - # Then, payload checks - write_body resp.txt - shell {grep -q response resp.txt} - } -run -} -run - -server s1 -wait diff --git a/bin/varnishtest/tests/a02025.vtc b/bin/varnishtest/tests/a02025.vtc deleted file mode 100644 index 594de1c28..000000000 --- a/bin/varnishtest/tests/a02025.vtc +++ /dev/null @@ -1,21 +0,0 @@ -vtest "Test -bodyfrom" - -shell {printf helloworld >body.txt} - -server s1 { - stream 1 { - rxreq - expect req.body == helloworld - txresp -bodyfrom body.txt - } -run -} -start - -client c1 -connect ${s1_sock} { - stream 1 { - txreq -bodyfrom body.txt - rxresp - expect resp.body == helloworld - } -run -} -run - -server s1 -wait diff --git a/bin/varnishtest/tests/a02026.vtc b/bin/varnishtest/tests/a02026.vtc deleted file mode 100644 index 25c44b7b0..000000000 --- a/bin/varnishtest/tests/a02026.vtc +++ /dev/null @@ -1,31 +0,0 @@ -vtest "Test -gzipbody and -gziplen" - -server s1 { - stream 1 { - rxreq - txresp -gzipbody "foo" - } -run - - stream 3 { - rxreq - txresp -gziplen 10 - } -run -} -start - -client c1 -connect ${s1_sock} { - stream 1 { - txreq - rxresp - gunzip - expect resp.body == "foo" - } -run - - stream 3 { - txreq - rxresp - gunzip - expect resp.bodylen == 10 - } -run -} -run - -server s1 -wait diff --git a/bin/varnishtest/tests/a02027.vtc b/bin/varnishtest/tests/a02027.vtc deleted file mode 100644 index 8156c199a..000000000 --- a/bin/varnishtest/tests/a02027.vtc +++ /dev/null @@ -1,69 +0,0 @@ -vtest "Malformed :path handling" - -server s1 { -} -start - -varnish v1 -vcl+backend { - sub vcl_recv { - return (synth(200)); - } -} -start -varnish v1 -cliok "param.set feature +http2" - -client c1 { - stream 1 { - txreq -noadd -hdr ":authority" "foo.com" -hdr ":path" "foobar" -hdr ":scheme" "http" -hdr ":method" "GET" - rxrst - expect rst.err == PROTOCOL_ERROR - } -run - -} -run - -client c1 { - stream 1 { - txreq -noadd -hdr ":authority" "foo.com" -hdr ":path" "//foo" -hdr ":scheme" "http" -hdr ":method" "GET" - rxresp - expect resp.status == 200 - } -run - -} -run - -client c1 { - stream 3 { - txreq -noadd -hdr ":authority" "foo.com" -hdr ":path" "*a" -hdr ":scheme" "http" -hdr ":method" "GET" - rxrst - expect rst.err == PROTOCOL_ERROR - } -run -} -run - -client c1 { - stream 1 { - txreq -noadd -hdr ":authority" "foo.com" -hdr ":path" "*" -hdr ":scheme" "http" -hdr ":method" "GET" - rxrst - expect rst.err == PROTOCOL_ERROR - } -run -} -run - -client c1 { - stream 1 { - txreq -noadd -hdr ":authority" "foo.com" -hdr ":path" "*" -hdr ":scheme" "http" -hdr ":method" "OPTIONS" - rxresp - expect resp.status == 200 - } -run -} -run - -client c1 { - stream 1 { - txreq -noadd -hdr ":authority" "foo.com" -hdr ":path" "*" -hdr ":scheme" "http" -hdr ":method" "OPTIONs" - rxrst - expect rst.err == PROTOCOL_ERROR - } -run -} -run - -client c1 { - stream 1 { - txreq -noadd -hdr ":authority" "foo.com" -hdr ":path" "*" -hdr ":scheme" "http" -hdr ":method" "OPTIONSx" - rxrst - expect rst.err == PROTOCOL_ERROR - } -run -} -run diff --git a/bin/varnishtest/tests/a02028.vtc b/bin/varnishtest/tests/a02028.vtc deleted file mode 100644 index 08f696793..000000000 --- a/bin/varnishtest/tests/a02028.vtc +++ /dev/null @@ -1,24 +0,0 @@ -vtest "Automatic stream numbers" - -server s1 { - loop 4 { - stream next { - rxreq - txresp - } -run - } -} -start - -client c1 -connect ${s1_sock} { - loop 3 { - stream next { - txreq - rxresp - } -run - } - - stream 7 { - txreq - rxresp - } -run -} -run diff --git a/bin/varnishtest/vtc.c b/bin/varnishtest/vtc.c deleted file mode 100644 index 1a2e5d828..000000000 --- a/bin/varnishtest/vtc.c +++ /dev/null @@ -1,625 +0,0 @@ -/*- - * Copyright (c) 2008-2011 Varnish Software AS - * All rights reserved. - * - * Author: Poul-Henning Kamp - * - * SPDX-License-Identifier: BSD-2-Clause - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#include "config.h" - -#include - -#include -#include -#include -#include -#include -#include - -#include "vtc.h" -#include "vtc_log.h" - -#include "vav.h" -#include "vrnd.h" - -#define MAX_TOKENS 200 - -volatile sig_atomic_t vtc_error; /* Error encountered */ -int vtc_stop; /* Stops current test without error */ -pthread_t vtc_thread; -int ign_unknown_macro = 0; -static struct vtclog *vltop; - -static pthread_mutex_t vtc_vrnd_mtx; - -static void -vtc_vrnd_lock(void) -{ - PTOK(pthread_mutex_lock(&vtc_vrnd_mtx)); -} - -static void -vtc_vrnd_unlock(void) -{ - PTOK(pthread_mutex_unlock(&vtc_vrnd_mtx)); -} - -static const char *tfn; - -/********************************************************************** - * Macro facility - */ - -struct macro { - unsigned magic; -#define MACRO_MAGIC 0x803423e3 - VTAILQ_ENTRY(macro) list; - char *name; - char *val; - macro_f *func; -}; - -static VTAILQ_HEAD(,macro) macro_list = VTAILQ_HEAD_INITIALIZER(macro_list); - -static const struct cmds global_cmds[] = { -#define CMD_GLOBAL(n) { #n, cmd_##n }, -#include "cmds.h" - { NULL, NULL } -}; - -static const struct cmds top_cmds[] = { -#define CMD_TOP(n) { #n, cmd_##n }, -#include "cmds.h" - { NULL, NULL } -}; - -/**********************************************************************/ - -static struct macro * -macro_def_int(const char *name, macro_f *func, const char *fmt, va_list ap) -{ - struct macro *m; - char buf[2048]; - - VTAILQ_FOREACH(m, ¯o_list, list) - if (!strcmp(name, m->name)) - break; - if (m == NULL) { - ALLOC_OBJ(m, MACRO_MAGIC); - AN(m); - REPLACE(m->name, name); - AN(m->name); - VTAILQ_INSERT_TAIL(¯o_list, m, list); - } - AN(m); - if (func != NULL) { - AZ(fmt); - m->func = func; - } else { - AN(fmt); - vbprintf(buf, fmt, ap); - REPLACE(m->val, buf); - AN(m->val); - } - return (m); -} - - -/********************************************************************** - * This is for defining macros before we fork the child process which - * runs the test-case. - */ - -void -extmacro_def(const char *name, macro_f *func, const char *fmt, ...) -{ - va_list ap; - - va_start(ap, fmt); - (void)macro_def_int(name, func, fmt, ap); - va_end(ap); -} - -/********************************************************************** - * Below this point is run inside the testing child-process. - */ - -static pthread_mutex_t macro_mtx; - -static void -init_macro(void) -{ - struct macro *m; - - /* Dump the extmacros for completeness */ - VTAILQ_FOREACH(m, ¯o_list, list) { - if (m->val != NULL) - vtc_log(vltop, 4, - "extmacro def %s=%s", m->name, m->val); - else - vtc_log(vltop, 4, "extmacro def %s(...)", m->name); - } - - PTOK(pthread_mutex_init(¯o_mtx, NULL)); -} - -void -macro_def(struct vtclog *vl, const char *instance, const char *name, - const char *fmt, ...) -{ - char buf1[256]; - struct macro *m; - va_list ap; - - AN(fmt); - - if (instance != NULL) { - bprintf(buf1, "%s_%s", instance, name); - name = buf1; - } - - PTOK(pthread_mutex_lock(¯o_mtx)); - va_start(ap, fmt); - m = macro_def_int(name, NULL, fmt, ap); - va_end(ap); - vtc_log(vl, 4, "macro def %s=%s", name, m->val); - PTOK(pthread_mutex_unlock(¯o_mtx)); -} - -void -macro_undef(struct vtclog *vl, const char *instance, const char *name) -{ - char buf1[256]; - struct macro *m; - - if (instance != NULL) { - bprintf(buf1, "%s_%s", instance, name); - name = buf1; - } - - PTOK(pthread_mutex_lock(¯o_mtx)); - VTAILQ_FOREACH(m, ¯o_list, list) - if (!strcmp(name, m->name)) - break; - if (m != NULL) { - if (!vtc_stop) - vtc_log(vl, 4, "macro undef %s", name); - CHECK_OBJ(m, MACRO_MAGIC); - VTAILQ_REMOVE(¯o_list, m, list); - free(m->name); - free(m->val); - FREE_OBJ(m); - } - PTOK(pthread_mutex_unlock(¯o_mtx)); -} - -unsigned -macro_isdef(const char *instance, const char *name) -{ - char buf1[256]; - struct macro *m; - - if (instance != NULL) { - bprintf(buf1, "%s_%s", instance, name); - name = buf1; - } - - PTOK(pthread_mutex_lock(¯o_mtx)); - VTAILQ_FOREACH(m, ¯o_list, list) - if (!strcmp(name, m->name)) - break; - PTOK(pthread_mutex_unlock(¯o_mtx)); - - return (m != NULL); -} - -void -macro_cat(struct vtclog *vl, struct vsb *vsb, const char *b, const char *e) -{ - struct macro *m; - char **argv, *retval = NULL; - const char *err = NULL; - int argc; - - AN(b); - if (e == NULL) - e = strchr(b, '\0'); - AN(e); - - argv = VAV_ParseTxt(b, e, &argc, ARGV_COMMA); - AN(argv); - - if (*argv != NULL) - vtc_fatal(vl, "Macro ${%.*s} parsing failed: %s", - (int)(e - b), b, *argv); - - assert(argc >= 2); - - PTOK(pthread_mutex_lock(¯o_mtx)); - VTAILQ_FOREACH(m, ¯o_list, list) { - CHECK_OBJ_NOTNULL(m, MACRO_MAGIC); - if (!strcmp(argv[1], m->name)) - break; - } - if (m != NULL) { - if (m->func != NULL) { - AZ(m->val); - retval = m->func(argc, argv, &err); - if (err == NULL) - AN(retval); - } else { - AN(m->val); - if (argc == 2) - REPLACE(retval, m->val); - else - err = "macro does not take arguments"; - } - } - PTOK(pthread_mutex_unlock(¯o_mtx)); - - VAV_Free(argv); - - if (err != NULL) - vtc_fatal(vl, "Macro ${%.*s} failed: %s", - (int)(e - b), b, err); - - if (retval == NULL) { - if (!ign_unknown_macro) - vtc_fatal(vl, "Macro ${%.*s} not found", - (int)(e - b), b); - VSB_printf(vsb, "${%.*s}", (int)(e - b), b); - return; - } - - VSB_cat(vsb, retval); - free(retval); -} - -struct vsb * -macro_expandf(struct vtclog *vl, const char *fmt, ...) -{ - va_list ap; - struct vsb *vsb1, *vsb2; - - vsb1 = VSB_new_auto(); - AN(vsb1); - va_start(ap, fmt); - VSB_vprintf(vsb1, fmt, ap); - va_end(ap); - AZ(VSB_finish(vsb1)); - vsb2 = macro_expand(vl, VSB_data(vsb1)); - VSB_destroy(&vsb1); - return (vsb2); -} - -struct vsb * -macro_expand(struct vtclog *vl, const char *text) -{ - struct vsb *vsb; - const char *p, *q; - - vsb = VSB_new_auto(); - AN(vsb); - while (*text != '\0') { - p = strstr(text, "${"); - if (p == NULL) { - VSB_cat(vsb, text); - break; - } - VSB_bcat(vsb, text, p - text); - q = strchr(p, '}'); - if (q == NULL) { - VSB_cat(vsb, text); - break; - } - assert(p[0] == '$'); - assert(p[1] == '{'); - assert(q[0] == '}'); - p += 2; - macro_cat(vl, vsb, p, q); - text = q + 1; - } - AZ(VSB_finish(vsb)); - return (vsb); -} - -/********************************************************************** - * Parse a string - * - * We make a copy of the string and deliberately leak it, so that all - * the cmd functions we call don't have to strdup(3) all over the place. - * - * Static checkers like Coverity may bitch about this, but we don't care. - */ - - -void -parse_string(struct vtclog *vl, void *priv, const char *spec) -{ - char *token_s[MAX_TOKENS], *token_e[MAX_TOKENS]; - struct vsb *token_exp; - char *e, *p, *q, *f, *buf; - int nest_brace; - int tn; - unsigned n, m; - const struct cmds *cp; - - AN(spec); - buf = strdup(spec); - AN(buf); - e = strchr(buf, '\0'); - AN(e); - for (p = buf; p < e; p++) { - if (vtc_error || vtc_stop) { - vtc_log(vl, 1, "Aborting execution, test %s", - vtc_error ? "failed" : "ended"); - break; - } - /* Start of line */ - if (isspace(*p)) - continue; - if (*p == '\n') - continue; - - if (*p == '#') { - for (; *p != '\0' && *p != '\n'; p++) - ; - if (*p == '\0') - break; - continue; - } - - q = strchr(p, '\n'); - if (q == NULL) - q = strchr(p, '\0'); - if (q - p > 60) - vtc_log(vl, 2, "=== %.60s...", p); - else - vtc_log(vl, 2, "=== %.*s", (int)(q - p), p); - - /* First content on line, collect tokens */ - memset(token_s, 0, sizeof token_s); - memset(token_e, 0, sizeof token_e); - tn = 0; - f = p; - while (p < e) { - assert(tn < MAX_TOKENS); - assert(p < e); - if (*p == '\n') { /* End on NL */ - break; - } - if (isspace(*p)) { /* Inter-token whitespace */ - p++; - continue; - } - if (*p == '\\' && p[1] == '\n') { /* line-cont */ - p += 2; - continue; - } - if (*p == '"') { /* quotes */ - token_s[tn] = ++p; - q = p; - for (; *p != '\0'; p++) { - assert(p < e); - if (*p == '"') - break; - if (*p == '\\') { - p += VAV_BackSlash(p, q) - 1; - q++; - } else { - if (*p == '\n') - vtc_fatal(vl, - "Unterminated quoted string in line: %*.*s", - (int)(p - f), (int)(p - f), f); - assert(*p != '\n'); - *q++ = *p; - } - } - token_e[tn++] = q; - p++; - } else if (*p == '{') { /* Braces */ - nest_brace = 0; - token_s[tn] = p + 1; - for (; p < e; p++) { - if (*p == '{') - nest_brace++; - else if (*p == '}') { - if (--nest_brace == 0) - break; - } - } - assert(*p == '}'); - token_e[tn++] = p++; - } else { /* other tokens */ - token_s[tn] = p; - for (; p < e && !isspace(*p); p++) - continue; - token_e[tn++] = p; - } - } - - assert(p <= e); - assert(tn < MAX_TOKENS); - token_s[tn] = NULL; - for (tn = 0; token_s[tn] != NULL; tn++) { - AN(token_e[tn]); /*lint !e771 */ - *token_e[tn] = '\0'; /*lint !e771 */ - if (NULL != strstr(token_s[tn], "${")) { - token_exp = macro_expand(vl, token_s[tn]); - if (vtc_error) - return; - token_s[tn] = VSB_data(token_exp); - token_e[tn] = strchr(token_s[tn], '\0'); - } - } - - -/* SECTION: loop loop - * - * loop NUMBER STRING - * Process STRING as a specification, NUMBER times. - * - * This works inside all specification strings - */ - - if (!strcmp(token_s[0], "loop")) { - n = strtoul(token_s[1], NULL, 0); - for (m = 0; m < n && !vtc_error && !vtc_stop; m++) { - vtc_log(vl, 4, "Loop #%u", m); - parse_string(vl, priv, token_s[2]); - } - continue; - } - - AN(vl->cmds); - for (cp = vl->cmds; cp->name != NULL; cp++) - if (!strcmp(token_s[0], cp->name)) - break; - - if (cp->name == NULL) { - for (cp = global_cmds; cp->name != NULL; cp++) - if (!strcmp(token_s[0], cp->name)) - break; - } - - if (cp->name == NULL) - vtc_fatal(vl, "Unknown command: \"%s\"", token_s[0]); - - assert(cp->cmd != NULL); - cp->cmd(token_s, priv, vl); - } -} - -/********************************************************************** - * Reset commands (between tests) - */ - -static void -reset_cmds(const struct cmds *cmd) -{ - - for (; cmd->name != NULL; cmd++) - cmd->cmd(NULL, NULL, NULL); -} - -/********************************************************************** - * Execute a file - */ - -int -fail_out(void) -{ - unsigned old_err; - static int once = 0; - - if (once++) { - vtc_log(vltop, 1, "failure during reset"); - return (vtc_error); - } - old_err = vtc_error; - if (!vtc_stop) - vtc_stop = 1; - vtc_log(vltop, 1, "RESETTING after %s", tfn); - reset_cmds(global_cmds); - reset_cmds(top_cmds); - vtc_error |= old_err; - - if (vtc_error) - vtc_log(vltop, 1, "TEST %s FAILED", tfn); - else - vtc_log(vltop, 1, "TEST %s completed", tfn); - - if (vtc_stop > 1) - return (1); - return (vtc_error); -} - -int -exec_file(const char *fn, const char *script, const char *tmpdir, - char *logbuf, unsigned loglen) -{ - FILE *f; - struct vsb *vsb; - const char *p; - - AN(tmpdir); - - (void)signal(SIGPIPE, SIG_IGN); - - PTOK(pthread_mutex_init(&vtc_vrnd_mtx, NULL)); - VRND_Lock = vtc_vrnd_lock; - VRND_Unlock = vtc_vrnd_unlock; - VRND_SeedAll(); - - tfn = fn; - vtc_loginit(logbuf, loglen); - vltop = vtc_logopen("top"); - AN(vltop); - vtc_log_set_cmd(vltop, top_cmds); - - vtc_log(vltop, 1, "TEST %s starting", fn); - - init_macro(); - init_server(); - init_syslog(); - init_tunnel(); - - vsb = VSB_new_auto(); - AN(vsb); - if (*fn != '/') - macro_cat(vltop, vsb, "pwd", NULL); - p = strrchr(fn, '/'); - if (p != NULL) { - VSB_putc(vsb, '/'); - VSB_bcat(vsb, fn, p - fn); - } - if (VSB_len(vsb) == 0) - VSB_putc(vsb, '/'); - AZ(VSB_finish(vsb)); - macro_def(vltop, NULL, "testdir", "%s", VSB_data(vsb)); - VSB_destroy(&vsb); - - /* Move into our tmpdir */ - AZ(chdir(tmpdir)); - macro_def(vltop, NULL, "tmpdir", "%s", tmpdir); - p = strrchr(tmpdir, '/'); - AN(p); - p++; - AN(*p); - macro_def(vltop, NULL, "vtcid", "%s", p); - - /* Drop file to tell what was going on here */ - f = fopen("INFO", "w"); - AN(f); - fprintf(f, "Test case: %s\n", fn); - AZ(fclose(f)); - - vtc_stop = 0; - - vtc_thread = pthread_self(); - parse_string(vltop, NULL, script); - return (fail_out()); -} diff --git a/bin/varnishtest/vtc.h b/bin/varnishtest/vtc.h deleted file mode 100644 index b765fe60a..000000000 --- a/bin/varnishtest/vtc.h +++ /dev/null @@ -1,162 +0,0 @@ -/*- - * Copyright (c) 2008-2011 Varnish Software AS - * All rights reserved. - * - * Author: Poul-Henning Kamp - * - * SPDX-License-Identifier: BSD-2-Clause - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - */ - -#include -#include -#include -#include -#ifdef HAVE_PTHREAD_NP_H -#include -#endif - -#include "vdef.h" - -#include "miniobj.h" -#include "vas.h" -#include "vqueue.h" -#include "vsb.h" - -#define VTC_CHECK_NAME(vl, nm, type, chr) \ - do { \ - AN(nm); \ - if (*(nm) != chr) \ - vtc_fatal(vl, \ - type " name must start with '%c' (got %s)", \ - chr, nm); \ - } while (0) - -struct vtclog; -struct suckaddr; - -#define CMD_ARGS char * const *av, void *priv, struct vtclog *vl - -typedef void cmd_f(CMD_ARGS); - -struct cmds { - const char *name; - cmd_f *cmd; -}; - -void parse_string(struct vtclog *vl, void *priv, const char *spec); -int fail_out(void); - -#define CMD_GLOBAL(n) cmd_f cmd_##n; -#define CMD_TOP(n) cmd_f cmd_##n; -#include "cmds.h" - -extern volatile sig_atomic_t vtc_error; /* Error, bail out */ -extern int vtc_stop; /* Abandon current test, no error */ -extern pthread_t vtc_thread; -extern int iflg; -extern vtim_dur vtc_maxdur; -extern char *vmod_path; -extern struct vsb *params_vsb; -extern int leave_temp; -extern int ign_unknown_macro; -extern const char *default_listen_addr; - -void init_server(void); -void init_syslog(void); -void init_tunnel(void); - -/* Sessions */ -struct vtc_sess *Sess_New(struct vtclog *vl, const char *name); -void Sess_Destroy(struct vtc_sess **spp); -int Sess_GetOpt(struct vtc_sess *, char * const **); -int sess_process(struct vtclog *vl, struct vtc_sess *, - const char *spec, int sock, int *sfd, const char *addr); - -typedef int sess_conn_f(void *priv, struct vtclog *); -typedef void sess_disc_f(void *priv, struct vtclog *, int *fd); -pthread_t -Sess_Start_Thread( - void *priv, - struct vtc_sess *vsp, - sess_conn_f *conn, - sess_disc_f *disc, - const char *listen_addr, - int *asocket, - const char *spec -); - -char * synth_body(const char *len, int rnd); - -void cmd_server_gen_vcl(struct vsb *vsb); -void cmd_server_gen_haproxy_conf(struct vsb *vsb); - -void vtc_log_set_cmd(struct vtclog *vl, const struct cmds *cmds); -void vtc_loginit(char *buf, unsigned buflen); -struct vtclog *vtc_logopen(const char *id, ...) v_printflike_(1, 2); -void vtc_logclose(void *arg); -void vtc_log(struct vtclog *vl, int lvl, const char *fmt, ...) - v_printflike_(3, 4); -void vtc_fatal(struct vtclog *vl, const char *, ...) - v_noreturn_ v_printflike_(2,3); -void vtc_dump(struct vtclog *vl, int lvl, const char *pfx, - const char *str, int len); -void vtc_hexdump(struct vtclog *, int , const char *, const void *, unsigned); - -void vtc_proxy_tlv(struct vtclog *vl, struct vsb *vsb, const char *kva); -int vtc_send_proxy(int fd, int version, const struct suckaddr *sac, - const struct suckaddr *sas, struct vsb *tlb); - -int exec_file(const char *fn, const char *script, const char *tmpdir, - char *logbuf, unsigned loglen); - -void macro_undef(struct vtclog *vl, const char *instance, const char *name); -void macro_def(struct vtclog *vl, const char *instance, const char *name, - const char *fmt, ...) v_printflike_(4, 5); -unsigned macro_isdef(const char *instance, const char *name); -void macro_cat(struct vtclog *, struct vsb *, const char *, const char *); -struct vsb *macro_expand(struct vtclog *vl, const char *text); -struct vsb *macro_expandf(struct vtclog *vl, const char *, ...) - v_printflike_(2, 3); - -typedef char* macro_f(int, char *const *, const char **); -void extmacro_def(const char *name, macro_f *func, const char *fmt, ...) - v_printflike_(3, 4); - -struct http; -void cmd_stream(CMD_ARGS); -void start_h2(struct http *hp); -void stop_h2(struct http *hp); -void b64_settings(struct http *hp, const char *s); - -/* vtc_gzip.c */ -void vtc_gunzip(struct http *, char *, long *); -int vtc_gzip_cmd(struct http *hp, char * const *argv, char **body, long *bodylen); - -/* vtc_subr.c */ -struct vsb *vtc_hex_to_bin(struct vtclog *vl, const char *arg); -void vtc_expect(struct vtclog *, const char *, const char *, const char *, - const char *, const char *); -void vtc_wait4(struct vtclog *, long, int, int, int); -void *vtc_record(struct vtclog *, int, struct vsb *); diff --git a/bin/varnishtest/vtc_barrier.c b/bin/varnishtest/vtc_barrier.c deleted file mode 100644 index c1585d223..000000000 --- a/bin/varnishtest/vtc_barrier.c +++ /dev/null @@ -1,505 +0,0 @@ -/*- - * Copyright (c) 2005 Varnish Software AS - * All rights reserved. - * - * Author: Dridi Boukelmoune - * - * SPDX-License-Identifier: BSD-2-Clause - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#include "config.h" - -#include -#include -#include -#include -#include - -#include -#include /* for MUSL */ - -#include "vtc.h" -#include "vtcp.h" -#include "vtim.h" -#include "vsa.h" - -enum barrier_e { - BARRIER_NONE = 0, - BARRIER_COND, - BARRIER_SOCK, -}; - -struct barrier { - unsigned magic; -#define BARRIER_MAGIC 0x7b54c275 - char *name; - VTAILQ_ENTRY(barrier) list; - pthread_mutex_t mtx; - pthread_cond_t cond; - - int waiters; - int expected; - int cyclic; - - enum barrier_e type; - union { - int cond_cycle; - pthread_t sock_thread; - }; -}; - -static VTAILQ_HEAD(, barrier) barriers = VTAILQ_HEAD_INITIALIZER(barriers); - -static struct barrier * -barrier_new(const char *name, struct vtclog *vl) -{ - struct barrier *b; - - ALLOC_OBJ(b, BARRIER_MAGIC); - AN(b); - if (!pthread_equal(pthread_self(), vtc_thread)) - vtc_fatal(vl, - "Barrier %s can only be created on the top thread", name); - REPLACE(b->name, name); - - PTOK(pthread_mutex_init(&b->mtx, NULL)); - PTOK(pthread_cond_init(&b->cond, NULL)); - b->waiters = 0; - b->expected = 0; - VTAILQ_INSERT_TAIL(&barriers, b, list); - return (b); -} - -/********************************************************************** - * Init a barrier - */ - -static void -barrier_expect(struct barrier *b, const char *av, struct vtclog *vl) -{ - unsigned expected; - - if (b->type != BARRIER_NONE) - vtc_fatal(vl, - "Barrier(%s) use error: already initialized", b->name); - - AZ(b->expected); - AZ(b->waiters); - expected = strtoul(av, NULL, 0); - if (expected < 2) - vtc_fatal(vl, - "Barrier(%s) use error: wrong expectation (%u)", - b->name, expected); - - b->expected = expected; -} - -static void -barrier_cond(struct barrier *b, const char *av, struct vtclog *vl) -{ - - CHECK_OBJ_NOTNULL(b, BARRIER_MAGIC); - PTOK(pthread_mutex_lock(&b->mtx)); - barrier_expect(b, av, vl); - b->type = BARRIER_COND; - PTOK(pthread_mutex_unlock(&b->mtx)); -} - -static void * -barrier_sock_thread(void *priv) -{ - struct barrier *b; - struct vtclog *vl; - const char *err; - char buf[vsa_suckaddr_len]; - const struct suckaddr *sua; - - char abuf[VTCP_ADDRBUFSIZE], pbuf[VTCP_PORTBUFSIZE]; - int i, sock, *conns; - struct pollfd pfd[1]; - - CAST_OBJ_NOTNULL(b, priv, BARRIER_MAGIC); - assert(b->type == BARRIER_SOCK); - - PTOK(pthread_mutex_lock(&b->mtx)); - - vl = vtc_logopen("%s", b->name); - pthread_cleanup_push(vtc_logclose, vl); - - sock = VTCP_listen_on(default_listen_addr, NULL, b->expected, &err); - if (sock < 0) { - PTOK(pthread_cond_signal(&b->cond)); - PTOK(pthread_mutex_unlock(&b->mtx)); - vtc_fatal(vl, "Barrier(%s) %s fails: %s (errno=%d)", - b->name, err, strerror(errno), errno); - } - assert(sock > 0); - VTCP_nonblocking(sock); - sua = VSA_getsockname(sock, buf, sizeof buf); - AN(sua); - VTCP_name(sua, abuf, sizeof abuf, pbuf, sizeof pbuf); - - macro_def(vl, b->name, "addr", "%s", abuf); - macro_def(vl, b->name, "port", "%s", pbuf); - if (VSA_Get_Proto(sua) == AF_INET) - macro_def(vl, b->name, "sock", "%s:%s", abuf, pbuf); - else - macro_def(vl, b->name, "sock", "[%s]:%s", abuf, pbuf); - - PTOK(pthread_cond_signal(&b->cond)); - PTOK(pthread_mutex_unlock(&b->mtx)); - - conns = calloc(b->expected, sizeof *conns); - AN(conns); - - while (!vtc_stop && !vtc_error) { - pfd[0].fd = sock; - pfd[0].events = POLLIN; - - i = poll(pfd, 1, 100); - if (i == 0) - continue; - if (i < 0) { - if (errno == EINTR) - continue; - closefd(&sock); - vtc_fatal(vl, - "Barrier(%s) select fails: %s (errno=%d)", - b->name, strerror(errno), errno); - } - assert(i == 1); - assert(b->waiters <= b->expected); - if (b->waiters == b->expected) - vtc_fatal(vl, - "Barrier(%s) use error: " - "more waiters than the %u expected", - b->name, b->expected); - - i = accept(sock, NULL, NULL); - if (i < 0) { - closefd(&sock); - vtc_fatal(vl, - "Barrier(%s) accept fails: %s (errno=%d)", - b->name, strerror(errno), errno); - } - - /* NB. We don't keep track of the established connections, only - * that connections were made to the barrier's socket. - */ - conns[b->waiters] = i; - - if (++b->waiters < b->expected) { - vtc_log(vl, 4, "Barrier(%s) wait %u of %u", - b->name, b->waiters, b->expected); - continue; - } - - vtc_log(vl, 4, "Barrier(%s) wake %u", b->name, b->expected); - for (i = 0; i < b->expected; i++) - closefd(&conns[i]); - - if (b->cyclic) - b->waiters = 0; - else - break; - } - - if (b->waiters % b->expected > 0) { - /* wake up outstanding waiters */ - for (i = 0; i < b->waiters; i++) - closefd(&conns[i]); - if (!vtc_error) - vtc_fatal(vl, "Barrier(%s) has %u outstanding waiters", - b->name, b->waiters); - } - - macro_undef(vl, b->name, "addr"); - macro_undef(vl, b->name, "port"); - macro_undef(vl, b->name, "sock"); - closefd(&sock); - free(conns); - pthread_cleanup_pop(0); - vtc_logclose(vl); - return (NULL); -} - -static void -barrier_sock(struct barrier *b, const char *av, struct vtclog *vl) -{ - - CHECK_OBJ_NOTNULL(b, BARRIER_MAGIC); - PTOK(pthread_mutex_lock(&b->mtx)); - barrier_expect(b, av, vl); - b->type = BARRIER_SOCK; - - /* NB. We can use the BARRIER_COND's pthread_cond_t to wait until the - * socket is ready for convenience. - */ - PTOK(pthread_create(&b->sock_thread, NULL, barrier_sock_thread, b)); - PTOK(pthread_cond_wait(&b->cond, &b->mtx)); - PTOK(pthread_mutex_unlock(&b->mtx)); -} - -static void -barrier_cyclic(struct barrier *b, struct vtclog *vl) -{ - enum barrier_e t; - int w; - - CHECK_OBJ_NOTNULL(b, BARRIER_MAGIC); - - PTOK(pthread_mutex_lock(&b->mtx)); - t = b->type; - w = b->waiters; - PTOK(pthread_mutex_unlock(&b->mtx)); - - if (t == BARRIER_NONE) - vtc_fatal(vl, - "Barrier(%s) use error: not initialized", b->name); - - if (w != 0) - vtc_fatal(vl, - "Barrier(%s) use error: already in use", b->name); - - PTOK(pthread_mutex_lock(&b->mtx)); - b->cyclic = 1; - PTOK(pthread_mutex_unlock(&b->mtx)); -} - -/********************************************************************** - * Sync a barrier - */ - -static void -barrier_cond_sync(struct barrier *b, struct vtclog *vl) -{ - struct timespec ts; - int r, w, c; - - CHECK_OBJ_NOTNULL(b, BARRIER_MAGIC); - assert(b->type == BARRIER_COND); - - PTOK(pthread_mutex_lock(&b->mtx)); - w = b->waiters; - assert(w <= b->expected); - - if (w == b->expected) - w = -1; - else - b->waiters = ++w; - - c = b->cond_cycle; - PTOK(pthread_mutex_unlock(&b->mtx)); - - if (w < 0) - vtc_fatal(vl, - "Barrier(%s) use error: more waiters than the %u expected", - b->name, b->expected); - - PTOK(pthread_mutex_lock(&b->mtx)); - if (w == b->expected) { - vtc_log(vl, 4, "Barrier(%s) wake %u", b->name, b->expected); - b->cond_cycle++; - if (b->cyclic) - b->waiters = 0; - PTOK(pthread_cond_broadcast(&b->cond)); - } else { - vtc_log(vl, 4, "Barrier(%s) wait %u of %u", - b->name, b->waiters, b->expected); - do { - ts = VTIM_timespec(VTIM_real() + .1); - r = pthread_cond_timedwait(&b->cond, &b->mtx, &ts); - assert(r == 0 || r == ETIMEDOUT); - } while (!vtc_stop && !vtc_error && r == ETIMEDOUT && - c == b->cond_cycle); - } - PTOK(pthread_mutex_unlock(&b->mtx)); -} - -static void -barrier_sock_sync(const struct barrier *b, struct vtclog *vl) -{ - struct vsb *vsb; - const char *err; - char buf[32]; - int i, sock; - ssize_t sz; - - CHECK_OBJ_NOTNULL(b, BARRIER_MAGIC); - assert(b->type == BARRIER_SOCK); - - vsb = macro_expandf(vl, "${%s_sock}", b->name); - vtc_log(vl, 4, "Barrier(%s) sync with socket", b->name); - - sock = VTCP_open(VSB_data(vsb), NULL, 0., &err); - if (sock < 0) - vtc_fatal(vl, "Barrier(%s) connection failed: %s", - b->name, err); - - VSB_destroy(&vsb); - - sz = read(sock, buf, sizeof buf); /* XXX loop with timeout? */ - i = errno; - closefd(&sock); - - if (sz < 0) - vtc_fatal(vl, "Barrier(%s) read failed: %s (errno=%d)", - b->name, strerror(i), i); - if (sz > 0) - vtc_fatal(vl, "Barrier(%s) unexpected data (%zdB)", - b->name, sz); -} - -static void -barrier_sync(struct barrier *b, struct vtclog *vl) -{ - - CHECK_OBJ_NOTNULL(b, BARRIER_MAGIC); - - switch (b->type) { - case BARRIER_NONE: - vtc_fatal(vl, - "Barrier(%s) use error: not initialized", b->name); - break; - case BARRIER_COND: - barrier_cond_sync(b, vl); - break; - case BARRIER_SOCK: - barrier_sock_sync(b, vl); - break; - default: - WRONG("Wrong barrier type"); - } -} - -/* SECTION: barrier barrier - * - * NOTE: This command is available everywhere commands are given. - * - * Barriers allows you to synchronize different threads to make sure events - * occur in the right order. It's even possible to use them in VCL. - * - * First, it's necessary to declare the barrier:: - * - * barrier bNAME TYPE NUMBER [-cyclic] - * - * With the arguments being: - * - * bNAME - * this is the name of the barrier, used to identify it when you'll - * create sync points. It must start with 'b'. - * - * TYPE - * it can be "cond" (mutex) or "sock" (socket) and sets internal - * behavior. If you don't need VCL synchronization, use cond. - * - * NUMBER - * number of sync point needed to go through the barrier. - * - * \-cyclic - * if present, the barrier will reset itself and be ready for another - * round once gotten through. - * - * Then, to add a sync point:: - * - * barrier bNAME sync - * - * This will block the parent thread until the number of sync points for bNAME - * reaches the NUMBER given in the barrier declaration. - * - * If you wish to synchronize the VCL, you need to declare a "sock" barrier. - * This will emit a macro definition named "bNAME_sock" that you can use in - * VCL (after importing the vtc vmod):: - * - * vtc.barrier_sync("${bNAME_sock}"); - * - * This function returns 0 if everything went well and is the equivalent of - * ``barrier bNAME sync`` at the VTC top-level. - * - * - */ - -void -cmd_barrier(CMD_ARGS) -{ - struct barrier *b, *b2; - int r; - - (void)priv; - - if (av == NULL) { - /* Reset and free */ - VTAILQ_FOREACH_SAFE(b, &barriers, list, b2) { - r = pthread_mutex_trylock(&b->mtx); - assert(r == 0 || r == EBUSY); - switch (b->type) { - case BARRIER_COND: - break; - case BARRIER_SOCK: - PTOK(pthread_join(b->sock_thread, NULL)); - break; - default: - WRONG("Wrong barrier type"); - } - if (r == 0) - PTOK(pthread_mutex_unlock(&b->mtx)); - } - return; - } - - AZ(strcmp(av[0], "barrier")); - av++; - - VTC_CHECK_NAME(vl, av[0], "Barrier", 'b'); - VTAILQ_FOREACH(b, &barriers, list) - if (!strcmp(b->name, av[0])) - break; - if (b == NULL) - b = barrier_new(av[0], vl); - av++; - - for (; *av != NULL; av++) { - if (!strcmp(*av, "cond")) { - av++; - AN(*av); - barrier_cond(b, *av, vl); - continue; - } - if (!strcmp(*av, "sock")) { - av++; - AN(*av); - barrier_sock(b, *av, vl); - continue; - } - if (!strcmp(*av, "sync")) { - barrier_sync(b, vl); - continue; - } - if (!strcmp(*av, "-cyclic")) { - barrier_cyclic(b, vl); - continue; - } - vtc_fatal(vl, "Unknown barrier argument: %s", *av); - } -} diff --git a/bin/varnishtest/vtc_client.c b/bin/varnishtest/vtc_client.c deleted file mode 100644 index 1281cc5a9..000000000 --- a/bin/varnishtest/vtc_client.c +++ /dev/null @@ -1,416 +0,0 @@ -/*- - * Copyright (c) 2008-2011 Varnish Software AS - * All rights reserved. - * - * Author: Poul-Henning Kamp - * - * SPDX-License-Identifier: BSD-2-Clause - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#include "config.h" - -#include -#include - -#include -#include -#include -#include -#include -#include - -#include "vtc.h" - -#include "vsa.h" -#include "vss.h" -#include "vtcp.h" -#include "vus.h" - -struct client { - unsigned magic; -#define CLIENT_MAGIC 0x6242397c - char *name; - struct vtclog *vl; - VTAILQ_ENTRY(client) list; - struct vtc_sess *vsp; - - char *spec; - - char connect[256]; - char *addr; - - char *proxy_spec; - int proxy_version; - - unsigned running; - pthread_t tp; -}; - -static VTAILQ_HEAD(, client) clients = VTAILQ_HEAD_INITIALIZER(clients); - -/********************************************************************** - * Send the proxy header - */ - -static void -client_proxy(struct vtclog *vl, int fd, int version, const char *speca) -{ - const struct suckaddr *sac, *sas; - char *spec, *save, *p; - struct vsb *tlv; - - spec = strdup(speca); - AN(spec); - save = NULL; - - p = strtok_r(spec, " ", &save); - AN(p); - sac = VSS_ResolveOne(NULL, p, NULL, 0, SOCK_STREAM, AI_PASSIVE); - if (sac == NULL) - vtc_fatal(vl, "Could not resolve client address"); - - p = strtok_r(NULL, " ", &save); - AN(p); - sas = VSS_ResolveOne(NULL, p, NULL, 0, SOCK_STREAM, AI_PASSIVE); - if (sas == NULL) - vtc_fatal(vl, "Could not resolve client address"); - - tlv = VSB_new_auto(); - while ((p = strtok_r(NULL, " ", &save)) != NULL) - vtc_proxy_tlv(vl, tlv, p); - - free(spec); - - AZ(VSB_finish(tlv)); - if (vtc_send_proxy(fd, version, sac, sas, tlv)) - vtc_fatal(vl, "Write failed: %s", strerror(errno)); - VSA_free(&sac); - VSA_free(&sas); -} - -/********************************************************************** - * Socket connect. - */ - -static int -client_tcp_connect(struct vtclog *vl, const char *addr, double tmo, - const char **errp) -{ - int fd; - char mabuf[VTCP_ADDRBUFSIZE], mpbuf[VTCP_PORTBUFSIZE]; - - AN(addr); - AN(errp); - fd = VTCP_open(addr, NULL, tmo, errp); - if (fd < 0) - return (fd); - VTCP_myname(fd, mabuf, sizeof mabuf, mpbuf, sizeof mpbuf); - vtc_log(vl, 3, "connected fd %d from %s %s to %s", fd, mabuf, mpbuf, - addr); - return (fd); -} - -/* cf. VTCP_Open() */ -static int v_matchproto_(vus_resolved_f) -uds_open(void *priv, const struct sockaddr_un *uds) -{ - double *p; - int s, i, tmo; - struct pollfd fds[1]; - socklen_t sl; - - sl = VUS_socklen(uds); - - AN(priv); - AN(uds); - p = priv; - assert(*p > 0.); - tmo = (int)(*p * 1e3); - - s = socket(uds->sun_family, SOCK_STREAM, 0); - if (s < 0) - return (s); - - VTCP_nonblocking(s); - i = connect(s, (const void*)uds, sl); - if (i == 0) - return (s); - if (errno != EINPROGRESS) { - closefd(&s); - return (-1); - } - - fds[0].fd = s; - fds[0].events = POLLWRNORM; - fds[0].revents = 0; - i = poll(fds, 1, tmo); - - if (i == 0) { - closefd(&s); - errno = ETIMEDOUT; - return (-1); - } - - return (VTCP_connected(s)); -} - -static int -client_uds_connect(struct vtclog *vl, const char *path, double tmo, - const char **errp) -{ - int fd; - - assert(tmo >= 0); - - errno = 0; - fd = VUS_resolver(path, uds_open, &tmo, errp); - if (fd < 0) { - *errp = strerror(errno); - return (fd); - } - vtc_log(vl, 3, "connected fd %d to %s", fd, path); - return (fd); -} - -static int -client_connect(struct vtclog *vl, struct client *c) -{ - const char *err; - int fd; - - vtc_log(vl, 3, "Connect to %s", c->addr); - if (VUS_is(c->addr)) - fd = client_uds_connect(vl, c->addr, 10., &err); - else - fd = client_tcp_connect(vl, c->addr, 10., &err); - if (fd < 0) - vtc_fatal(c->vl, "Failed to open %s: %s", - c->addr, err); - /* VTCP_blocking does its own checks, trust it */ - VTCP_blocking(fd); - if (c->proxy_spec != NULL) - client_proxy(vl, fd, c->proxy_version, c->proxy_spec); - return (fd); -} - -/********************************************************************** - * Client thread - */ - -static int -client_conn(void *priv, struct vtclog *vl) -{ - struct client *c; - - CAST_OBJ_NOTNULL(c, priv, CLIENT_MAGIC); - return (client_connect(vl, c)); -} - -static void -client_disc(void *priv, struct vtclog *vl, int *fdp) -{ - (void)priv; - vtc_log(vl, 3, "closing fd %d", *fdp); - VTCP_close(fdp); -} - -/********************************************************************** - * Allocate and initialize a client - */ - -static struct client * -client_new(const char *name) -{ - struct client *c; - - ALLOC_OBJ(c, CLIENT_MAGIC); - AN(c); - REPLACE(c->name, name); - c->vl = vtc_logopen("%s", name); - AN(c->vl); - c->vsp = Sess_New(c->vl, name); - AN(c->vsp); - - bprintf(c->connect, "%s", "${v1_sock}"); - VTAILQ_INSERT_TAIL(&clients, c, list); - return (c); -} - -/********************************************************************** - * Clean up client - */ - -static void -client_delete(struct client *c) -{ - - CHECK_OBJ_NOTNULL(c, CLIENT_MAGIC); - Sess_Destroy(&c->vsp); - vtc_logclose(c->vl); - free(c->spec); - free(c->name); - free(c->addr); - free(c->proxy_spec); - /* XXX: MEMLEAK (?)*/ - FREE_OBJ(c); -} - -/********************************************************************** - * Start the client thread - */ - -static void -client_start(struct client *c) -{ - struct vsb *vsb; - - CHECK_OBJ_NOTNULL(c, CLIENT_MAGIC); - vtc_log(c->vl, 2, "Starting client"); - c->running = 1; - vsb = macro_expand(c->vl, c->connect); - AN(vsb); - REPLACE(c->addr, VSB_data(vsb)); - VSB_destroy(&vsb); - c->tp = Sess_Start_Thread( - c, - c->vsp, - client_conn, - client_disc, - c->addr, - NULL, - c->spec - ); -} - -/********************************************************************** - * Wait for client thread to stop - */ - -static void -client_wait(struct client *c) -{ - void *res; - - CHECK_OBJ_NOTNULL(c, CLIENT_MAGIC); - vtc_log(c->vl, 2, "Waiting for client"); - PTOK(pthread_join(c->tp, &res)); - if (res != NULL) - vtc_fatal(c->vl, "Client returned \"%s\"", (char *)res); - c->tp = 0; - c->running = 0; -} - -/********************************************************************** - * Run the client thread - */ - -static void -client_run(struct client *c) -{ - - client_start(c); - client_wait(c); -} - - -/********************************************************************** - * Client command dispatch - */ - -void -cmd_client(CMD_ARGS) -{ - struct client *c, *c2; - - (void)priv; - - if (av == NULL) { - /* Reset and free */ - VTAILQ_FOREACH_SAFE(c, &clients, list, c2) { - VTAILQ_REMOVE(&clients, c, list); - if (c->tp != 0) - client_wait(c); - client_delete(c); - } - return; - } - - AZ(strcmp(av[0], "client")); - av++; - - VTC_CHECK_NAME(vl, av[0], "Client", 'c'); - VTAILQ_FOREACH(c, &clients, list) - if (!strcmp(c->name, av[0])) - break; - if (c == NULL) - c = client_new(av[0]); - av++; - - for (; *av != NULL; av++) { - if (vtc_error) - break; - - if (!strcmp(*av, "-wait")) { - client_wait(c); - continue; - } - - /* Don't muck about with a running client */ - if (c->running) - client_wait(c); - - AZ(c->running); - if (Sess_GetOpt(c->vsp, &av)) - continue; - - if (!strcmp(*av, "-connect")) { - bprintf(c->connect, "%s", av[1]); - av++; - continue; - } - if (!strcmp(*av, "-proxy1")) { - REPLACE(c->proxy_spec, av[1]); - c->proxy_version = 1; - av++; - continue; - } - if (!strcmp(*av, "-proxy2")) { - REPLACE(c->proxy_spec, av[1]); - c->proxy_version = 2; - av++; - continue; - } - if (!strcmp(*av, "-start")) { - client_start(c); - continue; - } - if (!strcmp(*av, "-run")) { - client_run(c); - continue; - } - if (**av == '-') - vtc_fatal(c->vl, "Unknown client argument: %s", *av); - REPLACE(c->spec, *av); - } -} diff --git a/bin/varnishtest/vtc_gzip.c b/bin/varnishtest/vtc_gzip.c deleted file mode 100644 index b07e7218d..000000000 --- a/bin/varnishtest/vtc_gzip.c +++ /dev/null @@ -1,254 +0,0 @@ -/*- - * Copyright (c) 2008-2019 Varnish Software AS - * All rights reserved. - * - * Author: Poul-Henning Kamp - * - * SPDX-License-Identifier: BSD-2-Clause - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#include "config.h" - -#include -#include -#include - -#include "vtc.h" -#include "vtc_http.h" -#include "vgz.h" - -#ifdef VGZ_EXTENSIONS -static void -vtc_report_gz_bits(struct vtclog *vl, const z_stream *vz) -{ - vtc_log(vl, 4, "startbit = %ju %ju/%ju", - (uintmax_t)vz->start_bit, - (uintmax_t)vz->start_bit >> 3, (uintmax_t)vz->start_bit & 7); - vtc_log(vl, 4, "lastbit = %ju %ju/%ju", - (uintmax_t)vz->last_bit, - (uintmax_t)vz->last_bit >> 3, (uintmax_t)vz->last_bit & 7); - vtc_log(vl, 4, "stopbit = %ju %ju/%ju", - (uintmax_t)vz->stop_bit, - (uintmax_t)vz->stop_bit >> 3, (uintmax_t)vz->stop_bit & 7); -} -#endif - -static size_t -APOS(ssize_t sz) -{ - assert(sz >= 0); - return (sz); -} - -/********************************************************************** - * GUNZIPery - */ - -static struct vsb * -vtc_gunzip_vsb(struct vtclog *vl, int fatal, const struct vsb *vin) -{ - z_stream vz; - struct vsb *vout; - int i; - char buf[BUFSIZ]; - - memset(&vz, 0, sizeof vz); - vout = VSB_new_auto(); - AN(vout); - - vz.next_in = (void*)VSB_data(vin); - vz.avail_in = APOS(VSB_len(vin)); - - assert(Z_OK == inflateInit2(&vz, 31)); - - do { - vz.next_out = (void*)buf; - vz.avail_out = sizeof buf; - i = inflate(&vz, Z_FINISH); - if (vz.avail_out != sizeof buf) - VSB_bcat(vout, buf, sizeof buf - vz.avail_out); - } while (i == Z_OK || i == Z_BUF_ERROR); - if (i != Z_STREAM_END) - vtc_log(vl, fatal, - "Gunzip error = %d (%s) in:%jd out:%jd len:%zd", - i, vz.msg, (intmax_t)vz.total_in, (intmax_t)vz.total_out, - VSB_len(vin)); - AZ(VSB_finish(vout)); -#ifdef VGZ_EXTENSIONS - vtc_report_gz_bits(vl, &vz); -#endif - assert(Z_OK == inflateEnd(&vz)); - return (vout); -} - -void -vtc_gunzip(struct http *hp, char *body, long *bodylen) -{ - struct vsb *vin, *vout; - - AN(body); - if (body[0] != (char)0x1f || body[1] != (char)0x8b) - vtc_log(hp->vl, hp->fatal, - "Gunzip error: body lacks gzip magic"); - - vin = VSB_new_auto(); - AN(vin); - VSB_bcat(vin, body, *bodylen); - AZ(VSB_finish(vin)); - vout = vtc_gunzip_vsb(hp->vl, hp->fatal, vin); - VSB_destroy(&vin); - - memcpy(body, VSB_data(vout), APOS(VSB_len(vout) + 1)); - *bodylen = APOS(VSB_len(vout)); - VSB_destroy(&vout); - vtc_log(hp->vl, 3, "new bodylen %ld", *bodylen); - vtc_dump(hp->vl, 4, "body", body, *bodylen); - bprintf(hp->bodylen, "%ld", *bodylen); -} - -/********************************************************************** - * GZIPery - */ - -static int -vtc_gzip_chunk(z_stream *vz, struct vsb *vout, const char *in, size_t inlen, int flush) -{ - int i; - char buf[BUFSIZ]; - - vz->next_in = TRUST_ME(in); - vz->avail_in = APOS(inlen); - do { - vz->next_out = (void*)buf; - vz->avail_out = sizeof buf; - i = deflate(vz, flush); - if (vz->avail_out != sizeof buf) - VSB_bcat(vout, buf, sizeof buf - vz->avail_out); - } while (i == Z_OK || vz->avail_in > 0); - vz->next_out = NULL; - vz->avail_out = 0; - vz->next_in = NULL; - AZ(vz->avail_in); - vz->avail_in = 0; - return (i); -} - -static void -vtc_gzip(struct http *hp, const char *input, char **body, long *bodylen, int fragment) -{ - struct vsb *vout; - int i, res; - size_t inlen = strlen(input); - z_stream vz; - - memset(&vz, 0, sizeof vz); - vout = VSB_new_auto(); - AN(vout); - - assert(Z_OK == deflateInit2(&vz, - hp->gziplevel, Z_DEFLATED, 31, 9, Z_DEFAULT_STRATEGY)); - - while (fragment && inlen > 3) { - res = inlen / 3; - i = vtc_gzip_chunk(&vz, vout, input, res, Z_BLOCK); - if (i != Z_OK && i != Z_BUF_ERROR) { - vtc_log(hp->vl, hp->fatal, - "Gzip error = %d (%s) in:%jd out:%jd len:%zd", - i, vz.msg, (intmax_t)vz.total_in, - (intmax_t)vz.total_out, strlen(input)); - } - input += res; - inlen -= res; - } - - i = vtc_gzip_chunk(&vz, vout, input, inlen, Z_FINISH); - if (i != Z_STREAM_END) { - vtc_log(hp->vl, hp->fatal, - "Gzip error = %d (%s) in:%jd out:%jd len:%zd", - i, vz.msg, (intmax_t)vz.total_in, (intmax_t)vz.total_out, - strlen(input)); - } - AZ(VSB_finish(vout)); -#ifdef VGZ_EXTENSIONS - res = vz.stop_bit & 7; - vtc_report_gz_bits(hp->vl, &vz); -#else - res = 0; -#endif - assert(Z_OK == deflateEnd(&vz)); - -#ifdef VGZ_EXTENSIONS - if (hp->gzipresidual >= 0 && hp->gzipresidual != res) - vtc_log(hp->vl, hp->fatal, - "Wrong gzip residual got %d wanted %d", - res, hp->gzipresidual); -#endif - *body = malloc(APOS(VSB_len(vout) + 1)); - AN(*body); - memcpy(*body, VSB_data(vout), APOS(VSB_len(vout) + 1)); - *bodylen = APOS(VSB_len(vout)); - VSB_destroy(&vout); - vtc_log(hp->vl, 3, "new bodylen %ld", *bodylen); - vtc_dump(hp->vl, 4, "body", *body, *bodylen); - bprintf(hp->bodylen, "%ld", *bodylen); -} - -int -vtc_gzip_cmd(struct http *hp, char * const *av, char **body, long *bodylen) -{ - char *b; - - AN(hp); - AN(av); - AN(body); - AN(bodylen); - - if (!strcmp(*av, "-gzipresidual")) { - hp->gzipresidual = strtoul(av[1], NULL, 0); - return (1); - } - if (!strcmp(*av, "-gziplevel")) { - hp->gziplevel = strtoul(av[1], NULL, 0); - return (1); - } - if (!strcmp(*av, "-gzipbody")) { - if (*body != NULL) - free(*body); - *body = NULL; - vtc_gzip(hp, av[1], body, bodylen, 0); - AN(*body); - return (2); - } - if (!strcmp(*av, "-gziplen")) { - if (*body != NULL) - free(*body); - *body = NULL; - b = synth_body(av[1], 1); - vtc_gzip(hp, b, body, bodylen, 1); - AN(*body); - free(b); - return (2); - } - return (0); -} diff --git a/bin/varnishtest/vtc_h2_enctbl.h b/bin/varnishtest/vtc_h2_enctbl.h deleted file mode 100644 index 186a293d9..000000000 --- a/bin/varnishtest/vtc_h2_enctbl.h +++ /dev/null @@ -1,261 +0,0 @@ -/*- - * For Copyright information see RFC7541 [BSD3] - */ - -HPACK(0, 0x1ff8, 13) -HPACK(1, 0x7fffd8, 23) -HPACK(2, 0xfffffe2, 28) -HPACK(3, 0xfffffe3, 28) -HPACK(4, 0xfffffe4, 28) -HPACK(5, 0xfffffe5, 28) -HPACK(6, 0xfffffe6, 28) -HPACK(7, 0xfffffe7, 28) -HPACK(8, 0xfffffe8, 28) -HPACK(9, 0xffffea, 24) -HPACK(10, 0x3ffffffc, 30) -HPACK(11, 0xfffffe9, 28) -HPACK(12, 0xfffffea, 28) -HPACK(13, 0x3ffffffd, 30) -HPACK(14, 0xfffffeb, 28) -HPACK(15, 0xfffffec, 28) -HPACK(16, 0xfffffed, 28) -HPACK(17, 0xfffffee, 28) -HPACK(18, 0xfffffef, 28) -HPACK(19, 0xffffff0, 28) -HPACK(20, 0xffffff1, 28) -HPACK(21, 0xffffff2, 28) -HPACK(22, 0x3ffffffe, 30) -HPACK(23, 0xffffff3, 28) -HPACK(24, 0xffffff4, 28) -HPACK(25, 0xffffff5, 28) -HPACK(26, 0xffffff6, 28) -HPACK(27, 0xffffff7, 28) -HPACK(28, 0xffffff8, 28) -HPACK(29, 0xffffff9, 28) -HPACK(30, 0xffffffa, 28) -HPACK(31, 0xffffffb, 28) -HPACK(32, 0x14, 6) /* ' ' */ -HPACK(33, 0x3f8, 10) /* '!' */ -HPACK(34, 0x3f9, 10) /* '"' */ -HPACK(35, 0xffa, 12) /* '#' */ -HPACK(36, 0x1ff9, 13) /* '$' */ -HPACK(37, 0x15, 6) /* '%' */ -HPACK(38, 0xf8, 8) /* '&' */ -HPACK(39, 0x7fa, 11) /* ''' */ -HPACK(40, 0x3fa, 10) /* '(' */ -HPACK(41, 0x3fb, 10) /* ')' */ -HPACK(42, 0xf9, 8) /* '*' */ -HPACK(43, 0x7fb, 11) /* '+' */ -HPACK(44, 0xfa, 8) /* ',' */ -HPACK(45, 0x16, 6) /* '-' */ -HPACK(46, 0x17, 6) /* '.' */ -HPACK(47, 0x18, 6) /* '/' */ -HPACK(48, 0x0, 5) /* '0' */ -HPACK(49, 0x1, 5) /* '1' */ -HPACK(50, 0x2, 5) /* '2' */ -HPACK(51, 0x19, 6) /* '3' */ -HPACK(52, 0x1a, 6) /* '4' */ -HPACK(53, 0x1b, 6) /* '5' */ -HPACK(54, 0x1c, 6) /* '6' */ -HPACK(55, 0x1d, 6) /* '7' */ -HPACK(56, 0x1e, 6) /* '8' */ -HPACK(57, 0x1f, 6) /* '9' */ -HPACK(58, 0x5c, 7) /* ':' */ -HPACK(59, 0xfb, 8) /* ';' */ -HPACK(60, 0x7ffc, 15) /* '<' */ -HPACK(61, 0x20, 6) /* '=' */ -HPACK(62, 0xffb, 12) /* '>' */ -HPACK(63, 0x3fc, 10) /* '?' */ -HPACK(64, 0x1ffa, 13) /* '@' */ -HPACK(65, 0x21, 6) /* 'A' */ -HPACK(66, 0x5d, 7) /* 'B' */ -HPACK(67, 0x5e, 7) /* 'C' */ -HPACK(68, 0x5f, 7) /* 'D' */ -HPACK(69, 0x60, 7) /* 'E' */ -HPACK(70, 0x61, 7) /* 'F' */ -HPACK(71, 0x62, 7) /* 'G' */ -HPACK(72, 0x63, 7) /* 'H' */ -HPACK(73, 0x64, 7) /* 'I' */ -HPACK(74, 0x65, 7) /* 'J' */ -HPACK(75, 0x66, 7) /* 'K' */ -HPACK(76, 0x67, 7) /* 'L' */ -HPACK(77, 0x68, 7) /* 'M' */ -HPACK(78, 0x69, 7) /* 'N' */ -HPACK(79, 0x6a, 7) /* 'O' */ -HPACK(80, 0x6b, 7) /* 'P' */ -HPACK(81, 0x6c, 7) /* 'Q' */ -HPACK(82, 0x6d, 7) /* 'R' */ -HPACK(83, 0x6e, 7) /* 'S' */ -HPACK(84, 0x6f, 7) /* 'T' */ -HPACK(85, 0x70, 7) /* 'U' */ -HPACK(86, 0x71, 7) /* 'V' */ -HPACK(87, 0x72, 7) /* 'W' */ -HPACK(88, 0xfc, 8) /* 'X' */ -HPACK(89, 0x73, 7) /* 'Y' */ -HPACK(90, 0xfd, 8) /* 'Z' */ -HPACK(91, 0x1ffb, 13) /* '[' */ -HPACK(92, 0x7fff0, 19) /* '\' */ -HPACK(93, 0x1ffc, 13) /* ']' */ -HPACK(94, 0x3ffc, 14) /* '^' */ -HPACK(95, 0x22, 6) /* '_' */ -HPACK(96, 0x7ffd, 15) /* '`' */ -HPACK(97, 0x3, 5) /* 'a' */ -HPACK(98, 0x23, 6) /* 'b' */ -HPACK(99, 0x4, 5) /* 'c' */ -HPACK(100, 0x24, 6) /* 'd' */ -HPACK(101, 0x5, 5) /* 'e' */ -HPACK(102, 0x25, 6) /* 'f' */ -HPACK(103, 0x26, 6) /* 'g' */ -HPACK(104, 0x27, 6) /* 'h' */ -HPACK(105, 0x6, 5) /* 'i' */ -HPACK(106, 0x74, 7) /* 'j' */ -HPACK(107, 0x75, 7) /* 'k' */ -HPACK(108, 0x28, 6) /* 'l' */ -HPACK(109, 0x29, 6) /* 'm' */ -HPACK(110, 0x2a, 6) /* 'n' */ -HPACK(111, 0x7, 5) /* 'o' */ -HPACK(112, 0x2b, 6) /* 'p' */ -HPACK(113, 0x76, 7) /* 'q' */ -HPACK(114, 0x2c, 6) /* 'r' */ -HPACK(115, 0x8, 5) /* 's' */ -HPACK(116, 0x9, 5) /* 't' */ -HPACK(117, 0x2d, 6) /* 'u' */ -HPACK(118, 0x77, 7) /* 'v' */ -HPACK(119, 0x78, 7) /* 'w' */ -HPACK(120, 0x79, 7) /* 'x' */ -HPACK(121, 0x7a, 7) /* 'y' */ -HPACK(122, 0x7b, 7) /* 'z' */ -HPACK(123, 0x7ffe, 15) /* '{' */ -HPACK(124, 0x7fc, 11) /* '|' */ -HPACK(125, 0x3ffd, 14) /* '}' */ -HPACK(126, 0x1ffd, 13) /* '~' */ -HPACK(127, 0xffffffc, 28) -HPACK(128, 0xfffe6, 20) -HPACK(129, 0x3fffd2, 22) -HPACK(130, 0xfffe7, 20) -HPACK(131, 0xfffe8, 20) -HPACK(132, 0x3fffd3, 22) -HPACK(133, 0x3fffd4, 22) -HPACK(134, 0x3fffd5, 22) -HPACK(135, 0x7fffd9, 23) -HPACK(136, 0x3fffd6, 22) -HPACK(137, 0x7fffda, 23) -HPACK(138, 0x7fffdb, 23) -HPACK(139, 0x7fffdc, 23) -HPACK(140, 0x7fffdd, 23) -HPACK(141, 0x7fffde, 23) -HPACK(142, 0xffffeb, 24) -HPACK(143, 0x7fffdf, 23) -HPACK(144, 0xffffec, 24) -HPACK(145, 0xffffed, 24) -HPACK(146, 0x3fffd7, 22) -HPACK(147, 0x7fffe0, 23) -HPACK(148, 0xffffee, 24) -HPACK(149, 0x7fffe1, 23) -HPACK(150, 0x7fffe2, 23) -HPACK(151, 0x7fffe3, 23) -HPACK(152, 0x7fffe4, 23) -HPACK(153, 0x1fffdc, 21) -HPACK(154, 0x3fffd8, 22) -HPACK(155, 0x7fffe5, 23) -HPACK(156, 0x3fffd9, 22) -HPACK(157, 0x7fffe6, 23) -HPACK(158, 0x7fffe7, 23) -HPACK(159, 0xffffef, 24) -HPACK(160, 0x3fffda, 22) -HPACK(161, 0x1fffdd, 21) -HPACK(162, 0xfffe9, 20) -HPACK(163, 0x3fffdb, 22) -HPACK(164, 0x3fffdc, 22) -HPACK(165, 0x7fffe8, 23) -HPACK(166, 0x7fffe9, 23) -HPACK(167, 0x1fffde, 21) -HPACK(168, 0x7fffea, 23) -HPACK(169, 0x3fffdd, 22) -HPACK(170, 0x3fffde, 22) -HPACK(171, 0xfffff0, 24) -HPACK(172, 0x1fffdf, 21) -HPACK(173, 0x3fffdf, 22) -HPACK(174, 0x7fffeb, 23) -HPACK(175, 0x7fffec, 23) -HPACK(176, 0x1fffe0, 21) -HPACK(177, 0x1fffe1, 21) -HPACK(178, 0x3fffe0, 22) -HPACK(179, 0x1fffe2, 21) -HPACK(180, 0x7fffed, 23) -HPACK(181, 0x3fffe1, 22) -HPACK(182, 0x7fffee, 23) -HPACK(183, 0x7fffef, 23) -HPACK(184, 0xfffea, 20) -HPACK(185, 0x3fffe2, 22) -HPACK(186, 0x3fffe3, 22) -HPACK(187, 0x3fffe4, 22) -HPACK(188, 0x7ffff0, 23) -HPACK(189, 0x3fffe5, 22) -HPACK(190, 0x3fffe6, 22) -HPACK(191, 0x7ffff1, 23) -HPACK(192, 0x3ffffe0, 26) -HPACK(193, 0x3ffffe1, 26) -HPACK(194, 0xfffeb, 20) -HPACK(195, 0x7fff1, 19) -HPACK(196, 0x3fffe7, 22) -HPACK(197, 0x7ffff2, 23) -HPACK(198, 0x3fffe8, 22) -HPACK(199, 0x1ffffec, 25) -HPACK(200, 0x3ffffe2, 26) -HPACK(201, 0x3ffffe3, 26) -HPACK(202, 0x3ffffe4, 26) -HPACK(203, 0x7ffffde, 27) -HPACK(204, 0x7ffffdf, 27) -HPACK(205, 0x3ffffe5, 26) -HPACK(206, 0xfffff1, 24) -HPACK(207, 0x1ffffed, 25) -HPACK(208, 0x7fff2, 19) -HPACK(209, 0x1fffe3, 21) -HPACK(210, 0x3ffffe6, 26) -HPACK(211, 0x7ffffe0, 27) -HPACK(212, 0x7ffffe1, 27) -HPACK(213, 0x3ffffe7, 26) -HPACK(214, 0x7ffffe2, 27) -HPACK(215, 0xfffff2, 24) -HPACK(216, 0x1fffe4, 21) -HPACK(217, 0x1fffe5, 21) -HPACK(218, 0x3ffffe8, 26) -HPACK(219, 0x3ffffe9, 26) -HPACK(220, 0xffffffd, 28) -HPACK(221, 0x7ffffe3, 27) -HPACK(222, 0x7ffffe4, 27) -HPACK(223, 0x7ffffe5, 27) -HPACK(224, 0xfffec, 20) -HPACK(225, 0xfffff3, 24) -HPACK(226, 0xfffed, 20) -HPACK(227, 0x1fffe6, 21) -HPACK(228, 0x3fffe9, 22) -HPACK(229, 0x1fffe7, 21) -HPACK(230, 0x1fffe8, 21) -HPACK(231, 0x7ffff3, 23) -HPACK(232, 0x3fffea, 22) -HPACK(233, 0x3fffeb, 22) -HPACK(234, 0x1ffffee, 25) -HPACK(235, 0x1ffffef, 25) -HPACK(236, 0xfffff4, 24) -HPACK(237, 0xfffff5, 24) -HPACK(238, 0x3ffffea, 26) -HPACK(239, 0x7ffff4, 23) -HPACK(240, 0x3ffffeb, 26) -HPACK(241, 0x7ffffe6, 27) -HPACK(242, 0x3ffffec, 26) -HPACK(243, 0x3ffffed, 26) -HPACK(244, 0x7ffffe7, 27) -HPACK(245, 0x7ffffe8, 27) -HPACK(246, 0x7ffffe9, 27) -HPACK(247, 0x7ffffea, 27) -HPACK(248, 0x7ffffeb, 27) -HPACK(249, 0xffffffe, 28) -HPACK(250, 0x7ffffec, 27) -HPACK(251, 0x7ffffed, 27) -HPACK(252, 0x7ffffee, 27) -HPACK(253, 0x7ffffef, 27) -HPACK(254, 0x7fffff0, 27) -HPACK(255, 0x3ffffee, 26) -HPACK(0, 0x3fffffff, 30) diff --git a/bin/varnishtest/vtc_h2_hpack.c b/bin/varnishtest/vtc_h2_hpack.c deleted file mode 100644 index d4580196a..000000000 --- a/bin/varnishtest/vtc_h2_hpack.c +++ /dev/null @@ -1,442 +0,0 @@ -/*- - * Copyright (c) 2008-2016 Varnish Software AS - * All rights reserved. - * - * Author: Guillaume Quintard - * - * SPDX-License-Identifier: BSD-2-Clause - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#include -#include -#include -#include - - -#include "vdef.h" - -#include "vas.h" -#include "vqueue.h" - -#include "hpack.h" -#include "vtc_h2_priv.h" - -struct symbol { - uint32_t val; - uint8_t size; -}; - -static const struct symbol coding_table[] = { -#define HPACK(i, v, l) {v, l}, -#include "vtc_h2_enctbl.h" -#undef HPACK - {0, 0} -}; - -#include "vtc_h2_dectbl.h" - -#define MASK(pack, n) (pack >> (64 - n)) -static int -huff_decode(char *str, int nm, struct hpk_iter *iter, int ilen) -{ - int l = 0; - uint64_t pack = 0; - unsigned pl = 0; /* pack length*/ - struct stbl *tbl = &tbl_0; - struct ssym *sym; - - (void)nm; - while (ilen > 0 || pl != 0) { - /* make sure we have enough data*/ - if (pl < tbl->msk) { - if (ilen == 0) { - if (pl == 0 || (MASK(pack, pl) == - (unsigned)((1U << pl) - 1U))) { - assert(tbl == &tbl_0); - return (l); - } - } - /* fit as many bytes as we can in pack */ - while (pl <= 56 && ilen > 0) { - pack |= (uint64_t)(*iter->buf & 0xff) - << (56 - pl); - pl += 8; - iter->buf++; - ilen--; - } - } - assert(tbl); - assert(tbl->msk); - sym = &tbl->syms[MASK(pack, tbl->msk)]; - - assert(sym->csm <= tbl->msk); - - if (sym->csm == 0 || pl < sym->csm) - return (0); - - assert(sym->csm <= 8); - pack <<= sym->csm; - assert(sym->csm <= pl); - pl -= sym->csm; - if (sym->nxt) { - tbl = sym->nxt; - continue; - } - str[l++] = sym->chr; - tbl = &tbl_0; - } - return (l); -} - -/* inspired from Dridi Boukelmoune's cashpack. */ -static enum hpk_result -huff_encode(struct hpk_iter *iter, const char *str, int len) -{ - uint64_t pack = 0; - int pl = 0; /* pack length*/ - uint32_t v; - uint8_t s; - - assert(iter->buf < iter->end); - - while (len--) { - v = coding_table[(uint8_t)*str].val; - s = coding_table[(uint8_t)*str].size; - - pl += s; - pack |= (uint64_t)v << (64 - pl); - - while (pl >= 8) { - if (iter->buf == iter->end) - return (hpk_done); - *iter->buf = (char)(pack >> 56); - iter->buf++; - pack <<= 8; - pl -= 8; - } - str++; - } - - /* padding */ - if (pl) { - assert(pl < 8); - if (iter->buf == iter->end) - return (hpk_done); - pl += 8; - pack |= (uint64_t)0xff << (64 - pl); - *iter->buf = (char)(pack >> 56); - iter->buf++; - } - - return (hpk_more); -} - -static int -huff_simulate(const char *str, int ilen, int huff) -{ - int olen = 0; - if (!huff || !ilen) - return (ilen); - while (ilen--) { - olen += coding_table[(unsigned char)*str].size; - str++; - } - return ((olen + 7) / 8); -} - -static enum hpk_result -num_decode(uint32_t *result, struct hpk_iter *iter, uint8_t prefix) -{ - uint8_t shift = 0; - - assert(iter->buf < iter->end); - assert(prefix); - assert(prefix <= 8); - - *result = 0; - *result = *iter->buf & (0xff >> (8-prefix)); - if (*result < (1U << prefix) - 1U) { - iter->buf++; - return (ITER_DONE(iter)); - } - do { - iter->buf++; - if (iter->end == iter->buf) - return (hpk_err); - /* check for overflow */ - if ((UINT32_MAX - *result) >> shift < (*iter->buf & 0x7f)) - return (hpk_err); - - *result += (uint32_t)(*iter->buf & 0x7f) << shift; - shift += 7; - } while (*iter->buf & 0x80); - iter->buf++; - - return (ITER_DONE(iter)); -} - -static enum hpk_result -num_encode(struct hpk_iter *iter, uint8_t prefix, uint32_t num) -{ - assert(prefix); - assert(prefix <= 8); - assert(iter->buf < iter->end); - - uint8_t pmax = (1U << prefix) - 1U; - - *iter->buf &= 0xffU << prefix; - if (num <= pmax) { - *iter->buf++ |= num; - return (ITER_DONE(iter)); - } else if (iter->end - iter->buf < 2) - return (hpk_err); - - iter->buf[0] |= pmax; - num -= pmax; - do { - iter->buf++; - if (iter->end == iter->buf) - return (hpk_err); - *iter->buf = num % 128; - *iter->buf |= 0x80; - num /= 128; - } while (num); - *iter->buf++ &= 127; - return (ITER_DONE(iter)); -} - -static enum hpk_result -str_encode(struct hpk_iter *iter, const struct hpk_txt *t) -{ - int slen = huff_simulate(t->ptr, t->len, t->huff); - assert(iter->buf < iter->end); - if (t->huff) - *iter->buf = 0x80; - else - *iter->buf = 0; - - if (hpk_err == num_encode(iter, 7, slen)) - return (hpk_err); - - if (slen > iter->end - iter->buf) - return (hpk_err); - - if (t->huff) { - return (huff_encode(iter, t->ptr, t->len)); - } else { - memcpy(iter->buf, t->ptr, slen); - iter->buf += slen; - return (ITER_DONE(iter)); - } -} - -static enum hpk_result -str_decode(struct hpk_iter *iter, struct hpk_txt *t) -{ - uint32_t num; - int huff; - - assert(iter->buf < iter->end); - - huff = (*iter->buf & 0x80); - if (hpk_more != num_decode(&num, iter, 7)) - return (hpk_err); - assert(iter->buf < iter->end); - if (num > (unsigned)(iter->end - iter->buf)) - return (hpk_err); - if (huff) { /*Huffman encoding */ - t->ptr = malloc((num * 8L) / 5L + 1L); - AN(t->ptr); - num = huff_decode(t->ptr, (num * 8) / 5, iter, num); - if (!num) { - free(t->ptr); - return (hpk_err); - } - t->huff = 1; - /* XXX: do we care? */ - t->ptr = realloc(t->ptr, num + 1L); - AN(t->ptr); - } else { /* literal string */ - t->huff = 0; - t->ptr = malloc(num + 1L); - AN(t->ptr); - memcpy(t->ptr, iter->buf, num); - iter->buf += num; - } - - t->ptr[num] = '\0'; - t->len = num; - - return (ITER_DONE(iter)); -} - -static inline void -txtcpy(struct hpk_txt *to, const struct hpk_txt *from) -{ - //AZ(to->ptr); - to->ptr = malloc(from->len + 1L); - AN(to->ptr); - memcpy(to->ptr, from->ptr, from->len + 1L); - to->len = from->len; -} - -int -gethpk_iterLen(const struct hpk_iter *iter) -{ - return (iter->buf - iter->orig); -} - -enum hpk_result -HPK_DecHdr(struct hpk_iter *iter, struct hpk_hdr *header) -{ - int pref = 0; - const struct hpk_txt *t; - uint32_t num; - int must_index = 0; - assert(iter); - assert(iter->buf < iter->end); - /* Indexed Header Field */ - if (*iter->buf & 128) { - header->t = hpk_idx; - if (hpk_err == num_decode(&num, iter, 7)) - return (hpk_err); - - if (num) { /* indexed key and value*/ - t = tbl_get_key(iter->ctx, num); - if (!t) - return (hpk_err); - txtcpy(&header->key, t); - - t = tbl_get_value(iter->ctx, num); - if (!t) { - free(header->key.ptr); - return (hpk_err); - } - - txtcpy(&header->value, t); - - if (iter->buf < iter->end) - return (hpk_more); - else - return (hpk_done); - } else - return (hpk_err); - - } - /* Literal Header Field with Incremental Indexing */ - else if (*iter->buf >> 6 == 1) { - header->t = hpk_inc; - pref = 6; - must_index = 1; - } - /* Literal Header Field without Indexing */ - else if (*iter->buf >> 4 == 0) { - header->t = hpk_not; - pref = 4; - } - /* Literal Header Field never Indexed */ - else if (*iter->buf >> 4 == 1) { - header->t = hpk_never; - pref = 4; - } - /* Dynamic Table Size Update */ - /* XXX if under max allowed value */ - else if (*iter->buf >> 5 == 1) { - if (hpk_done != num_decode(&num, iter, 5)) - return (hpk_err); - return (HPK_ResizeTbl(iter->ctx, num)); - } else { - return (hpk_err); - } - - assert(pref); - if (hpk_more != num_decode(&num, iter, pref)) - return (hpk_err); - - header->i = num; - if (num) { /* indexed key */ - t = tbl_get_key(iter->ctx, num); - if (!t) - return (hpk_err); - txtcpy(&header->key, t); - } else { - if (hpk_more != str_decode(iter, &header->key)) { - free(header->key.ptr); - return (hpk_err); - } - } - - if (hpk_err == str_decode(iter, &header->value)) { - free(header->key.ptr); - return (hpk_err); - } - - if (must_index) - push_header(iter->ctx, header); - return (ITER_DONE(iter)); -} - -enum hpk_result -HPK_EncHdr(struct hpk_iter *iter, const struct hpk_hdr *h) -{ - int pref; - int must_index = 0; - enum hpk_result ret; - switch (h->t) { - case hpk_idx: - *iter->buf = 0x80; - assert(num_encode(iter, 7, h->i) != hpk_err); - return (ITER_DONE(iter)); - case hpk_inc: - *iter->buf = 0x40; - pref = 6; - must_index = 1; - break; - case hpk_not: - *iter->buf = 0x00; - pref = 4; - break; - case hpk_never: - *iter->buf = 0x10; - pref = 4; - break; - default: - INCOMPL(); - } - if (h->i) { - if (hpk_more != num_encode(iter, pref, h->i)) - return (hpk_err); - } else { - iter->buf++; - if (hpk_more != str_encode(iter, &h->key)) - return (hpk_err); - } - ret = str_encode(iter, &h->value); - if (ret == hpk_err) - return (hpk_err); - if (must_index) - push_header(iter->ctx, h); - return (ret); - -} diff --git a/bin/varnishtest/vtc_h2_priv.h b/bin/varnishtest/vtc_h2_priv.h deleted file mode 100644 index 48221f86e..000000000 --- a/bin/varnishtest/vtc_h2_priv.h +++ /dev/null @@ -1,50 +0,0 @@ -/*- - * Copyright (c) 2008-2016 Varnish Software AS - * All rights reserved. - * - * Author: Guillaume Quintard - * - * SPDX-License-Identifier: BSD-2-Clause - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#define ITER_DONE(iter) (iter->buf == iter->end ? hpk_done : hpk_more) - -struct dynhdr { - struct hpk_hdr header; - VTAILQ_ENTRY(dynhdr) list; -}; - -VTAILQ_HEAD(dynamic_table,dynhdr); - -struct hpk_iter { - struct hpk_ctx *ctx; - uint8_t *orig; - uint8_t *buf; - uint8_t *end; -}; - -const struct hpk_txt * tbl_get_key(const struct hpk_ctx *ctx, uint32_t index); - -const struct hpk_txt * tbl_get_value(const struct hpk_ctx *ctx, uint32_t index); -void push_header (struct hpk_ctx *ctx, const struct hpk_hdr *h); diff --git a/bin/varnishtest/vtc_h2_stattbl.h b/bin/varnishtest/vtc_h2_stattbl.h deleted file mode 100644 index a8b325856..000000000 --- a/bin/varnishtest/vtc_h2_stattbl.h +++ /dev/null @@ -1,65 +0,0 @@ -/*- - * For Copyright information see RFC7541 [BSD3] - */ - -STAT_HDRS(1, ":authority", "") -STAT_HDRS(2, ":method", "GET") -STAT_HDRS(3, ":method", "POST") -STAT_HDRS(4, ":path", "/") -STAT_HDRS(5, ":path", "/index.html") -STAT_HDRS(6, ":scheme", "http") -STAT_HDRS(7, ":scheme", "https") -STAT_HDRS(8, ":status", "200") -STAT_HDRS(9, ":status", "204") -STAT_HDRS(10, ":status", "206") -STAT_HDRS(11, ":status", "304") -STAT_HDRS(12, ":status", "400") -STAT_HDRS(13, ":status", "404") -STAT_HDRS(14, ":status", "500") -STAT_HDRS(15, "accept-charset", "") -STAT_HDRS(16, "accept-encoding", "gzip,deflate") -STAT_HDRS(17, "accept-language", "") -STAT_HDRS(18, "accept-ranges", "") -STAT_HDRS(19, "accept", "") -STAT_HDRS(20, "access-control-allow-origin", "") -STAT_HDRS(21, "age", "") -STAT_HDRS(22, "allow", "") -STAT_HDRS(23, "authorization", "") -STAT_HDRS(24, "cache-control", "") -STAT_HDRS(25, "content-disposition", "") -STAT_HDRS(26, "content-encoding", "") -STAT_HDRS(27, "content-language", "") -STAT_HDRS(28, "content-length", "") -STAT_HDRS(29, "content-location", "") -STAT_HDRS(30, "content-range", "") -STAT_HDRS(31, "content-type", "") -STAT_HDRS(32, "cookie", "") -STAT_HDRS(33, "date", "") -STAT_HDRS(34, "etag", "") -STAT_HDRS(35, "expect", "") -STAT_HDRS(36, "expires", "") -STAT_HDRS(37, "from", "") -STAT_HDRS(38, "host", "") -STAT_HDRS(39, "if-match", "") -STAT_HDRS(40, "if-modified-since", "") -STAT_HDRS(41, "if-none-match", "") -STAT_HDRS(42, "if-range", "") -STAT_HDRS(43, "if-unmodified-since", "") -STAT_HDRS(44, "last-modified", "") -STAT_HDRS(45, "link", "") -STAT_HDRS(46, "location", "") -STAT_HDRS(47, "max-forwards", "") -STAT_HDRS(48, "proxy-authenticate", "") -STAT_HDRS(49, "proxy-authorization", "") -STAT_HDRS(50, "range", "") -STAT_HDRS(51, "referer", "") -STAT_HDRS(52, "refresh", "") -STAT_HDRS(53, "retry-after", "") -STAT_HDRS(54, "server", "") -STAT_HDRS(55, "set-cookie", "") -STAT_HDRS(56, "strict-transport-security", "") -STAT_HDRS(57, "transfer-encoding", "") -STAT_HDRS(58, "user-agent", "") -STAT_HDRS(59, "vary", "") -STAT_HDRS(60, "via", "") -STAT_HDRS(61, "www-authenticate", "") diff --git a/bin/varnishtest/vtc_h2_tbl.c b/bin/varnishtest/vtc_h2_tbl.c deleted file mode 100644 index 658e9bc4b..000000000 --- a/bin/varnishtest/vtc_h2_tbl.c +++ /dev/null @@ -1,298 +0,0 @@ -/*- - * Copyright (c) 2008-2016 Varnish Software AS - * All rights reserved. - * - * Author: Guillaume Quintard - * - * SPDX-License-Identifier: BSD-2-Clause - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#include -#include -#include -#include - -#include "vdef.h" - -#include "vas.h" -#include "vqueue.h" - -#include "hpack.h" -#include "vtc_h2_priv.h" - -/* TODO: fix that crazy workaround */ -#define STAT_HDRS(i, k, v) \ - static char key_ ## i[] = k; \ - static char value_ ## i[] = v; -#include "vtc_h2_stattbl.h" -#undef STAT_HDRS - -/*lint -save -e778 */ -static const struct hpk_hdr sttbl[] = { - {{NULL, 0, 0}, {NULL, 0, 0}, hpk_idx, 0}, -#define STAT_HDRS(j, k, v) \ -{ \ - .key = { \ - .ptr = key_ ## j, \ - .len = sizeof(k) - 1, \ - .huff = 0 \ - }, \ - .value = { \ - .ptr = value_ ## j, \ - .len = sizeof(v) - 1, \ - .huff = 0 \ - }, \ - .t = hpk_idx, \ - .i = j, \ -}, -#include "vtc_h2_stattbl.h" -#undef STAT_HDRS -}; -/*lint -restore */ - -struct hpk_ctx { - const struct hpk_hdr *sttbl; - struct dynamic_table dyntbl; - uint32_t maxsize; - uint32_t size; -}; - - -struct hpk_iter * -HPK_NewIter(struct hpk_ctx *ctx, void *buf, int size) -{ - struct hpk_iter *iter = malloc(sizeof(*iter)); - assert(iter); - assert(ctx); - assert(buf); - assert(size); - iter->ctx = ctx; - iter->orig = buf; - iter->buf = buf; - iter->end = iter->buf + size; - return (iter); -} - -void -HPK_FreeIter(struct hpk_iter *iter) -{ - free(iter); -} - -static void -pop_header(struct hpk_ctx *ctx) -{ - assert(!VTAILQ_EMPTY(&ctx->dyntbl)); - struct dynhdr *h = VTAILQ_LAST(&ctx->dyntbl, dynamic_table); - VTAILQ_REMOVE(&ctx->dyntbl, h, list); - ctx->size -= h->header.key.len + h->header.value.len + 32; - free(h->header.key.ptr); - free(h->header.value.ptr); - free(h); -} - -void -push_header (struct hpk_ctx *ctx, const struct hpk_hdr *oh) -{ - const struct hpk_hdr *ih; - struct dynhdr *h; - uint32_t len; - - assert(ctx->size <= ctx->maxsize); - AN(oh); - - if (!ctx->maxsize) - return; - len = oh->value.len + 32; - if (oh->key.ptr) - len += oh->key.len; - else { - AN(oh->i); - ih = HPK_GetHdr(ctx, oh->i); - AN(ih); - len += ih->key.len; - } - - while (!VTAILQ_EMPTY(&ctx->dyntbl) && ctx->maxsize - ctx->size < len) - pop_header(ctx); - if (ctx->maxsize - ctx->size >= len) { - h = malloc(sizeof(*h)); - AN(h); - h->header.t = hpk_idx; - - if (oh->key.ptr) { - h->header.key.len = oh->key.len; - h->header.key.ptr = malloc(oh->key.len + 1L); - AN(h->header.key.ptr); - memcpy(h->header.key.ptr, - oh->key.ptr, oh->key.len + 1L); - } else { - AN(oh->i); - ih = HPK_GetHdr(ctx, oh->i); - AN(ih); - - h->header.key.len = ih->key.len; - h->header.key.ptr = malloc(ih->key.len + 1L); - AN(h->header.key.ptr); - memcpy(h->header.key.ptr, - ih->key.ptr, ih->key.len + 1L); - } - - h->header.value.len = oh->value.len; - h->header.value.ptr = malloc(oh->value.len + 1L); - AN(h->header.value.ptr); - memcpy(h->header.value.ptr, oh->value.ptr, oh->value.len + 1L); - - VTAILQ_INSERT_HEAD(&ctx->dyntbl, h, list); - ctx->size += len; - } - -} - -enum hpk_result -HPK_ResizeTbl(struct hpk_ctx *ctx, uint32_t num) -{ - ctx->maxsize = num; - while (!VTAILQ_EMPTY(&ctx->dyntbl) && ctx->maxsize < ctx->size) - pop_header(ctx); - return (hpk_done); -} - -static const struct hpk_txt * -tbl_get_field(const struct hpk_ctx *ctx, uint32_t idx, int key) -{ - struct dynhdr *dh; - assert(ctx); - if (idx > 61 + ctx->size) - return (NULL); - else if (idx <= 61) { - if (key) - return (&ctx->sttbl[idx].key); - else - return (&ctx->sttbl[idx].value); - } - - idx -= 62; - VTAILQ_FOREACH(dh, &ctx->dyntbl, list) - if (!idx--) - break; - if (idx && dh) { - if (key) - return (&dh->header.key); - else - return (&dh->header.value); - } else - return (NULL); -} - -const struct hpk_txt * -tbl_get_key(const struct hpk_ctx *ctx, uint32_t idx) -{ - return (tbl_get_field(ctx, idx, 1)); -} - -const struct hpk_txt * -tbl_get_value(const struct hpk_ctx *ctx, uint32_t idx) -{ - return (tbl_get_field(ctx, idx, 0)); -} - -const struct hpk_hdr * -HPK_GetHdr(const struct hpk_ctx *ctx, uint32_t idx) -{ - uint32_t oi = idx; - struct dynhdr *dh; - assert(ctx); - if (idx > 61 + ctx->size) - return (NULL); - else if (idx <= 61) - return (&ctx->sttbl[idx]); - - idx -= 62; - VTAILQ_FOREACH(dh, &ctx->dyntbl, list) - if (!idx--) - break; - if (idx && dh) { - dh->header.i = oi; - return (&dh->header); - } else - return (NULL); -} - -uint32_t -HPK_GetTblSize(const struct hpk_ctx *ctx) -{ - return (ctx->size); -} - -uint32_t -HPK_GetTblMaxSize(const struct hpk_ctx *ctx) -{ - return (ctx->maxsize); -} - -uint32_t -HPK_GetTblLength(const struct hpk_ctx *ctx) -{ - struct dynhdr *dh; - uint32_t l = 0; - VTAILQ_FOREACH(dh, &ctx->dyntbl, list) - l++; - return (l); -} - -#if 0 -void -dump_dyn_tbl(const struct hpk_ctx *ctx) -{ - int i = 0; - struct dynhdr *dh; - printf("DUMPING %u/%u\n", ctx->size, ctx->maxsize); - VTAILQ_FOREACH(dh, &ctx->dyntbl, list) { - printf(" (%d) %s: %s\n", - i++, dh->header.key.ptr, dh->header.value.ptr); - } - printf("DONE\n"); -} -#endif - -struct hpk_ctx * -HPK_NewCtx(uint32_t maxsize) -{ - struct hpk_ctx *ctx = calloc(1, sizeof(*ctx)); - assert(ctx); - ctx->sttbl = sttbl; - ctx->maxsize = maxsize; - ctx->size = 0; - return (ctx); -} - -void -HPK_FreeCtx(struct hpk_ctx *ctx) -{ - - while (!VTAILQ_EMPTY(&ctx->dyntbl)) - pop_header(ctx); - free(ctx); -} diff --git a/bin/varnishtest/vtc_haproxy.c b/bin/varnishtest/vtc_haproxy.c deleted file mode 100644 index 432493ae6..000000000 --- a/bin/varnishtest/vtc_haproxy.c +++ /dev/null @@ -1,1159 +0,0 @@ -/*- - * Copyright (c) 2008-2018 Varnish Software AS - * All rights reserved. - * - * Author: Fr?d?ric L?caille - * - * SPDX-License-Identifier: BSD-2-Clause - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#include "config.h" - -#include -#include -#include -#include -#include /* for MUSL (mode_t) */ -#include -#include -#include - -#include "vtc.h" - -#include "vfil.h" -#include "vpf.h" -#include "vre.h" -#include "vtcp.h" -#include "vsa.h" -#include "vtim.h" - -#define HAPROXY_PROGRAM_ENV_VAR "HAPROXY_PROGRAM" -#define HAPROXY_ARGS_ENV_VAR "HAPROXY_ARGS" -#define HAPROXY_OPT_WORKER "-W" -#define HAPROXY_OPT_MCLI "-S" -#define HAPROXY_OPT_DAEMON "-D" -#define HAPROXY_SIGNAL SIGINT -#define HAPROXY_EXPECT_EXIT (128 + HAPROXY_SIGNAL) - -struct envar { - VTAILQ_ENTRY(envar) list; - char *name; - char *value; -}; - -struct haproxy { - unsigned magic; -#define HAPROXY_MAGIC 0x8a45cf75 - char *name; - struct vtclog *vl; - VTAILQ_ENTRY(haproxy) list; - - const char *filename; - struct vsb *args; - int opt_worker; - int opt_mcli; - int opt_daemon; - int opt_check_mode; - char *pid_fn; - pid_t pid; - pid_t ppid; - int fds[4]; - char *cfg_fn; - struct vsb *cfg_vsb; - - pthread_t tp; - int expect_exit; - int expect_signal; - int its_dead_jim; - - /* UNIX socket CLI. */ - char *cli_fn; - /* TCP socket CLI. */ - struct haproxy_cli *cli; - - /* master CLI */ - struct haproxy_cli *mcli; - - char *workdir; - struct vsb *msgs; - char closed_sock[256]; /* Closed TCP socket */ - VTAILQ_HEAD(,envar) envars; -}; - -static VTAILQ_HEAD(, haproxy) haproxies = - VTAILQ_HEAD_INITIALIZER(haproxies); - -struct haproxy_cli { - unsigned magic; -#define HAPROXY_CLI_MAGIC 0xb09a4ed8 - struct vtclog *vl; - char running; - - char *spec; - - int sock; - char connect[256]; - - pthread_t tp; - size_t txbuf_sz; - char *txbuf; - size_t rxbuf_sz; - char *rxbuf; - - double timeout; -}; - -static void haproxy_write_conf(struct haproxy *h); - -static void -haproxy_add_envar(struct haproxy *h, - const char *name, const char *value) -{ - struct envar *e; - - e = malloc(sizeof *e); - AN(e); - e->name = strdup(name); - e->value = strdup(value); - AN(e->name); - AN(e->value); - VTAILQ_INSERT_TAIL(&h->envars, e, list); -} - -static void -haproxy_delete_envars(struct haproxy *h) -{ - struct envar *e, *e2; - VTAILQ_FOREACH_SAFE(e, &h->envars, list, e2) { - VTAILQ_REMOVE(&h->envars, e, list); - free(e->name); - free(e->value); - free(e); - } -} - -static void -haproxy_build_env(const struct haproxy *h) -{ - struct envar *e; - - VTAILQ_FOREACH(e, &h->envars, list) { - if (setenv(e->name, e->value, 0) == -1) - vtc_fatal(h->vl, "setenv() failed: %s (%d)", - strerror(errno), errno); - } -} - -/********************************************************************** - * Socket connect (same as client_tcp_connect()). - */ - -static int -haproxy_cli_tcp_connect(struct vtclog *vl, const char *addr, double tmo, - const char **errp) -{ - int fd; - char mabuf[VTCP_ADDRBUFSIZE], mpbuf[VTCP_PORTBUFSIZE]; - - AN(addr); - AN(errp); - fd = VTCP_open(addr, NULL, tmo, errp); - if (fd < 0) - return (fd); - VTCP_myname(fd, mabuf, sizeof mabuf, mpbuf, sizeof mpbuf); - vtc_log(vl, 3, - "CLI connected fd %d from %s %s to %s", fd, mabuf, mpbuf, addr); - return (fd); -} - -/* - * SECTION: haproxy.cli haproxy CLI Specification - * SECTION: haproxy.cli.send - * send STRING - * Push STRING on the CLI connection. STRING will be terminated by an - * end of line character (\n). - */ -static void v_matchproto_(cmd_f) -cmd_haproxy_cli_send(CMD_ARGS) -{ - struct vsb *vsb; - struct haproxy_cli *hc; - int j; - - (void)vl; - CAST_OBJ_NOTNULL(hc, priv, HAPROXY_CLI_MAGIC); - AZ(strcmp(av[0], "send")); - AN(av[1]); - AZ(av[2]); - - vsb = VSB_new_auto(); - AN(vsb); - AZ(VSB_cat(vsb, av[1])); - AZ(VSB_cat(vsb, "\n")); - AZ(VSB_finish(vsb)); - if (hc->sock == -1) { - int fd; - const char *err; - struct vsb *vsb_connect; - - vsb_connect = macro_expand(hc->vl, hc->connect); - AN(vsb_connect); - fd = haproxy_cli_tcp_connect(hc->vl, - VSB_data(vsb_connect), 10., &err); - if (fd < 0) - vtc_fatal(hc->vl, - "CLI failed to open %s: %s", VSB_data(vsb), err); - VSB_destroy(&vsb_connect); - hc->sock = fd; - } - vtc_dump(hc->vl, 4, "CLI send", VSB_data(vsb), -1); - - if (VSB_tofile(vsb, hc->sock)) - vtc_fatal(hc->vl, - "CLI fd %d send error %s", hc->sock, strerror(errno)); - - /* a CLI command must be followed by a SHUT_WR if we want HAProxy to - * close after the response */ - j = shutdown(hc->sock, SHUT_WR); - vtc_log(hc->vl, 3, "CLI shutting fd %d", hc->sock); - if (!VTCP_Check(j)) - vtc_fatal(hc->vl, "Shutdown failed: %s", strerror(errno)); - - VSB_destroy(&vsb); -} - -#define HAPROXY_CLI_RECV_LEN (1 << 14) -static void -haproxy_cli_recv(struct haproxy_cli *hc) -{ - ssize_t ret; - size_t rdz, left, off; - - rdz = ret = off = 0; - /* We want to null terminate this buffer. */ - left = hc->rxbuf_sz - 1; - while (!vtc_error && left > 0) { - VTCP_set_read_timeout(hc->sock, hc->timeout); - - ret = recv(hc->sock, hc->rxbuf + off, HAPROXY_CLI_RECV_LEN, 0); - if (ret < 0) { - if (errno == EINTR || errno == EAGAIN) - continue; - - vtc_fatal(hc->vl, - "CLI fd %d recv() failed (%s)", - hc->sock, strerror(errno)); - } - /* Connection closed. */ - if (ret == 0) { - if (rdz > 0 && hc->rxbuf[rdz - 1] != '\n') - vtc_fatal(hc->vl, - "CLI rx timeout (fd: %d %.3fs ret: %zd)", - hc->sock, hc->timeout, ret); - - vtc_log(hc->vl, 4, "CLI connection normally closed"); - vtc_log(hc->vl, 3, "CLI closing fd %d", hc->sock); - VTCP_close(&hc->sock); - break; - } - - rdz += ret; - left -= ret; - off += ret; - } - hc->rxbuf[rdz] = '\0'; - vtc_dump(hc->vl, 4, "CLI recv", hc->rxbuf, rdz); -} - -/* - * SECTION: haproxy.cli.expect - * expect OP STRING - * Regex match the CLI reception buffer with STRING - * if OP is ~ or, on the contrary, if OP is !~ check that there is - * no regex match. - */ -static void v_matchproto_(cmd_f) -cmd_haproxy_cli_expect(CMD_ARGS) -{ - struct haproxy_cli *hc; - struct vsb vsb[1]; - vre_t *vre; - int error, erroroffset, i, ret; - char *cmp, *spec, errbuf[VRE_ERROR_LEN]; - - (void)vl; - CAST_OBJ_NOTNULL(hc, priv, HAPROXY_CLI_MAGIC); - AZ(strcmp(av[0], "expect")); - av++; - - cmp = av[0]; - spec = av[1]; - AN(cmp); - AN(spec); - AZ(av[2]); - - assert(!strcmp(cmp, "~") || !strcmp(cmp, "!~")); - - haproxy_cli_recv(hc); - - vre = VRE_compile(spec, 0, &error, &erroroffset, 1); - if (vre == NULL) { - AN(VSB_init(vsb, errbuf, sizeof errbuf)); - AZ(VRE_error(vsb, error)); - AZ(VSB_finish(vsb)); - VSB_fini(vsb); - vtc_fatal(hc->vl, "CLI regexp error: '%s' (@%d) (%s)", - errbuf, erroroffset, spec); - } - - i = VRE_match(vre, hc->rxbuf, 0, 0, NULL); - - VRE_free(&vre); - - ret = (i >= 0 && *cmp == '~') || (i < 0 && *cmp == '!'); - if (!ret) - vtc_fatal(hc->vl, "CLI expect failed %s \"%s\"", cmp, spec); - else - vtc_log(hc->vl, 4, "CLI expect match %s \"%s\"", cmp, spec); -} - -static const struct cmds haproxy_cli_cmds[] = { -#define CMD_HAPROXY_CLI(n) { #n, cmd_haproxy_cli_##n }, - CMD_HAPROXY_CLI(send) - CMD_HAPROXY_CLI(expect) -#undef CMD_HAPROXY_CLI - { NULL, NULL } -}; - -/********************************************************************** - * HAProxy CLI client thread - */ - -static void * -haproxy_cli_thread(void *priv) -{ - struct haproxy_cli *hc; - struct vsb *vsb; - int fd; - const char *err; - - CAST_OBJ_NOTNULL(hc, priv, HAPROXY_CLI_MAGIC); - AN(*hc->connect); - - vsb = macro_expand(hc->vl, hc->connect); - AN(vsb); - - fd = haproxy_cli_tcp_connect(hc->vl, VSB_data(vsb), 10., &err); - if (fd < 0) - vtc_fatal(hc->vl, - "CLI failed to open %s: %s", VSB_data(vsb), err); - VTCP_blocking(fd); - hc->sock = fd; - parse_string(hc->vl, hc, hc->spec); - vtc_log(hc->vl, 2, "CLI ending"); - VSB_destroy(&vsb); - return (NULL); -} - -/********************************************************************** - * Wait for the CLI client thread to stop - */ - -static void -haproxy_cli_wait(struct haproxy_cli *hc) -{ - void *res; - - CHECK_OBJ_NOTNULL(hc, HAPROXY_CLI_MAGIC); - vtc_log(hc->vl, 2, "CLI waiting"); - PTOK(pthread_join(hc->tp, &res)); - if (res != NULL) - vtc_fatal(hc->vl, "CLI returned \"%s\"", (char *)res); - REPLACE(hc->spec, NULL); - hc->tp = 0; - hc->running = 0; -} - -/********************************************************************** - * Start the CLI client thread - */ - -static void -haproxy_cli_start(struct haproxy_cli *hc) -{ - CHECK_OBJ_NOTNULL(hc, HAPROXY_CLI_MAGIC); - vtc_log(hc->vl, 2, "CLI starting"); - PTOK(pthread_create(&hc->tp, NULL, haproxy_cli_thread, hc)); - hc->running = 1; - -} - -/********************************************************************** - * Run the CLI client thread - */ - -static void -haproxy_cli_run(struct haproxy_cli *hc) -{ - haproxy_cli_start(hc); - haproxy_cli_wait(hc); -} - -/********************************************************************** - * - */ - -static void -haproxy_wait_pidfile(struct haproxy *h) -{ - char buf_err[1024] = {0}; - int usleep_time = 1000; - double t0; - pid_t pid; - - vtc_log(h->vl, 3, "wait-pid-file"); - for (t0 = VTIM_mono(); VTIM_mono() - t0 < 3;) { - if (vtc_error) - return; - - if (VPF_Read(h->pid_fn, &pid) != 0) { - bprintf(buf_err, - "Could not read PID file '%s'", h->pid_fn); - usleep(usleep_time); - continue; - } - - if (!h->opt_daemon && pid != h->pid) { - bprintf(buf_err, - "PID file has different PID (%ld != %lld)", - (long)pid, (long long)h->pid); - usleep(usleep_time); - continue; - } - - if (kill(pid, 0) < 0) { - bprintf(buf_err, - "Could not find PID %ld process", (long)pid); - usleep(usleep_time); - continue; - } - - h->pid = pid; - - vtc_log(h->vl, 2, "haproxy PID %ld successfully started", - (long)pid); - return; - } - vtc_fatal(h->vl, "haproxy %s PID file check failed:\n\t%s\n", - h->name, buf_err); -} - -/********************************************************************** - * Allocate and initialize a CLI client - */ - -static struct haproxy_cli * -haproxy_cli_new(struct haproxy *h) -{ - struct haproxy_cli *hc; - - ALLOC_OBJ(hc, HAPROXY_CLI_MAGIC); - AN(hc); - - hc->vl = h->vl; - vtc_log_set_cmd(hc->vl, haproxy_cli_cmds); - hc->sock = -1; - bprintf(hc->connect, "${%s_cli_sock}", h->name); - - hc->txbuf_sz = hc->rxbuf_sz = 2048 * 1024; - hc->txbuf = malloc(hc->txbuf_sz); - AN(hc->txbuf); - hc->rxbuf = malloc(hc->rxbuf_sz); - AN(hc->rxbuf); - - return (hc); -} - -/* creates a master CLI client (-mcli) */ -static struct haproxy_cli * -haproxy_mcli_new(struct haproxy *h) -{ - struct haproxy_cli *hc; - - ALLOC_OBJ(hc, HAPROXY_CLI_MAGIC); - AN(hc); - - hc->vl = h->vl; - vtc_log_set_cmd(hc->vl, haproxy_cli_cmds); - hc->sock = -1; - bprintf(hc->connect, "${%s_mcli_sock}", h->name); - - hc->txbuf_sz = hc->rxbuf_sz = 2048 * 1024; - hc->txbuf = malloc(hc->txbuf_sz); - AN(hc->txbuf); - hc->rxbuf = malloc(hc->rxbuf_sz); - AN(hc->rxbuf); - - return (hc); -} - -/* Bind an address/port for the master CLI (-mcli) */ -static int -haproxy_create_mcli(struct haproxy *h) -{ - int sock; - const char *err; - char buf[128], addr[128], port[128]; - char vsabuf[vsa_suckaddr_len]; - const struct suckaddr *sua; - - sock = VTCP_listen_on(default_listen_addr, NULL, 100, &err); - if (err != NULL) - vtc_fatal(h->vl, - "Create listen socket failed: %s", err); - assert(sock > 0); - sua = VSA_getsockname(sock, vsabuf, sizeof vsabuf); - AN(sua); - - VTCP_name(sua, addr, sizeof addr, port, sizeof port); - bprintf(buf, "%s_mcli", h->name); - if (VSA_Get_Proto(sua) == AF_INET) - macro_def(h->vl, buf, "sock", "%s:%s", addr, port); - else - macro_def(h->vl, buf, "sock", "[%s]:%s", addr, port); - macro_def(h->vl, buf, "addr", "%s", addr); - macro_def(h->vl, buf, "port", "%s", port); - - return (sock); -} - -static void -haproxy_cli_delete(struct haproxy_cli *hc) -{ - CHECK_OBJ_NOTNULL(hc, HAPROXY_CLI_MAGIC); - REPLACE(hc->spec, NULL); - REPLACE(hc->txbuf, NULL); - REPLACE(hc->rxbuf, NULL); - FREE_OBJ(hc); -} - -/********************************************************************** - * Allocate and initialize a haproxy - */ - -static struct haproxy * -haproxy_new(const char *name) -{ - struct haproxy *h; - struct vsb *vsb; - char buf[PATH_MAX]; - int closed_sock; - char addr[128], port[128]; - const char *err; - const char *env_args; - char vsabuf[vsa_suckaddr_len]; - const struct suckaddr *sua; - - ALLOC_OBJ(h, HAPROXY_MAGIC); - AN(h); - REPLACE(h->name, name); - - h->args = VSB_new_auto(); - env_args = getenv(HAPROXY_ARGS_ENV_VAR); - if (env_args) { - VSB_cat(h->args, env_args); - VSB_cat(h->args, " "); - } - - h->vl = vtc_logopen("%s", name); - vtc_log_set_cmd(h->vl, haproxy_cli_cmds); - AN(h->vl); - - h->filename = getenv(HAPROXY_PROGRAM_ENV_VAR); - if (h->filename == NULL) - h->filename = "haproxy"; - - bprintf(buf, "${tmpdir}/%s", name); - vsb = macro_expand(h->vl, buf); - AN(vsb); - h->workdir = strdup(VSB_data(vsb)); - AN(h->workdir); - VSB_destroy(&vsb); - - bprintf(buf, "%s/stats.sock", h->workdir); - h->cli_fn = strdup(buf); - AN(h->cli_fn); - - bprintf(buf, "%s/cfg", h->workdir); - h->cfg_fn = strdup(buf); - AN(h->cfg_fn); - - /* Create a new TCP socket to reserve an IP:port and close it asap. - * May be useful to simulate an unreachable server. - */ - bprintf(h->closed_sock, "%s_closed", h->name); - closed_sock = VTCP_listen_on("127.0.0.1:0", NULL, 100, &err); - if (err != NULL) - vtc_fatal(h->vl, - "Create listen socket failed: %s", err); - assert(closed_sock > 0); - sua = VSA_getsockname(closed_sock, vsabuf, sizeof vsabuf); - AN(sua); - VTCP_name(sua, addr, sizeof addr, port, sizeof port); - if (VSA_Get_Proto(sua) == AF_INET) - macro_def(h->vl, h->closed_sock, "sock", "%s:%s", addr, port); - else - macro_def(h->vl, h->closed_sock, "sock", "[%s]:%s", addr, port); - macro_def(h->vl, h->closed_sock, "addr", "%s", addr); - macro_def(h->vl, h->closed_sock, "port", "%s", port); - VTCP_close(&closed_sock); - - h->cli = haproxy_cli_new(h); - AN(h->cli); - - h->mcli = haproxy_mcli_new(h); - AN(h->mcli); - - bprintf(buf, "rm -rf \"%s\" ; mkdir -p \"%s\"", h->workdir, h->workdir); - AZ(system(buf)); - - VTAILQ_INIT(&h->envars); - VTAILQ_INSERT_TAIL(&haproxies, h, list); - - return (h); -} - -/********************************************************************** - * Delete a haproxy instance - */ - -static void -haproxy_delete(struct haproxy *h) -{ - char buf[PATH_MAX]; - - CHECK_OBJ_NOTNULL(h, HAPROXY_MAGIC); - vtc_logclose(h->vl); - - if (!leave_temp) { - bprintf(buf, "rm -rf \"%s\"", h->workdir); - AZ(system(buf)); - } - - free(h->name); - free(h->workdir); - free(h->cli_fn); - free(h->cfg_fn); - free(h->pid_fn); - VSB_destroy(&h->args); - haproxy_cli_delete(h->cli); - haproxy_cli_delete(h->mcli); - - /* XXX: MEMLEAK (?) */ - FREE_OBJ(h); -} - -/********************************************************************** - * HAProxy listener - */ - -static void * -haproxy_thread(void *priv) -{ - struct haproxy *h; - - CAST_OBJ_NOTNULL(h, priv, HAPROXY_MAGIC); - (void)vtc_record(h->vl, h->fds[0], h->msgs); - h->its_dead_jim = 1; - return (NULL); -} - - -/********************************************************************** - * Start a HAProxy instance. - */ - -static void -haproxy_start(struct haproxy *h) -{ - char buf[PATH_MAX]; - struct vsb *vsb; - - vtc_log(h->vl, 2, "%s", __func__); - - AZ(VSB_finish(h->args)); - vtc_log(h->vl, 4, "opt_worker %d opt_daemon %d opt_check_mode %d opt_mcli %d", - h->opt_worker, h->opt_daemon, h->opt_check_mode, h->opt_mcli); - - vsb = VSB_new_auto(); - AN(vsb); - - VSB_printf(vsb, "exec \"%s\"", h->filename); - if (h->opt_check_mode) - VSB_cat(vsb, " -c"); - else if (h->opt_daemon) - VSB_cat(vsb, " -D"); - else - VSB_cat(vsb, " -d"); - - if (h->opt_worker) { - VSB_cat(vsb, " -W"); - if (h->opt_mcli) { - int sock; - sock = haproxy_create_mcli(h); - VSB_printf(vsb, " -S \"fd@%d\"", sock); - } - } - - VSB_printf(vsb, " %s", VSB_data(h->args)); - - VSB_printf(vsb, " -f \"%s\" ", h->cfg_fn); - - if (h->opt_worker || h->opt_daemon) { - bprintf(buf, "%s/pid", h->workdir); - h->pid_fn = strdup(buf); - AN(h->pid_fn); - VSB_printf(vsb, " -p \"%s\"", h->pid_fn); - } - - AZ(VSB_finish(vsb)); - vtc_dump(h->vl, 4, "argv", VSB_data(vsb), -1); - - if (h->opt_worker && !h->opt_daemon) { - /* - * HAProxy master process must exit with status 128 + - * if signaled by signal. - */ - h->expect_exit = HAPROXY_EXPECT_EXIT; - } - - haproxy_write_conf(h); - - AZ(pipe(&h->fds[0])); - vtc_log(h->vl, 4, "XXX %d @%d", h->fds[1], __LINE__); - AZ(pipe(&h->fds[2])); - h->pid = h->ppid = fork(); - assert(h->pid >= 0); - if (h->pid == 0) { - haproxy_build_env(h); - haproxy_delete_envars(h); - AZ(chdir(h->name)); - AZ(dup2(h->fds[0], 0)); - assert(dup2(h->fds[3], 1) == 1); - assert(dup2(1, 2) == 2); - closefd(&h->fds[0]); - closefd(&h->fds[1]); - closefd(&h->fds[2]); - closefd(&h->fds[3]); - AZ(execl("/bin/sh", "/bin/sh", "-c", VSB_data(vsb), (char*)0)); - exit(1); - } - VSB_destroy(&vsb); - - vtc_log(h->vl, 3, "PID: %ld", (long)h->pid); - macro_def(h->vl, h->name, "pid", "%ld", (long)h->pid); - macro_def(h->vl, h->name, "name", "%s", h->workdir); - - closefd(&h->fds[0]); - closefd(&h->fds[3]); - h->fds[0] = h->fds[2]; - h->fds[2] = h->fds[3] = -1; - - PTOK(pthread_create(&h->tp, NULL, haproxy_thread, h)); - - if (h->pid_fn != NULL) - haproxy_wait_pidfile(h); -} - - -/********************************************************************** - * Wait for a HAProxy instance. - */ - -static void -haproxy_wait(struct haproxy *h) -{ - void *p; - int i, n, sig; - - vtc_log(h->vl, 2, "Wait"); - - if (h->pid < 0) - haproxy_start(h); - - if (h->cli->spec) - haproxy_cli_run(h->cli); - - if (h->mcli->spec) - haproxy_cli_run(h->mcli); - - closefd(&h->fds[1]); - - sig = SIGINT; - n = 0; - vtc_log(h->vl, 2, "Stop HAproxy pid=%ld", (long)h->pid); - while (h->opt_daemon || (!h->opt_check_mode && !h->its_dead_jim)) { - assert(h->pid > 0); - if (n == 0) { - i = kill(h->pid, sig); - if (i == 0) - h->expect_signal = -sig; - if (i && errno == ESRCH) - break; - vtc_log(h->vl, 4, - "Kill(%d)=%d: %s", sig, i, strerror(errno)); - } - VTIM_sleep(0.1); - if (++n == 20) { - switch (sig) { - case SIGINT: sig = SIGTERM ; break; - case SIGTERM: sig = SIGKILL ; break; - default: break; - } - n = 0; - } - } - - PTOK(pthread_join(h->tp, &p)); - AZ(p); - closefd(&h->fds[0]); - if (!h->opt_daemon) { - vtc_wait4(h->vl, h->ppid, h->expect_exit, h->expect_signal, 0); - h->ppid = -1; - } - h->pid = -1; -} - -#define HAPROXY_BE_FD_STR "fd@${" -#define HAPROXY_BE_FD_STRLEN strlen(HAPROXY_BE_FD_STR) - -static int -haproxy_build_backends(struct haproxy *h, const char *vsb_data) -{ - char *s, *p, *q; - - s = strdup(vsb_data); - if (!s) - return (-1); - - p = s; - while (1) { - int sock; - char buf[128], addr[128], port[128]; - const char *err; - char vsabuf[vsa_suckaddr_len]; - const struct suckaddr *sua; - - p = strstr(p, HAPROXY_BE_FD_STR); - if (!p) - break; - - q = p += HAPROXY_BE_FD_STRLEN; - while (*q && *q != '}') - q++; - if (*q != '}') - break; - - *q++ = '\0'; - sock = VTCP_listen_on("127.0.0.1:0", NULL, 100, &err); - if (err != NULL) - vtc_fatal(h->vl, - "Create listen socket failed: %s", err); - assert(sock > 0); - sua = VSA_getsockname(sock, vsabuf, sizeof vsabuf); - AN(sua); - - VTCP_name(sua, addr, sizeof addr, port, sizeof port); - bprintf(buf, "%s_%s", h->name, p); - if (VSA_Get_Proto(sua) == AF_INET) - macro_def(h->vl, buf, "sock", "%s:%s", addr, port); - else - macro_def(h->vl, buf, "sock", "[%s]:%s", addr, port); - macro_def(h->vl, buf, "addr", "%s", addr); - macro_def(h->vl, buf, "port", "%s", port); - - bprintf(buf, "%d", sock); - vtc_log(h->vl, 4, "setenv(%s, %s)", p, buf); - haproxy_add_envar(h, p, buf); - p = q; - } - free(s); - return (0); -} - -static void -haproxy_check_conf(struct haproxy *h, const char *expect) -{ - - h->msgs = VSB_new_auto(); - AN(h->msgs); - h->opt_check_mode = 1; - haproxy_start(h); - haproxy_wait(h); - AZ(VSB_finish(h->msgs)); - if (strstr(VSB_data(h->msgs), expect) == NULL) - vtc_fatal(h->vl, "Did not find expected string '%s'", expect); - vtc_log(h->vl, 2, "Found expected '%s'", expect); - VSB_destroy(&h->msgs); -} - -/********************************************************************** - * Write a configuration for HAProxy instance. - */ - -static void -haproxy_store_conf(struct haproxy *h, const char *cfg, int auto_be) -{ - struct vsb *vsb, *vsb2; - - vsb = VSB_new_auto(); - AN(vsb); - - vsb2 = VSB_new_auto(); - AN(vsb2); - - VSB_printf(vsb, " global\n\tstats socket \"%s\" " - "level admin mode 600\n", h->cli_fn); - VSB_cat(vsb, " stats socket \"fd@${cli}\" level admin\n"); - AZ(VSB_cat(vsb, cfg)); - - if (auto_be) - cmd_server_gen_haproxy_conf(vsb); - - AZ(VSB_finish(vsb)); - - AZ(haproxy_build_backends(h, VSB_data(vsb))); - - h->cfg_vsb = macro_expand(h->vl, VSB_data(vsb)); - AN(h->cfg_vsb); - - VSB_destroy(&vsb2); - VSB_destroy(&vsb); -} - -static void -haproxy_write_conf(struct haproxy *h) -{ - struct vsb *vsb; - - vsb = macro_expand(h->vl, VSB_data(h->cfg_vsb)); - AN(vsb); - assert(VSB_len(vsb) >= 0); - - vtc_dump(h->vl, 4, "conf", VSB_data(vsb), VSB_len(vsb)); - if (VFIL_writefile(h->workdir, h->cfg_fn, - VSB_data(vsb), VSB_len(vsb)) != 0) - vtc_fatal(h->vl, - "failed to write haproxy configuration file: %s (%d)", - strerror(errno), errno); - - VSB_destroy(&vsb); -} - -/* SECTION: haproxy haproxy - * - * Define and interact with haproxy instances. - * - * To define a haproxy server, you'll use this syntax:: - * - * haproxy hNAME -conf-OK CONFIG - * haproxy hNAME -conf-BAD ERROR CONFIG - * haproxy hNAME [-D] [-W] [-arg STRING] [-conf[+vcl] STRING] - * - * The first ``haproxy hNAME`` invocation will start the haproxy master - * process in the background, waiting for the ``-start`` switch to actually - * start the child. - * - * Arguments: - * - * hNAME - * Identify the HAProxy server with a string, it must starts with 'h'. - * - * \-conf-OK CONFIG - * Run haproxy in '-c' mode to check config is OK - * stdout/stderr should contain 'Configuration file is valid' - * The exit code should be 0. - * - * \-conf-BAD ERROR CONFIG - * Run haproxy in '-c' mode to check config is BAD. - * "ERROR" should be part of the diagnostics on stdout/stderr. - * The exit code should be 1. - * - * \-D - * Run HAproxy in daemon mode. If not given '-d' mode used. - * - * \-W - * Enable HAproxy in Worker mode. - * - * \-S - * Enable HAproxy Master CLI in Worker mode - * - * \-arg STRING - * Pass an argument to haproxy, for example "-h simple_list". - * - * \-cli STRING - * Specify the spec to be run by the command line interface (CLI). - * - * \-mcli STRING - * Specify the spec to be run by the command line interface (CLI) - * of the Master process. - * - * \-conf STRING - * Specify the configuration to be loaded by this HAProxy instance. - * - * \-conf+backend STRING - * Specify the configuration to be loaded by this HAProxy instance, - * all server instances will be automatically appended - * - * \-start - * Start this HAProxy instance. - * - * \-wait - * Stop this HAProxy instance. - * - * \-expectexit NUMBER - * Expect haproxy to exit(3) with this value - * - */ - -void -cmd_haproxy(CMD_ARGS) -{ - struct haproxy *h, *h2; - - (void)priv; - - if (av == NULL) { - /* Reset and free */ - VTAILQ_FOREACH_SAFE(h, &haproxies, list, h2) { - vtc_log(h->vl, 2, - "Reset and free %s haproxy %ld", - h->name, (long)h->pid); - if (h->pid >= 0) - haproxy_wait(h); - VTAILQ_REMOVE(&haproxies, h, list); - haproxy_delete(h); - } - return; - } - - AZ(strcmp(av[0], "haproxy")); - av++; - - VTC_CHECK_NAME(vl, av[0], "haproxy", 'h'); - VTAILQ_FOREACH(h, &haproxies, list) - if (!strcmp(h->name, av[0])) - break; - if (h == NULL) - h = haproxy_new(av[0]); - av++; - - for (; *av != NULL; av++) { - if (vtc_error) - break; - - if (!strcmp(*av, "-conf-OK")) { - AN(av[1]); - haproxy_store_conf(h, av[1], 0); - h->expect_exit = 0; - haproxy_check_conf(h, ""); - av++; - continue; - } - if (!strcmp(*av, "-conf-BAD")) { - AN(av[1]); - AN(av[2]); - haproxy_store_conf(h, av[2], 0); - h->expect_exit = 1; - haproxy_check_conf(h, av[1]); - av += 2; - continue; - } - - if (!strcmp(*av, HAPROXY_OPT_DAEMON)) { - h->opt_daemon = 1; - continue; - } - if (!strcmp(*av, HAPROXY_OPT_WORKER)) { - h->opt_worker = 1; - continue; - } - if (!strcmp(*av, HAPROXY_OPT_MCLI)) { - h->opt_mcli = 1; - continue; - } - if (!strcmp(*av, "-arg")) { - AN(av[1]); - AZ(h->pid); - VSB_cat(h->args, " "); - VSB_cat(h->args, av[1]); - av++; - continue; - } - - if (!strcmp(*av, "-cli")) { - REPLACE(h->cli->spec, av[1]); - if (h->tp) - haproxy_cli_run(h->cli); - av++; - continue; - } - - if (!strcmp(*av, "-mcli")) { - REPLACE(h->mcli->spec, av[1]); - if (h->tp) - haproxy_cli_run(h->mcli); - av++; - continue; - } - - if (!strcmp(*av, "-conf")) { - AN(av[1]); - haproxy_store_conf(h, av[1], 0); - av++; - continue; - } - if (!strcmp(*av, "-conf+backend")) { - AN(av[1]); - haproxy_store_conf(h, av[1], 1); - av++; - continue; - } - - if (!strcmp(*av, "-expectexit")) { - h->expect_exit = strtoul(av[1], NULL, 0); - av++; - continue; - } - if (!strcmp(*av, "-start")) { - haproxy_start(h); - continue; - } - if (!strcmp(*av, "-wait")) { - haproxy_wait(h); - continue; - } - vtc_fatal(h->vl, "Unknown haproxy argument: %s", *av); - } -} diff --git a/bin/varnishtest/vtc_http.c b/bin/varnishtest/vtc_http.c deleted file mode 100644 index a5f701862..000000000 --- a/bin/varnishtest/vtc_http.c +++ /dev/null @@ -1,2016 +0,0 @@ -/*- - * Copyright (c) 2008-2019 Varnish Software AS - * All rights reserved. - * - * Author: Poul-Henning Kamp - * - * SPDX-License-Identifier: BSD-2-Clause - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#include "config.h" - -#include - -#include -#include -#include -#include -#include -#include - -#include "vtc.h" -#include "vtc_http.h" - -#include "vct.h" -#include "vfil.h" -#include "vnum.h" -#include "vrnd.h" -#include "vtcp.h" -#include "vtim.h" -#include "hpack.h" - -extern const struct cmds http_cmds[]; - -/* SECTION: client-server client/server - * - * Client and server threads are fake HTTP entities used to test your Varnish - * and VCL. They take any number of arguments, and the one that are not - * recognized, assuming they don't start with '-', are treated as - * specifications, laying out the actions to undertake:: - * - * client cNAME [...] - * server sNAME [...] - * - * Clients and server are identified by a string that's the first argument, - * clients' names start with 'c' and servers' names start with 's'. - * - * As the client and server commands share a good deal of arguments and - * specification actions, they are grouped in this single section, specific - * items will be explicitly marked as such. - * - * SECTION: client-server.macros Macros and automatic behaviour - * - * To make things easier in the general case, clients will connect by default - * to a Varnish server called v1. To connect to a different Varnish server, use - * '-connect ${vNAME_sock}'. - * - * The -vcl+backend switch of the ``varnish`` command will add all the declared - * servers as backends. Be careful though, servers will by default listen to - * the 127.0.0.1 IP and will pick a random port, and publish 3 macros: - * sNAME_addr, sNAME_port and sNAME_sock, but only once they are started. For - * 'varnish -vcl+backend' to create the vcl with the correct values, the server - * must be started first. - * - * SECTION: client-server.args Arguments - * - * \-start - * Start the thread in background, processing the last given - * specification. - * - * \-wait - * Block until the thread finishes. - * - * \-run (client only) - * Equivalent to "-start -wait". - * - * \-repeat NUMBER - * Instead of processing the specification only once, do it NUMBER times. - * - * \-keepalive - * For repeat, do not open new connections but rather run all - * iterations in the same connection - * - * \-break (server only) - * Stop the server. - * - * \-listen STRING (server only) - * Dictate the listening socket for the server. STRING is of the form - * "IP PORT", or "/PATH/TO/SOCKET" for a Unix domain socket. In the - * latter case, the path must begin with '/', and the server must be - * able to create it. - * - * \-connect STRING (client only) - * Indicate the server to connect to. STRING is also of the form - * "IP PORT", or "/PATH/TO/SOCKET". As with "server -listen", a - * Unix domain socket is recognized when STRING begins with a '/'. - * - * \-dispatch (server only, s0 only) - * Normally, to keep things simple, server threads only handle one - * connection at a time, but the -dispatch switch allows to accept - * any number of connection and handle them following the given spec. - * - * However, -dispatch is only allowed for the server name "s0". - * - * \-proxy1 STRING (client only) - * Use the PROXY protocol version 1 for this connection. STRING - * is of the form "CLIENTIP:PORT SERVERIP:PORT". - * - * \-proxy2 STRING (client only) - * Use the PROXY protocol version 2 for this connection. STRING - * is of the form "CLIENTIP:PORT SERVERIP:PORT [TLV [TLV ... ]]". - * - * TLV is in the form name=val - * - * name: 0xID or alpn, authority, crc32c, noop, unique_id, netns - * val: 0x... or string - * - * ssl is currently not implemented (can be sent as hex) - * - * SECTION: client-server.spec Specification - * - * It's a string, either double-quoted "like this", but most of the time - * enclosed in curly brackets, allowing multilining. Write a command per line in - * it, empty line are ignored, and long line can be wrapped by using a - * backslash. For example:: - * - * client c1 { - * txreq -url /foo \ - * -hdr "bar: baz" - * - * rxresp - * } -run - */ - -#define ONLY_CLIENT(hp, av) \ - do { \ - if (hp->h2) \ - vtc_fatal(hp->vl, \ - "\"%s\" only possible before H/2 upgrade", \ - av[0]); \ - if (hp->sfd != NULL) \ - vtc_fatal(hp->vl, \ - "\"%s\" only possible in client", av[0]); \ - } while (0) - -#define ONLY_SERVER(hp, av) \ - do { \ - if (hp->h2) \ - vtc_fatal(hp->vl, \ - "\"%s\" only possible before H/2 upgrade", \ - av[0]); \ - if (hp->sfd == NULL) \ - vtc_fatal(hp->vl, \ - "\"%s\" only possible in server", av[0]); \ - } while (0) - - -/* XXX: we may want to vary this */ -static const char * const nl = "\r\n"; - -/********************************************************************** - * Generate a synthetic body - */ - -char * -synth_body(const char *len, int rnd) -{ - int i, j, k, l; - char *b; - - - AN(len); - i = strtoul(len, NULL, 0); - assert(i > 0); - b = malloc(i + 1L); - AN(b); - l = k = '!'; - for (j = 0; j < i; j++) { - if ((j % 64) == 63) { - b[j] = '\n'; - k++; - if (k == '~') - k = '!'; - l = k; - } else if (rnd) { - b[j] = (VRND_RandomTestable() % 95) + ' '; - } else { - b[j] = (char)l; - if (++l == '~') - l = '!'; - } - } - b[i - 1] = '\n'; - b[i] = '\0'; - return (b); -} - -/********************************************************************** - * Finish and write the vsb to the fd - */ - -static void -http_write(const struct http *hp, int lvl, const char *pfx) -{ - - AZ(VSB_finish(hp->vsb)); - vtc_dump(hp->vl, lvl, pfx, VSB_data(hp->vsb), VSB_len(hp->vsb)); - if (VSB_tofile(hp->vsb, hp->sess->fd)) - vtc_log(hp->vl, hp->fatal, "Write failed: %s", - strerror(errno)); -} - -/********************************************************************** - * find header - */ - -static char * -http_find_header(char * const *hh, const char *hdr) -{ - int n, l; - char *r; - - l = strlen(hdr); - - for (n = 3; hh[n] != NULL; n++) { - if (strncasecmp(hdr, hh[n], l) || hh[n][l] != ':') - continue; - for (r = hh[n] + l + 1; vct_issp(*r); r++) - continue; - return (r); - } - return (NULL); -} - -/********************************************************************** - * count header - */ - -static int -http_count_header(char * const *hh, const char *hdr) -{ - int n, l, r = 0; - - l = strlen(hdr); - - for (n = 3; hh[n] != NULL; n++) { - if (strncasecmp(hdr, hh[n], l) || hh[n][l] != ':') - continue; - r++; - } - return (r); -} - -/* SECTION: client-server.spec.expect - * - * expect STRING1 OP STRING2 - * Test if "STRING1 OP STRING2" is true, and if not, fails the test. - * OP can be ==, <, <=, >, >= when STRING1 and STRING2 represent numbers - * in which case it's an order operator. If STRING1 and STRING2 are - * meant as strings OP is a matching operator, either == (exact match) - * or ~ (regex match). - * - * varnishtest will first try to resolve STRING1 and STRING2 by looking - * if they have special meanings, in which case, the resolved value is - * use for the test. Note that this value can be a string representing a - * number, allowing for tests such as:: - * - * expect req.http.x-num > 2 - * - * Here's the list of recognized strings, most should be obvious as they - * either match VCL logic, or the txreq/txresp options: - * - * - remote.ip - * - remote.port - * - remote.path - * - req.method - * - req.url - * - req.proto - * - resp.proto - * - resp.status - * - resp.reason - * - resp.chunklen - * - req.bodylen - * - req.body - * - resp.bodylen - * - resp.body - * - req.http.NAME - * - resp.http.NAME - */ - -static const char * -cmd_var_resolve(struct http *hp, char *spec) -{ - char **hh, *hdr; - if (!strcmp(spec, "remote.ip")) - return (hp->rem_ip); - if (!strcmp(spec, "remote.port")) - return (hp->rem_port); - if (!strcmp(spec, "remote.path")) - return (hp->rem_path); - if (!strcmp(spec, "req.method")) - return (hp->req[0]); - if (!strcmp(spec, "req.url")) - return (hp->req[1]); - if (!strcmp(spec, "req.proto")) - return (hp->req[2]); - if (!strcmp(spec, "resp.proto")) - return (hp->resp[0]); - if (!strcmp(spec, "resp.status")) - return (hp->resp[1]); - if (!strcmp(spec, "resp.reason")) - return (hp->resp[2]); - if (!strcmp(spec, "resp.chunklen")) - return (hp->chunklen); - if (!strcmp(spec, "req.bodylen")) - return (hp->bodylen); - if (!strcmp(spec, "req.body")) - return (hp->body != NULL ? hp->body : spec); - if (!strcmp(spec, "resp.bodylen")) - return (hp->bodylen); - if (!strcmp(spec, "resp.body")) - return (hp->body != NULL ? hp->body : spec); - if (!strncmp(spec, "req.http.", 9)) { - hh = hp->req; - hdr = spec + 9; - } else if (!strncmp(spec, "resp.http.", 10)) { - hh = hp->resp; - hdr = spec + 10; - } else if (!strcmp(spec, "h2.state")) { - if (hp->h2) - return ("true"); - else - return ("false"); - } else - return (spec); - hdr = http_find_header(hh, hdr); - return (hdr); -} - -static void -cmd_http_expect(CMD_ARGS) -{ - struct http *hp; - const char *lhs; - char *cmp; - const char *rhs; - - (void)vl; - CAST_OBJ_NOTNULL(hp, priv, HTTP_MAGIC); - AZ(strcmp(av[0], "expect")); - av++; - - AN(av[0]); - AN(av[1]); - AN(av[2]); - AZ(av[3]); - lhs = cmd_var_resolve(hp, av[0]); - cmp = av[1]; - rhs = cmd_var_resolve(hp, av[2]); - - vtc_expect(vl, av[0], lhs, cmp, av[2], rhs); -} - -/* SECTION: client-server.spec.expect_pattern - * - * expect_pattern - * - * Expect as the http body the test pattern generated by chunkedlen ('0'..'7' - * repeating). - */ -static void -cmd_http_expect_pattern(CMD_ARGS) -{ - char *p; - struct http *hp; - char t = '0'; - - (void)vl; - CAST_OBJ_NOTNULL(hp, priv, HTTP_MAGIC); - AZ(strcmp(av[0], "expect_pattern")); - av++; - AZ(av[0]); - for (p = hp->body; *p != '\0'; p++) { - if (*p != t) - vtc_fatal(hp->vl, - "EXPECT PATTERN FAIL @%zd should 0x%02x is 0x%02x", - (ssize_t) (p - hp->body), t, *p); - t += 1; - t &= ~0x08; - } - vtc_log(hp->vl, 4, "EXPECT PATTERN SUCCESS"); -} - -/********************************************************************** - * Split a HTTP protocol header - */ - -static void -http_splitheader(struct http *hp, int req) -{ - char *p, *q, **hh; - int n; - char buf[20]; - - CHECK_OBJ_NOTNULL(hp, HTTP_MAGIC); - if (req) { - memset(hp->req, 0, sizeof hp->req); - hh = hp->req; - } else { - memset(hp->resp, 0, sizeof hp->resp); - hh = hp->resp; - } - - n = 0; - p = hp->rx_b; - if (*p == '\0') { - vtc_log(hp->vl, 4, "No headers"); - return; - } - - /* REQ/PROTO */ - while (vct_islws(*p)) - p++; - hh[n++] = p; - while (!vct_islws(*p)) - p++; - AZ(vct_iscrlf(p, hp->rx_e)); - *p++ = '\0'; - - /* URL/STATUS */ - while (vct_issp(*p)) /* XXX: H space only */ - p++; - AZ(vct_iscrlf(p, hp->rx_e)); - hh[n++] = p; - while (!vct_islws(*p)) - p++; - if (vct_iscrlf(p, hp->rx_e)) { - hh[n++] = NULL; - q = p; - p = vct_skipcrlf(p, hp->rx_e); - *q = '\0'; - } else { - *p++ = '\0'; - /* PROTO/MSG */ - while (vct_issp(*p)) /* XXX: H space only */ - p++; - hh[n++] = p; - while (!vct_iscrlf(p, hp->rx_e)) - p++; - q = p; - p = vct_skipcrlf(p, hp->rx_e); - *q = '\0'; - } - assert(n == 3); - - while (*p != '\0') { - assert(n < MAX_HDR); - if (vct_iscrlf(p, hp->rx_e)) - break; - hh[n++] = p++; - while (*p != '\0' && !vct_iscrlf(p, hp->rx_e)) - p++; - if (*p == '\0') { - break; - } - q = p; - p = vct_skipcrlf(p, hp->rx_e); - *q = '\0'; - } - p = vct_skipcrlf(p, hp->rx_e); - assert(*p == '\0'); - - for (n = 0; n < 3 || hh[n] != NULL; n++) { - bprintf(buf, "http[%2d] ", n); - vtc_dump(hp->vl, 4, buf, hh[n], -1); - } -} - - -/********************************************************************** - * Receive another character - */ - -static int -http_rxchar(struct http *hp, int n, int eof) -{ - int i; - struct pollfd pfd[1]; - - while (n > 0) { - pfd[0].fd = hp->sess->fd; - pfd[0].events = POLLIN; - pfd[0].revents = 0; - i = poll(pfd, 1, (int)(hp->timeout * 1000)); - if (i < 0 && errno == EINTR) - continue; - if (i == 0) { - vtc_log(hp->vl, hp->fatal, - "HTTP rx timeout (fd:%d %.3fs)", - hp->sess->fd, hp->timeout); - continue; - } - if (i < 0) { - vtc_log(hp->vl, hp->fatal, - "HTTP rx failed (fd:%d poll: %s)", - hp->sess->fd, strerror(errno)); - continue; - } - assert(i > 0); - assert(hp->rx_p + n < hp->rx_e); - i = read(hp->sess->fd, hp->rx_p, n); - if (!(pfd[0].revents & POLLIN)) - vtc_log(hp->vl, 4, - "HTTP rx poll (fd:%d revents: %x n=%d, i=%d)", - hp->sess->fd, pfd[0].revents, n, i); - if (i == 0 && eof) - return (i); - if (i == 0) { - vtc_log(hp->vl, hp->fatal, - "HTTP rx EOF (fd:%d read: %s) %d", - hp->sess->fd, strerror(errno), n); - return (-1); - } - if (i < 0) { - vtc_log(hp->vl, hp->fatal, - "HTTP rx failed (fd:%d read: %s)", - hp->sess->fd, strerror(errno)); - return (-1); - } - hp->rx_p += i; - *hp->rx_p = '\0'; - n -= i; - } - return (1); -} - -static int -http_rxchunk(struct http *hp) -{ - char *q, *old; - int i; - - old = hp->rx_p; - do { - if (http_rxchar(hp, 1, 0) < 0) - return (-1); - } while (hp->rx_p[-1] != '\n'); - vtc_dump(hp->vl, 4, "len", old, -1); - i = strtoul(old, &q, 16); - bprintf(hp->chunklen, "%d", i); - if ((q == old) || (q == hp->rx_p) || (*q != '\0' && !vct_islws(*q))) { - vtc_log(hp->vl, hp->fatal, "Chunklen fail (%02x @ %td)", - (*q & 0xff), q - old); - return (-1); - } - assert(*q == '\0' || vct_islws(*q)); - hp->rx_p = old; - if (i > 0) { - if (http_rxchar(hp, i, 0) < 0) - return (-1); - vtc_dump(hp->vl, 4, "chunk", old, i); - } - old = hp->rx_p; - if (http_rxchar(hp, 2, 0) < 0) - return (-1); - if (!vct_iscrlf(old, hp->rx_e)) { - vtc_log(hp->vl, hp->fatal, "Chunklen without CRLF"); - return (-1); - } - hp->rx_p = old; - *hp->rx_p = '\0'; - return (i); -} - -/********************************************************************** - * Swallow a HTTP message body - * - * max: 0 is all - */ - -static void -http_swallow_body(struct http *hp, char * const *hh, int body, int max) -{ - const char *p, *q; - int i, l, ll; - - l = hp->rx_p - hp->body; - - p = http_find_header(hh, "transfer-encoding"); - q = http_find_header(hh, "content-length"); - if (p != NULL && !strcasecmp(p, "chunked")) { - if (q != NULL) { - vtc_log(hp->vl, hp->fatal, "Both C-E: Chunked and C-L"); - return; - } - ll = 0; - while (http_rxchunk(hp) > 0) { - ll = (hp->rx_p - hp->body) - l; - if (max && ll >= max) - break; - } - p = "chunked"; - } else if (q != NULL) { - ll = strtoul(q, NULL, 10); - if (max && ll > l + max) - ll = max; - else - ll -= l; - i = http_rxchar(hp, ll, 0); - if (i < 0) - return; - p = "c-l"; - } else if (body) { - ll = 0; - do { - i = http_rxchar(hp, 1, 1); - if (i < 0) - return; - ll += i; - if (max && ll >= max) - break; - } while (i > 0); - p = "eof"; - } else { - p = "none"; - ll = l = 0; - } - vtc_dump(hp->vl, 4, p, hp->body + l, ll); - l += ll; - hp->bodyl = l; - bprintf(hp->bodylen, "%d", l); -} - -/********************************************************************** - * Receive a HTTP protocol header - */ - -static void -http_rxhdr(struct http *hp) -{ - int i, s = 0; - char *p; - ssize_t l; - - CHECK_OBJ_NOTNULL(hp, HTTP_MAGIC); - hp->rx_p = hp->rx_b; - *hp->rx_p = '\0'; - hp->body = NULL; - bprintf(hp->bodylen, "%s", ""); - while (1) { - p = hp->rx_p; - i = http_rxchar(hp, 1, 1); - if (i < 1) - break; - if (s == 0 && *p == '\r') - s = 1; - else if ((s == 0 || s == 1) && *p == '\n') - s = 2; - else if (s == 2 && *p == '\r') - s = 3; - else if ((s == 2 || s == 3) && *p == '\n') - break; - else - s = 0; - } - l = hp->rx_p - hp->rx_b; - vtc_dump(hp->vl, 4, "rxhdr", hp->rx_b, l); - vtc_log(hp->vl, 4, "rxhdrlen = %zd", l); - if (i < 1) - vtc_log(hp->vl, hp->fatal, "HTTP header is incomplete"); - *hp->rx_p = '\0'; - hp->body = hp->rx_p; -} - -/* SECTION: client-server.spec.rxresp - * - * rxresp [-no_obj] (client only) - * Receive and parse a response's headers and body. If -no_obj is - * present, only get the headers. - */ - -static void -cmd_http_rxresp(CMD_ARGS) -{ - struct http *hp; - int has_obj = 1; - - (void)vl; - CAST_OBJ_NOTNULL(hp, priv, HTTP_MAGIC); - ONLY_CLIENT(hp, av); - AZ(strcmp(av[0], "rxresp")); - av++; - - for (; *av != NULL; av++) - if (!strcmp(*av, "-no_obj")) - has_obj = 0; - else - vtc_fatal(hp->vl, - "Unknown http rxresp spec: %s\n", *av); - http_rxhdr(hp); - http_splitheader(hp, 0); - if (http_count_header(hp->resp, "Content-Length") > 1) - vtc_fatal(hp->vl, - "Multiple Content-Length headers.\n"); - if (!has_obj) - return; - if (!hp->resp[0] || !hp->resp[1]) - return; - if (hp->head_method) - return; - if (!strcmp(hp->resp[1], "200")) - http_swallow_body(hp, hp->resp, 1, 0); - else - http_swallow_body(hp, hp->resp, 0, 0); - vtc_log(hp->vl, 4, "bodylen = %s", hp->bodylen); -} - -/* SECTION: client-server.spec.rxresphdrs - * - * rxresphdrs (client only) - * Receive and parse a response's headers. - */ - -static void -cmd_http_rxresphdrs(CMD_ARGS) -{ - struct http *hp; - - (void)vl; - CAST_OBJ_NOTNULL(hp, priv, HTTP_MAGIC); - ONLY_CLIENT(hp, av); - AZ(strcmp(av[0], "rxresphdrs")); - av++; - - for (; *av != NULL; av++) - vtc_fatal(hp->vl, "Unknown http rxresp spec: %s\n", *av); - http_rxhdr(hp); - http_splitheader(hp, 0); - if (http_count_header(hp->resp, "Content-Length") > 1) - vtc_fatal(hp->vl, - "Multiple Content-Length headers.\n"); -} - -/* SECTION: client-server.spec.gunzip - * - * gunzip - * Gunzip the body in place. - */ -static void -cmd_http_gunzip(CMD_ARGS) -{ - struct http *hp; - - (void)av; - (void)vl; - - CAST_OBJ_NOTNULL(hp, priv, HTTP_MAGIC); - vtc_gunzip(hp, hp->body, &hp->bodyl); -} - -/********************************************************************** - * Handle common arguments of a transmitted request or response - */ - -static char* const * -http_tx_parse_args(char * const *av, struct vtclog *vl, struct http *hp, - char *body, unsigned nohost, unsigned nodate, unsigned noserver, unsigned nouseragent) -{ - long bodylen = 0; - char *b, *c; - char *nullbody; - ssize_t len; - int nolen = 0; - int l; - - nullbody = body; - - for (; *av != NULL; av++) { - if (!strcmp(*av, "-nolen")) { - nolen = 1; - } else if (!strcmp(*av, "-nohost")) { - nohost = 1; - } else if (!strcmp(*av, "-nodate")) { - nodate = 1; - } else if (!strcmp(*av, "-hdr")) { - if (!strncasecmp(av[1], "Content-Length:", 15) || - !strncasecmp(av[1], "Transfer-Encoding:", 18)) - nolen = 1; - if (!strncasecmp(av[1], "Host:", 5)) - nohost = 1; - if (!strncasecmp(av[1], "Date:", 5)) - nodate = 1; - if (!strncasecmp(av[1], "Server:", 7)) - noserver = 1; - if (!strncasecmp(av[1], "User-Agent:", 11)) - nouseragent = 1; - VSB_printf(hp->vsb, "%s%s", av[1], nl); - av++; - } else if (!strcmp(*av, "-hdrlen")) { - VSB_printf(hp->vsb, "%s: ", av[1]); - l = atoi(av[2]); - while (l-- > 0) - VSB_putc(hp->vsb, '0' + (l % 10)); - VSB_printf(hp->vsb, "%s", nl); - av+=2; - } else - break; - } - for (; *av != NULL; av++) { - if (!strcmp(*av, "-body")) { - assert(body == nullbody); - REPLACE(body, av[1]); - - AN(body); - av++; - bodylen = strlen(body); - for (b = body; *b != '\0'; b++) { - if (*b == '\\' && b[1] == '0') { - *b = '\0'; - for (c = b+1; *c != '\0'; c++) { - *c = c[1]; - } - b++; - bodylen--; - } - } - } else if (!strcmp(*av, "-bodyfrom")) { - assert(body == nullbody); - free(body); - body = VFIL_readfile(NULL, av[1], &len); - AN(body); - assert(len < INT_MAX); - bodylen = len; - av++; - } else if (!strcmp(*av, "-bodylen")) { - assert(body == nullbody); - free(body); - body = synth_body(av[1], 0); - bodylen = strlen(body); - av++; - } else if (!strncmp(*av, "-gzip", 5)) { - l = vtc_gzip_cmd(hp, av, &body, &bodylen); - if (l == 0) - break; - av++; - if (l > 1) - VSB_printf(hp->vsb, "Content-Encoding: gzip%s", nl); - } else - break; - } - if (!nohost) { - VSB_cat(hp->vsb, "Host: "); - macro_cat(vl, hp->vsb, "localhost", NULL); - VSB_cat(hp->vsb, nl); - } - if (!nodate) { - VSB_cat(hp->vsb, "Date: "); - macro_cat(vl, hp->vsb, "date", NULL); - VSB_cat(hp->vsb, nl); - } - if (!noserver) - VSB_printf(hp->vsb, "Server: %s%s", hp->sess->name, nl); - if (!nouseragent) - VSB_printf(hp->vsb, "User-Agent: %s%s", hp->sess->name, nl); - if (body != NULL && !nolen) - VSB_printf(hp->vsb, "Content-Length: %ld%s", bodylen, nl); - VSB_cat(hp->vsb, nl); - if (body != NULL) { - VSB_bcat(hp->vsb, body, bodylen); - free(body); - } - return (av); -} - -/* SECTION: client-server.spec.txreq - * - * txreq|txresp [...] - * Send a minimal request or response, but overload it if necessary. - * - * txreq is client-specific and txresp is server-specific. - * - * The only thing different between a request and a response, apart - * from who can send them is that the first line (request line vs - * status line), so all the options are pretty much the same. - * - * \-method STRING (txreq only) - * What method to use (default: "GET"). - * - * \-req STRING (txreq only) - * Alias for -method. - * - * \-url STRING (txreq only) - * What location to use (default "/"). - * - * \-proto STRING - * What protocol use in the status line. - * (default: "HTTP/1.1"). - * - * \-status NUMBER (txresp only) - * What status code to return (default 200). - * - * \-reason STRING (txresp only) - * What message to put in the status line (default: "OK"). - * - * \-noserver (txresp only) - * Don't include a Server header with the id of the server. - * - * \-nouseragent (txreq only) - * Don't include a User-Agent header with the id of the client. - * - * These three switches can appear in any order but must come before the - * following ones. - * - * \-nohost - * Don't include a Host header in the request. Also Implied - * by the addition of a Host header with ``-hdr``. - * - * \-nolen - * Don't include a Content-Length header. Also implied by the - * addition of a Content-Length or Transfer-Encoding header - * with ``-hdr``. - * - * \-nodate - * Don't include a Date header in the response. Also implied - * by the addition of a Date header with ``-hdr``. - * - * \-hdr STRING - * Add STRING as a header, it must follow this format: - * "name: value". It can be called multiple times. - * - * \-hdrlen STRING NUMBER - * Add STRING as a header with NUMBER bytes of content. - * - * You can then use the arguments related to the body: - * - * \-body STRING - * Input STRING as body. - * - * \-bodyfrom FILE - * Same as -body but content is read from FILE. - * - * \-bodylen NUMBER - * Generate and input a body that is NUMBER bytes-long. - * - * \-gziplevel NUMBER - * Set the gzip level (call it before any of the other gzip - * switches). - * - * \-gzipresidual NUMBER - * Add extra gzip bits. You should never need it. - * - * \-gzipbody STRING - * Gzip STRING and send it as body. - * - * \-gziplen NUMBER - * Combine -bodylen and -gzipbody: generate a string of length - * NUMBER, gzip it and send as body. - */ - -/********************************************************************** - * Transmit a response - */ - -static void -cmd_http_txresp(CMD_ARGS) -{ - struct http *hp; - const char *proto = "HTTP/1.1"; - const char *status = "200"; - const char *reason = "OK"; - char* body = NULL; - unsigned noserver = 0; - - (void)vl; - CAST_OBJ_NOTNULL(hp, priv, HTTP_MAGIC); - ONLY_SERVER(hp, av); - AZ(strcmp(av[0], "txresp")); - av++; - - VSB_clear(hp->vsb); - - for (; *av != NULL; av++) { - if (!strcmp(*av, "-proto")) { - proto = av[1]; - av++; - } else if (!strcmp(*av, "-status")) { - status = av[1]; - av++; - } else if (!strcmp(*av, "-reason")) { - reason = av[1]; - av++; - continue; - } else if (!strcmp(*av, "-noserver")) { - noserver = 1; - continue; - } else - break; - } - - VSB_printf(hp->vsb, "%s %s %s%s", proto, status, reason, nl); - - /* send a "Content-Length: 0" header unless something else happens */ - REPLACE(body, ""); - - av = http_tx_parse_args(av, vl, hp, body, 1, 0, noserver, 1); - if (*av != NULL) - vtc_fatal(hp->vl, "Unknown http txresp spec: %s\n", *av); - - http_write(hp, 4, "txresp"); -} - -static void -cmd_http_upgrade(CMD_ARGS) -{ - char *h; - struct http *hp; - - CAST_OBJ_NOTNULL(hp, priv, HTTP_MAGIC); - ONLY_SERVER(hp, av); - AN(hp->sfd); - - h = http_find_header(hp->req, "Upgrade"); - if (!h || strcmp(h, "h2c")) - vtc_fatal(vl, "Req misses \"Upgrade: h2c\" header"); - - h = http_find_header(hp->req, "Connection"); - if (!h || strcmp(h, "Upgrade, HTTP2-Settings")) - vtc_fatal(vl, "Req misses \"Connection: " - "Upgrade, HTTP2-Settings\" header"); - - h = http_find_header(hp->req, "HTTP2-Settings"); - if (!h) - vtc_fatal(vl, "Req misses \"HTTP2-Settings\" header"); - - parse_string(vl, hp, - "txresp -status 101" - " -hdr \"Connection: Upgrade\"" - " -hdr \"Upgrade: h2c\"\n" - ); - - parse_string(vl, hp, - "rxpri\n" - "stream 0 {\n" - " txsettings\n" - " rxsettings\n" - " txsettings -ack\n" - " rxsettings\n" - " expect settings.ack == true\n" - "} -start\n" - ); - - b64_settings(hp, h); - -} - -/********************************************************************** - * Receive a request - */ - -/* SECTION: client-server.spec.rxreq - * - * rxreq (server only) - * Receive and parse a request's headers and body. - */ -static void -cmd_http_rxreq(CMD_ARGS) -{ - struct http *hp; - - CAST_OBJ_NOTNULL(hp, priv, HTTP_MAGIC); - ONLY_SERVER(hp, av); - AZ(strcmp(av[0], "rxreq")); - av++; - - for (; *av != NULL; av++) - vtc_fatal(vl, "Unknown http rxreq spec: %s\n", *av); - http_rxhdr(hp); - http_splitheader(hp, 1); - if (http_count_header(hp->req, "Content-Length") > 1) - vtc_fatal(vl, "Multiple Content-Length headers.\n"); - http_swallow_body(hp, hp->req, 0, 0); - vtc_log(vl, 4, "bodylen = %s", hp->bodylen); -} - -/* SECTION: client-server.spec.rxreqhdrs - * - * rxreqhdrs (server only) - * Receive and parse a request's headers (but not the body). - */ - -static void -cmd_http_rxreqhdrs(CMD_ARGS) -{ - struct http *hp; - - (void)vl; - CAST_OBJ_NOTNULL(hp, priv, HTTP_MAGIC); - AZ(strcmp(av[0], "rxreqhdrs")); - av++; - - for (; *av != NULL; av++) - vtc_fatal(hp->vl, "Unknown http rxreq spec: %s\n", *av); - http_rxhdr(hp); - http_splitheader(hp, 1); - if (http_count_header(hp->req, "Content-Length") > 1) - vtc_fatal(hp->vl, "Multiple Content-Length headers.\n"); -} - -/* SECTION: client-server.spec.rxreqbody - * - * rxreqbody (server only) - * Receive a request's body. - */ - -static void -cmd_http_rxreqbody(CMD_ARGS) -{ - struct http *hp; - - (void)vl; - CAST_OBJ_NOTNULL(hp, priv, HTTP_MAGIC); - ONLY_SERVER(hp, av); - AZ(strcmp(av[0], "rxreqbody")); - av++; - - for (; *av != NULL; av++) - vtc_fatal(hp->vl, "Unknown http rxreq spec: %s\n", *av); - http_swallow_body(hp, hp->req, 0, 0); - vtc_log(hp->vl, 4, "bodylen = %s", hp->bodylen); -} - -/* SECTION: client-server.spec.rxrespbody - * - * rxrespbody (client only) - * Receive (part of) a response's body. - * - * -max : max length of this receive, 0 for all - */ - -static void -cmd_http_rxrespbody(CMD_ARGS) -{ - struct http *hp; - int max = 0; - - (void)vl; - CAST_OBJ_NOTNULL(hp, priv, HTTP_MAGIC); - ONLY_CLIENT(hp, av); - AZ(strcmp(av[0], "rxrespbody")); - av++; - - for (; *av != NULL; av++) - if (!strcmp(*av, "-max")) { - max = atoi(av[1]); - av++; - } else - vtc_fatal(hp->vl, - "Unknown http rxrespbody spec: %s\n", *av); - - http_swallow_body(hp, hp->resp, 1, max); - vtc_log(hp->vl, 4, "bodylen = %s", hp->bodylen); -} - -/* SECTION: client-server.spec.rxchunk - * - * rxchunk - * Receive an HTTP chunk. - */ - -static void -cmd_http_rxchunk(CMD_ARGS) -{ - struct http *hp; - int ll, i; - - (void)vl; - CAST_OBJ_NOTNULL(hp, priv, HTTP_MAGIC); - ONLY_CLIENT(hp, av); - - i = http_rxchunk(hp); - if (i == 0) { - ll = hp->rx_p - hp->body; - hp->bodyl = ll; - bprintf(hp->bodylen, "%d", ll); - vtc_log(hp->vl, 4, "bodylen = %s", hp->bodylen); - } -} - -/********************************************************************** - * Transmit a request - */ - -static void -cmd_http_txreq(CMD_ARGS) -{ - struct http *hp; - const char *req = "GET"; - const char *url = "/"; - const char *proto = "HTTP/1.1"; - const char *up = NULL; - unsigned nohost; - unsigned nouseragent = 0; - - (void)vl; - CAST_OBJ_NOTNULL(hp, priv, HTTP_MAGIC); - ONLY_CLIENT(hp, av); - AZ(strcmp(av[0], "txreq")); - av++; - - VSB_clear(hp->vsb); - - hp->head_method = 0; - for (; *av != NULL; av++) { - if (!strcmp(*av, "-url")) { - url = av[1]; - av++; - } else if (!strcmp(*av, "-proto")) { - proto = av[1]; - av++; - } else if (!strcmp(*av, "-method") || - !strcmp(*av, "-req")) { - req = av[1]; - hp->head_method = !strcmp(av[1], "HEAD") ; - av++; - } else if (!hp->sfd && !strcmp(*av, "-up")) { - up = av[1]; - av++; - } else if (!strcmp(*av, "-nouseragent")) { - nouseragent = 1; - } else - break; - } - VSB_printf(hp->vsb, "%s %s %s%s", req, url, proto, nl); - - if (up) - VSB_printf(hp->vsb, "Connection: Upgrade, HTTP2-Settings%s" - "Upgrade: h2c%s" - "HTTP2-Settings: %s%s", nl, nl, up, nl); - - nohost = strcmp(proto, "HTTP/1.1") != 0; - av = http_tx_parse_args(av, vl, hp, NULL, nohost, 1, 1, nouseragent); - if (*av != NULL) - vtc_fatal(hp->vl, "Unknown http txreq spec: %s\n", *av); - http_write(hp, 4, "txreq"); - - if (up) { - parse_string(vl, hp, - "rxresp\n" - "expect resp.status == 101\n" - "expect resp.http.connection == Upgrade\n" - "expect resp.http.upgrade == h2c\n" - "txpri\n" - ); - b64_settings(hp, up); - parse_string(vl, hp, - "stream 0 {\n" - " txsettings\n" - " rxsettings\n" - " txsettings -ack\n" - " rxsettings\n" - " expect settings.ack == true" - "} -start\n" - ); - } -} - -/* SECTION: client-server.spec.recv - * - * recv NUMBER - * Read NUMBER bytes from the connection. - */ - -static void -cmd_http_recv(CMD_ARGS) -{ - struct http *hp; - int i, n; - char u[32]; - - (void)vl; - CAST_OBJ_NOTNULL(hp, priv, HTTP_MAGIC); - AN(av[1]); - AZ(av[2]); - n = strtoul(av[1], NULL, 0); - while (n > 0) { - i = read(hp->sess->fd, u, n > 32 ? 32 : n); - if (i > 0) - vtc_dump(hp->vl, 4, "recv", u, i); - else - vtc_log(hp->vl, hp->fatal, "recv() got %d (%s)", i, - strerror(errno)); - n -= i; - } -} - -/* SECTION: client-server.spec.send - * - * send STRING - * Push STRING on the connection. - */ - -static void -cmd_http_send(CMD_ARGS) -{ - struct http *hp; - int i; - - (void)vl; - CAST_OBJ_NOTNULL(hp, priv, HTTP_MAGIC); - AN(av[1]); - AZ(av[2]); - vtc_dump(hp->vl, 4, "send", av[1], -1); - i = write(hp->sess->fd, av[1], strlen(av[1])); - if (i != strlen(av[1])) - vtc_log(hp->vl, hp->fatal, "Write error in http_send(): %s", - strerror(errno)); -} - -/* SECTION: client-server.spec.send_n - * - * send_n NUMBER STRING - * Write STRING on the socket NUMBER times. - */ - -static void -cmd_http_send_n(CMD_ARGS) -{ - struct http *hp; - int i, n, l; - - (void)vl; - CAST_OBJ_NOTNULL(hp, priv, HTTP_MAGIC); - AN(av[1]); - AN(av[2]); - AZ(av[3]); - n = strtoul(av[1], NULL, 0); - vtc_dump(hp->vl, 4, "send_n", av[2], -1); - l = strlen(av[2]); - while (n--) { - i = write(hp->sess->fd, av[2], l); - if (i != l) - vtc_log(hp->vl, hp->fatal, - "Write error in http_send(): %s", - strerror(errno)); - } -} - -/* SECTION: client-server.spec.send_urgent - * - * send_urgent STRING - * Send string as TCP OOB urgent data. You will never need this. - */ - -static void -cmd_http_send_urgent(CMD_ARGS) -{ - struct http *hp; - int i; - - (void)vl; - CAST_OBJ_NOTNULL(hp, priv, HTTP_MAGIC); - AN(av[1]); - AZ(av[2]); - vtc_dump(hp->vl, 4, "send_urgent", av[1], -1); - i = send(hp->sess->fd, av[1], strlen(av[1]), MSG_OOB); - if (i != strlen(av[1])) - vtc_log(hp->vl, hp->fatal, - "Write error in http_send_urgent(): %s", strerror(errno)); -} - -/* SECTION: client-server.spec.sendhex - * - * sendhex STRING - * Send bytes as described by STRING. STRING should consist of hex pairs - * possibly separated by whitespace or newlines. For example: - * "0F EE a5 3df2". - */ - -static void -cmd_http_sendhex(CMD_ARGS) -{ - struct vsb *vsb; - struct http *hp; - - (void)vl; - CAST_OBJ_NOTNULL(hp, priv, HTTP_MAGIC); - AN(av[1]); - AZ(av[2]); - vsb = vtc_hex_to_bin(hp->vl, av[1]); - assert(VSB_len(vsb) >= 0); - vtc_hexdump(hp->vl, 4, "sendhex", VSB_data(vsb), VSB_len(vsb)); - if (VSB_tofile(vsb, hp->sess->fd)) - vtc_log(hp->vl, hp->fatal, "Write failed: %s", - strerror(errno)); - VSB_destroy(&vsb); -} - -/* SECTION: client-server.spec.chunked - * - * chunked STRING - * Send STRING as chunked encoding. - */ - -static void -cmd_http_chunked(CMD_ARGS) -{ - struct http *hp; - - (void)vl; - CAST_OBJ_NOTNULL(hp, priv, HTTP_MAGIC); - AN(av[1]); - AZ(av[2]); - VSB_clear(hp->vsb); - VSB_printf(hp->vsb, "%jx%s%s%s", - (uintmax_t)strlen(av[1]), nl, av[1], nl); - http_write(hp, 4, "chunked"); -} - -/* SECTION: client-server.spec.chunkedlen - * - * chunkedlen NUMBER - * Do as ``chunked`` except that the string will be generated - * for you, with a length of NUMBER characters. - */ - -static void -cmd_http_chunkedlen(CMD_ARGS) -{ - unsigned len; - unsigned u, v; - char buf[16384]; - struct http *hp; - - (void)vl; - CAST_OBJ_NOTNULL(hp, priv, HTTP_MAGIC); - AN(av[1]); - AZ(av[2]); - VSB_clear(hp->vsb); - - len = atoi(av[1]); - - if (len == 0) { - VSB_printf(hp->vsb, "0%s%s", nl, nl); - } else { - for (u = 0; u < sizeof buf; u++) - buf[u] = (u & 7) + '0'; - - VSB_printf(hp->vsb, "%x%s", len, nl); - for (u = 0; u < len; u += v) { - v = vmin_t(unsigned, len - u, sizeof buf); - VSB_bcat(hp->vsb, buf, v); - } - VSB_printf(hp->vsb, "%s", nl); - } - http_write(hp, 4, "chunked"); -} - - -/* SECTION: client-server.spec.timeout - * - * timeout NUMBER - * Set the TCP timeout for this entity. - */ - -static void -cmd_http_timeout(CMD_ARGS) -{ - struct http *hp; - double d; - - (void)vl; - CAST_OBJ_NOTNULL(hp, priv, HTTP_MAGIC); - AN(av[1]); - AZ(av[2]); - d = VNUM(av[1]); - if (isnan(d)) - vtc_fatal(vl, "timeout is not a number (%s)", av[1]); - hp->timeout = d; -} - -/* SECTION: client-server.spec.expect_close - * - * expect_close - * Reads from the connection, expecting nothing to read but an EOF. - */ -static void -cmd_http_expect_close(CMD_ARGS) -{ - struct http *hp; - struct pollfd fds[1]; - char c; - int i; - - (void)vl; - CAST_OBJ_NOTNULL(hp, priv, HTTP_MAGIC); - AZ(av[1]); - - vtc_log(vl, 4, "Expecting close (fd = %d)", hp->sess->fd); - if (hp->h2) - stop_h2(hp); - while (1) { - fds[0].fd = hp->sess->fd; - fds[0].events = POLLIN; - fds[0].revents = 0; - i = poll(fds, 1, (int)(hp->timeout * 1000)); - if (i < 0 && errno == EINTR) - continue; - if (i == 0) - vtc_log(vl, hp->fatal, "Expected close: timeout"); - if (i != 1 || !(fds[0].revents & (POLLIN|POLLERR|POLLHUP))) - vtc_log(vl, hp->fatal, - "Expected close: poll = %d, revents = 0x%x", - i, fds[0].revents); - i = read(hp->sess->fd, &c, 1); - if (i <= 0 && VTCP_Check(i)) - break; - if (i == 1 && vct_islws(c)) - continue; - vtc_log(vl, hp->fatal, - "Expecting close: read = %d, c = 0x%02x", i, c); - } - vtc_log(vl, 4, "fd=%d EOF, as expected", hp->sess->fd); -} - -/* SECTION: client-server.spec.close - * - * close (server only) - * Close the connection. Note that if operating in HTTP/2 mode no - * extra (GOAWAY) frame is sent, it's simply a TCP close. - */ -static void -cmd_http_close(CMD_ARGS) -{ - struct http *hp; - - (void)vl; - CAST_OBJ_NOTNULL(hp, priv, HTTP_MAGIC); - ONLY_SERVER(hp, av); - AZ(av[1]); - assert(hp->sfd != NULL); - assert(*hp->sfd >= 0); - if (hp->h2) - stop_h2(hp); - VTCP_close(&hp->sess->fd); - vtc_log(vl, 4, "Closed"); -} - -/* SECTION: client-server.spec.accept - * - * accept (server only) - * Close the current connection, if any, and accept a new one. Note - * that this new connection is HTTP/1.x. - */ -static void -cmd_http_accept(CMD_ARGS) -{ - struct http *hp; - - (void)vl; - CAST_OBJ_NOTNULL(hp, priv, HTTP_MAGIC); - ONLY_SERVER(hp, av); - AZ(av[1]); - assert(hp->sfd != NULL); - assert(*hp->sfd >= 0); - if (hp->h2) - stop_h2(hp); - if (hp->sess->fd >= 0) - VTCP_close(&hp->sess->fd); - vtc_log(vl, 4, "Accepting"); - hp->sess->fd = accept(*hp->sfd, NULL, NULL); - if (hp->sess->fd < 0) - vtc_log(vl, hp->fatal, "Accepted failed: %s", strerror(errno)); - vtc_log(vl, 3, "Accepted socket fd is %d", hp->sess->fd); -} - -/* SECTION: client-server.spec.shutdown - * - * shutdown - * Initiate shutdown. - * - * \-read - * Shutdown the read direction. - * \-write - * Shutdown the write direction. - * - * The default is both direction. - */ -static void -cmd_http_shutdown(CMD_ARGS) -{ - struct http *hp; - int how = SHUT_RDWR; - const char *str[] = { - [SHUT_RD] = "RD", - [SHUT_WR] = "WR", - [SHUT_RDWR] = "RDWR", - }; - - (void)vl; - CAST_OBJ_NOTNULL(hp, priv, HTTP_MAGIC); - AZ(strcmp(av[0], "shutdown")); - av++; - - if (*av != NULL) { - if (!strcmp(*av, "-read")) { - how = SHUT_RD; - av++; - } else if (!strcmp(*av, "-write")) { - how = SHUT_WR; - av++; - } - } - - if (*av != NULL) - vtc_fatal(hp->vl, "Unknown http shutdown spec: %s\n", *av); - - vtc_log(vl, 4, "Shutting down fd (%s): %d", str[how], hp->sess->fd); - if (shutdown(hp->sess->fd, how) < 0) - vtc_log(vl, hp->fatal, "Shutdown failed: %s", strerror(errno)); - vtc_log(vl, 3, "Shutdown socket fd (%d): %d", how, hp->sess->fd); -} - -/* SECTION: client-server.spec.fatal - * - * fatal|non_fatal - * Control whether a failure of this entity should stop the test. - */ - -static void -cmd_http_fatal(CMD_ARGS) -{ - struct http *hp; - CAST_OBJ_NOTNULL(hp, priv, HTTP_MAGIC); - - (void)vl; - AZ(av[1]); - if (!strcmp(av[0], "fatal")) { - hp->fatal = 0; - } else { - assert(!strcmp(av[0], "non_fatal")); - hp->fatal = -1; - } -} - -#define cmd_http_non_fatal cmd_http_fatal - -static const char PREFACE[24] = { - 0x50, 0x52, 0x49, 0x20, 0x2a, 0x20, 0x48, 0x54, - 0x54, 0x50, 0x2f, 0x32, 0x2e, 0x30, 0x0d, 0x0a, - 0x0d, 0x0a, 0x53, 0x4d, 0x0d, 0x0a, 0x0d, 0x0a -}; - -/* SECTION: client-server.spec.txpri - * - * txpri (client only) - * Send an HTTP/2 preface ("PRI * HTTP/2.0\\r\\n\\r\\nSM\\r\\n\\r\\n") - * and set client to HTTP/2. - */ -static void -cmd_http_txpri(CMD_ARGS) -{ - size_t l; - struct http *hp; - - CAST_OBJ_NOTNULL(hp, priv, HTTP_MAGIC); - ONLY_CLIENT(hp, av); - - vtc_dump(hp->vl, 4, "txpri", PREFACE, sizeof(PREFACE)); - /* Dribble out the preface */ - l = write(hp->sess->fd, PREFACE, 18); - if (l != 18) - vtc_log(vl, hp->fatal, "Write failed: (%zd vs %zd) %s", - l, sizeof(PREFACE), strerror(errno)); - VTIM_sleep(0.01); - l = write(hp->sess->fd, PREFACE + 18, sizeof(PREFACE) - 18); - if (l != sizeof(PREFACE) - 18) - vtc_log(vl, hp->fatal, "Write failed: (%zd vs %zd) %s", - l, sizeof(PREFACE), strerror(errno)); - - start_h2(hp); - AN(hp->h2); -} - -/* SECTION: client-server.spec.rxpri - * - * rxpri (server only) - * Receive a preface. If valid set the server to HTTP/2, abort - * otherwise. - */ -static void -cmd_http_rxpri(CMD_ARGS) -{ - struct http *hp; - - CAST_OBJ_NOTNULL(hp, priv, HTTP_MAGIC); - ONLY_SERVER(hp, av); - - hp->rx_p = hp->rx_b; - if (!http_rxchar(hp, sizeof(PREFACE), 0)) - vtc_fatal(vl, "Couldn't retrieve connection preface"); - if (memcmp(hp->rx_b, PREFACE, sizeof(PREFACE))) - vtc_fatal(vl, "Received invalid preface\n"); - start_h2(hp); - AN(hp->h2); -} - -/* SECTION: client-server.spec.settings - * - * settings -dectbl INT - * Force internal HTTP/2 settings to certain values. Currently only - * support setting the decoding table size. - */ -static void -cmd_http_settings(CMD_ARGS) -{ - uint32_t n; - char *p; - struct http *hp; - CAST_OBJ_NOTNULL(hp, priv, HTTP_MAGIC); - - if (!hp->h2) - vtc_fatal(hp->vl, "Only possible in H/2 mode"); - - CAST_OBJ_NOTNULL(hp, priv, HTTP_MAGIC); - - for (; *av != NULL; av++) { - if (!strcmp(*av, "-dectbl")) { - n = strtoul(av[1], &p, 0); - if (*p != '\0') - vtc_fatal(hp->vl, "-dectbl takes an integer as " - "argument (found %s)", av[1]); - assert(HPK_ResizeTbl(hp->decctx, n) != hpk_err); - av++; - } else - vtc_fatal(vl, "Unknown settings spec: %s\n", *av); - } -} - -static void -cmd_http_stream(CMD_ARGS) -{ - struct http *hp; - CAST_OBJ_NOTNULL(hp, priv, HTTP_MAGIC); - if (!hp->h2) { - vtc_log(hp->vl, 4, "Not in H/2 mode, do what's needed"); - if (hp->sfd) - parse_string(vl, hp, "rxpri"); - else - parse_string(vl, hp, "txpri"); - parse_string(vl, hp, - "stream 0 {\n" - " txsettings\n" - " rxsettings\n" - " txsettings -ack\n" - " rxsettings\n" - " expect settings.ack == true" - "} -run\n" - ); - } - cmd_stream(av, hp, vl); -} - -/* SECTION: client-server.spec.write_body - * - * write_body STRING - * Write the body of a request or a response to a file. By using the - * shell command, higher-level checks on the body can be performed - * (eg. XML, JSON, ...) provided that such checks can be delegated - * to an external program. - */ -static void -cmd_http_write_body(CMD_ARGS) -{ - struct http *hp; - - (void)vl; - CAST_OBJ_NOTNULL(hp, priv, HTTP_MAGIC); - AN(av[0]); - AN(av[1]); - AZ(av[2]); - AZ(strcmp(av[0], "write_body")); - if (VFIL_writefile(NULL, av[1], hp->body, hp->bodyl) != 0) - vtc_fatal(hp->vl, "failed to write body: %s (%d)", - strerror(errno), errno); -} - -/********************************************************************** - * Execute HTTP specifications - */ - -const struct cmds http_cmds[] = { -#define CMD_HTTP(n) { #n, cmd_http_##n }, - /* session */ - CMD_HTTP(accept) - CMD_HTTP(close) - CMD_HTTP(recv) - CMD_HTTP(send) - CMD_HTTP(send_n) - CMD_HTTP(send_urgent) - CMD_HTTP(sendhex) - CMD_HTTP(shutdown) - CMD_HTTP(timeout) - - /* spec */ - CMD_HTTP(fatal) - CMD_HTTP(non_fatal) - - /* body */ - CMD_HTTP(gunzip) - CMD_HTTP(write_body) - - /* HTTP/1.x */ - CMD_HTTP(chunked) - CMD_HTTP(chunkedlen) - CMD_HTTP(rxchunk) - - /* HTTP/2 */ - CMD_HTTP(stream) - CMD_HTTP(settings) - - /* client */ - CMD_HTTP(rxresp) - CMD_HTTP(rxrespbody) - CMD_HTTP(rxresphdrs) - CMD_HTTP(txpri) - CMD_HTTP(txreq) - - /* server */ - CMD_HTTP(rxpri) - CMD_HTTP(rxreq) - CMD_HTTP(rxreqbody) - CMD_HTTP(rxreqhdrs) - CMD_HTTP(txresp) - CMD_HTTP(upgrade) - - /* expect */ - CMD_HTTP(expect) - CMD_HTTP(expect_close) - CMD_HTTP(expect_pattern) -#undef CMD_HTTP - { NULL, NULL } -}; - -static void -http_process_cleanup(void *arg) -{ - struct http *hp; - - CAST_OBJ_NOTNULL(hp, arg, HTTP_MAGIC); - - if (hp->h2) - stop_h2(hp); - VSB_destroy(&hp->vsb); - free(hp->rx_b); - free(hp->rem_ip); - free(hp->rem_port); - free(hp->rem_path); - FREE_OBJ(hp); -} - -int -http_process(struct vtclog *vl, struct vtc_sess *vsp, const char *spec, - int sock, int *sfd, const char *addr, int rcvbuf) -{ - struct http *hp; - int retval, oldbuf; - socklen_t intlen = sizeof(int); - - (void)sfd; - ALLOC_OBJ(hp, HTTP_MAGIC); - AN(hp); - hp->sess = vsp; - hp->sess->fd = sock; - hp->timeout = vtc_maxdur * .5; - - if (rcvbuf) { - // XXX setsockopt() too late on SunOS - // https://github.com/varnishcache/varnish-cache/pull/2980#issuecomment-486214661 - hp->rcvbuf = rcvbuf; - - oldbuf = 0; - AZ(getsockopt(hp->sess->fd, SOL_SOCKET, SO_RCVBUF, &oldbuf, &intlen)); - AZ(setsockopt(hp->sess->fd, SOL_SOCKET, SO_RCVBUF, &rcvbuf, intlen)); - AZ(getsockopt(hp->sess->fd, SOL_SOCKET, SO_RCVBUF, &rcvbuf, &intlen)); - - vtc_log(vl, 3, "-rcvbuf fd=%d old=%d new=%d actual=%d", - hp->sess->fd, oldbuf, hp->rcvbuf, rcvbuf); - } - - hp->nrxbuf = 2048*1024; - hp->rx_b = malloc(hp->nrxbuf); - AN(hp->rx_b); - hp->rx_e = hp->rx_b + hp->nrxbuf; - hp->rx_p = hp->rx_b; - *hp->rx_p = '\0'; - - hp->vsb = VSB_new_auto(); - AN(hp->vsb); - - hp->sfd = sfd; - - hp->rem_ip = malloc(VTCP_ADDRBUFSIZE); - AN(hp->rem_ip); - - hp->rem_port = malloc(VTCP_PORTBUFSIZE); - AN(hp->rem_port); - - hp->vl = vl; - vtc_log_set_cmd(hp->vl, http_cmds); - hp->gziplevel = 0; - hp->gzipresidual = -1; - - if (*addr != '/') { - VTCP_hisname(sock, hp->rem_ip, VTCP_ADDRBUFSIZE, hp->rem_port, - VTCP_PORTBUFSIZE); - hp->rem_path = NULL; - } else { - strcpy(hp->rem_ip, "0.0.0.0"); - strcpy(hp->rem_port, "0"); - hp->rem_path = strdup(addr); - } - /* XXX: After an upgrade to HTTP/2 the cleanup of a server that is - * not -wait'ed before the test resets is subject to a race where the - * cleanup does not happen, so ASAN reports leaks despite the push - * of a cleanup handler. To easily reproduce, remove the server wait - * from a02022.vtc and run with ASAN enabled. - */ - pthread_cleanup_push(http_process_cleanup, hp); - parse_string(vl, hp, spec); - retval = hp->sess->fd; - pthread_cleanup_pop(0); - http_process_cleanup(hp); - return (retval); -} - -/********************************************************************** - * Magic test routine - * - * This function brute-forces some short strings through gzip(9) to - * find candidates for all possible 8 bit positions of the stopbit. - * - * Here is some good short output strings: - * - * 0 184 - * 1 257 <1ea86e6cf31bf4ec3d7a86> - * 2 106 <10> - * 3 163 - * 4 180 <71c5d18ec5d5d1> - * 5 189 <39886d28a6d2988> - * 6 118 <80000> - * 7 151 <386811868> - * - */ - -#if 0 -void xxx(void); - -void -xxx(void) -{ - z_stream vz; - int n; - char ibuf[200]; - char obuf[200]; - int fl[8]; - int i, j; - - for (n = 0; n < 8; n++) - fl[n] = 9999; - - memset(&vz, 0, sizeof vz); - - for (n = 0; n < 999999999; n++) { - *ibuf = 0; - for (j = 0; j < 7; j++) { - snprintf(strchr(ibuf, 0), 5, "%x", - (unsigned)VRND_RandomTestable() & 0xffff); - vz.next_in = TRUST_ME(ibuf); - vz.avail_in = strlen(ibuf); - vz.next_out = TRUST_ME(obuf); - vz.avail_out = sizeof obuf; - assert(Z_OK == deflateInit2(&vz, - 9, Z_DEFLATED, 31, 9, Z_DEFAULT_STRATEGY)); - assert(Z_STREAM_END == deflate(&vz, Z_FINISH)); - i = vz.stop_bit & 7; - if (fl[i] > strlen(ibuf)) { - printf("%d %jd <%s>\n", i, vz.stop_bit, ibuf); - fl[i] = strlen(ibuf); - } - assert(Z_OK == deflateEnd(&vz)); - } - } - - printf("FOO\n"); -} -#endif diff --git a/bin/varnishtest/vtc_http.h b/bin/varnishtest/vtc_http.h deleted file mode 100644 index 62c598a55..000000000 --- a/bin/varnishtest/vtc_http.h +++ /dev/null @@ -1,101 +0,0 @@ -/*- - * Copyright (c) 2008-2015 Varnish Software AS - * All rights reserved. - * - * Author: Poul-Henning Kamp - * - * SPDX-License-Identifier: BSD-2-Clause - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#define MAX_HDR 64 - -struct vtc_sess { - unsigned magic; -#define VTC_SESS_MAGIC 0x932bd565 - struct vtclog *vl; - char *name; - int repeat; - int keepalive; - int fd; - - ssize_t rcvbuf; -}; - -struct h2_window { - uint64_t init; - int64_t size; -}; - -struct http { - unsigned magic; -#define HTTP_MAGIC 0x2f02169c - int *sfd; - struct vtc_sess *sess; - vtim_dur timeout; - struct vtclog *vl; - - struct vsb *vsb; - - int rcvbuf; - int nrxbuf; - char *rx_b; - char *rx_p; - char *rx_e; - char *rem_ip; - char *rem_port; - char *rem_path; - char *body; - long bodyl; - char bodylen[20]; - char chunklen[20]; - - char *req[MAX_HDR]; - char *resp[MAX_HDR]; - - int gziplevel; - int gzipresidual; - - int head_method; - - int fatal; - - /* H/2 */ - unsigned h2; - int wf; - int no_rfc7540_priorities; - - pthread_t tp; - VTAILQ_HEAD(, stream) streams; - unsigned last_stream; - pthread_mutex_t mtx; - pthread_cond_t cond; - struct hpk_ctx *encctx; - struct hpk_ctx *decctx; - struct h2_window h2_win_self[1]; - struct h2_window h2_win_peer[1]; -}; - -int http_process(struct vtclog *vl, struct vtc_sess *vsp, const char *spec, - int sock, int *sfd, const char *addr, int rcvbuf); - diff --git a/bin/varnishtest/vtc_http2.c b/bin/varnishtest/vtc_http2.c deleted file mode 100644 index 827e493fc..000000000 --- a/bin/varnishtest/vtc_http2.c +++ /dev/null @@ -1,2978 +0,0 @@ -/*- - * Copyright (c) 2008-2016 Varnish Software AS - * All rights reserved. - * - * Author: Guillaume Quintard - * - * SPDX-License-Identifier: BSD-2-Clause - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#include "config.h" - -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -#include "vtc.h" -#include "vtc_http.h" - -#include "vfil.h" -#include "hpack.h" -#include "vend.h" - -#define ERR_MAX 13 -#define BUF_SIZE (1024*2048) - -static const char *const h2_errs[] = { -#define H2_ERROR(n,v,sc,g,r,t) [v] = #n, -#include - NULL -}; - -static const char *const h2_types[] = { -#define H2_FRAME(l,u,t,f,...) [t] = #u, -#include - NULL -}; - -static const char * const h2_settings[] = { - [0] = "unknown", -#define H2_SETTING(U,l,v,...) [v] = #U, -#include - NULL -}; - -enum h2_settings_e { -#define H2_SETTING(U,l,v,...) SETTINGS_##U = v, -#include - SETTINGS_MAX -}; - - -enum h2_type_e { -#define H2_FRAME(l,u,t,f,...) TYPE_##u = t, -#include - TYPE_MAX -}; - -//lint -save -e849 Same enum value -enum { - ACK = 0x1, - END_STREAM = 0x1, - PADDED = 0x8, - END_HEADERS = 0x4, - PRIORITY = 0x20, -}; -//lint -restore - -struct stream { - unsigned magic; -#define STREAM_MAGIC 0x63f1fac2 - uint32_t id; - struct vtclog *vl; - char *spec; - char *name; - VTAILQ_ENTRY(stream) list; - unsigned running; - pthread_cond_t cond; - struct frame *frame; - pthread_t tp; - struct http *hp; - int64_t win_self; - int64_t win_peer; - int wf; - - VTAILQ_HEAD(, frame) fq; - - char *body; - long bodylen; - struct hpk_hdr req[MAX_HDR]; - struct hpk_hdr resp[MAX_HDR]; - - int dependency; - int weight; -}; - -static void -clean_headers(struct hpk_hdr *h) -{ - unsigned n = MAX_HDR; - - while (h->t && n > 0) { - if (h->key.len) - free(h->key.ptr); - if (h->value.len) - free(h->value.ptr); - memset(h, 0, sizeof(*h)); - h++; - n--; - } -} - -#define ONLY_H2_CLIENT(hp, av) \ - do { \ - if (hp->sfd != NULL) \ - vtc_fatal(s->vl, \ - "\"%s\" only possible in client", av[0]); \ - } while (0) - -#define ONLY_H2_SERVER(hp, av) \ - do { \ - if (hp->sfd == NULL) \ - vtc_fatal(s->vl, \ - "\"%s\" only possible in server", av[0]); \ - } while (0) - -static void -http_write(const struct http *hp, int lvl, - const char *buf, int s, const char *pfx) -{ - ssize_t l; - - CHECK_OBJ_NOTNULL(hp, HTTP_MAGIC); - AN(buf); - AN(pfx); - - vtc_dump(hp->vl, lvl, pfx, buf, s); - l = write(hp->sess->fd, buf, s); - if (l != s) - vtc_log(hp->vl, hp->fatal, "Write failed: (%zd vs %d) %s", - l, s, strerror(errno)); -} - -static int -get_bytes(const struct http *hp, char *buf, size_t n) -{ - int i; - struct pollfd pfd[1]; - - CHECK_OBJ_NOTNULL(hp, HTTP_MAGIC); - AN(buf); - - while (n > 0) { - pfd[0].fd = hp->sess->fd; - pfd[0].events = POLLIN; - pfd[0].revents = 0; - i = poll(pfd, 1, (int)(hp->timeout * 1000)); - if (i < 0 && errno == EINTR) - continue; - if (i == 0) - vtc_log(hp->vl, 3, - "HTTP2 rx timeout (fd:%d %.3fs)", - hp->sess->fd, hp->timeout); - if (i < 0) - vtc_log(hp->vl, 3, - "HTTP2 rx failed (fd:%d poll: %s)", - hp->sess->fd, strerror(errno)); - if (i <= 0) - return (i); - i = read(hp->sess->fd, buf, n); - if (!(pfd[0].revents & POLLIN)) - vtc_log(hp->vl, 4, - "HTTP2 rx poll (fd:%d revents: %x n=%zu, i=%d)", - hp->sess->fd, pfd[0].revents, n, i); - if (i == 0) - vtc_log(hp->vl, 3, - "HTTP2 rx EOF (fd:%d read: %s)", - hp->sess->fd, strerror(errno)); - if (i < 0) - vtc_log(hp->vl, 3, - "HTTP2 rx failed (fd:%d read: %s)", - hp->sess->fd, strerror(errno)); - if (i <= 0) - return (i); - n -= i; - } - return (1); - -} - -VTAILQ_HEAD(fq_head, frame); - -struct frame { - unsigned magic; -#define FRAME_MAGIC 0x5dd3ec4 - uint32_t size; - uint32_t stid; - uint8_t type; - uint8_t flags; - char *data; - - VTAILQ_ENTRY(frame) list; - - union { - struct { - uint32_t stream; - uint8_t exclusive; - uint8_t weight; - } prio; - uint32_t rst_err; - double settings[SETTINGS_MAX+1]; - struct { - char data[9]; - int ack; - } ping; - struct { - uint32_t err; - uint32_t stream; - char *debug; - } goaway; - uint32_t winup_size; - uint32_t promised; - uint8_t padded; - } md; -}; - -static void -readFrameHeader(struct frame *f, const char *buf) -{ - CHECK_OBJ_NOTNULL(f, FRAME_MAGIC); - AN(buf); - - f->size = (unsigned char)buf[0] << 16; - f->size += (unsigned char)buf[1] << 8; - f->size += (unsigned char)buf[2]; - - f->type = (unsigned char)buf[3]; - - f->flags = (unsigned char)buf[4]; - - f->stid = vbe32dec(buf+5); -} - -static void -writeFrameHeader(char *buf, const struct frame *f) -{ - CHECK_OBJ_NOTNULL(f, FRAME_MAGIC); - AN(buf); - buf[0] = (f->size >> 16) & 0xff; - buf[1] = (f->size >> 8) & 0xff; - buf[2] = (f->size ) & 0xff; - - buf[3] = f->type; - - buf[4] = f->flags; - - vbe32enc(buf + 5, f->stid); -} - -#define INIT_FRAME(f, ty, sz, id, fl) \ -do { \ - f.magic = FRAME_MAGIC; \ - f.type = TYPE_ ## ty; \ - f.size = sz; \ - f.stid = id; \ - f.flags = fl; \ - f.data = NULL; \ -} while(0) - -static void -replace_frame(struct frame **fp, struct frame *new) -{ - struct frame *old; - - AN(fp); - CHECK_OBJ_ORNULL(new, FRAME_MAGIC); - - old = *fp; - *fp = new; - if (old == NULL) - return; - - CHECK_OBJ(old, FRAME_MAGIC); - if (old->type == TYPE_GOAWAY) - free(old->md.goaway.debug); - free(old->data); - FREE_OBJ(old); -} - -static void -clean_frame(struct frame **fp) -{ - - replace_frame(fp, NULL); -} - -static void -write_frame(struct stream *sp, const struct frame *f, const unsigned lock) -{ - struct http *hp; - ssize_t l; - char hdr[9]; - - CHECK_OBJ_NOTNULL(sp, STREAM_MAGIC); - hp = sp->hp; - CHECK_OBJ_NOTNULL(hp, HTTP_MAGIC); - CHECK_OBJ_NOTNULL(f, FRAME_MAGIC); - - writeFrameHeader(hdr, f); - - vtc_log(sp->vl, 3, - "tx: stream: %d, type: %s (%d), flags: 0x%02x, size: %d", - f->stid, - f->type < TYPE_MAX ? h2_types[f->type] : "?", - f->type, f->flags, f->size); - - if (f->type == TYPE_DATA) { - sp->win_peer -= f->size; - hp->h2_win_peer->size -= f->size; - } - - if (lock) - PTOK(pthread_mutex_lock(&hp->mtx)); - l = write(hp->sess->fd, hdr, sizeof(hdr)); - if (l != sizeof(hdr)) - vtc_log(sp->vl, hp->fatal, "Write failed: (%zd vs %zd) %s", - l, sizeof(hdr), strerror(errno)); - - if (f->size) { - AN(f->data); - l = write(hp->sess->fd, f->data, f->size); - if (l != f->size) - vtc_log(sp->vl, hp->fatal, - "Write failed: (%zd vs %d) %s", - l, f->size, strerror(errno)); - } - if (lock) - PTOK(pthread_mutex_unlock(&hp->mtx)); -} - -static void -exclusive_stream_dependency(const struct stream *s) -{ - struct stream *target; - struct http *hp = s->hp; - - if (s->id == 0) - return; - - VTAILQ_FOREACH(target, &hp->streams, list) { - if (target->id != s->id && target->dependency == s->dependency) - target->dependency = s->id; - } -} - -static void -explain_flags(uint8_t flags, uint8_t type, struct vtclog *vl) -{ - if (flags & ACK && (type == TYPE_PING || type == TYPE_SETTINGS)) { - vtc_log(vl, 3, "flag: ACK"); - } else if (flags & END_STREAM && (type == TYPE_HEADERS || - type == TYPE_PUSH_PROMISE || type == TYPE_DATA)) { - vtc_log(vl, 3, "flag: END_STREAM"); - } else if (flags & END_HEADERS && (type == TYPE_HEADERS || - type == TYPE_PUSH_PROMISE || type == TYPE_CONTINUATION)) { - vtc_log(vl, 3, "flag: END_TYPE_HEADERS"); - } else if (flags & PRIORITY && (type == TYPE_HEADERS || - type == TYPE_PUSH_PROMISE)) { - vtc_log(vl, 3, "flag: END_PRIORITY"); - } else if (flags & PADDED && (type == TYPE_DATA || type == - TYPE_HEADERS || type == TYPE_PUSH_PROMISE)) { - vtc_log(vl, 3, "flag: PADDED"); - } else if (flags) - vtc_log(vl, 3, "UNKNOWN FLAG(S): 0x%02x", flags); -} - -static void -parse_data(struct stream *s, struct frame *f) -{ - struct http *hp; - uint32_t size = f->size; - char *data = f->data; - - CHECK_OBJ_NOTNULL(f, FRAME_MAGIC); - CHECK_OBJ_NOTNULL(s, STREAM_MAGIC); - CAST_OBJ_NOTNULL(hp, s->hp, HTTP_MAGIC);; - - if (f->flags & PADDED) { - f->md.padded = *((uint8_t *)data); - if (f->md.padded >= size) { - vtc_log(s->vl, hp->fatal, - "invalid padding: %d reported," - "but size is only %d", - f->md.padded, size); - size = 0; - f->md.padded = 0; - } - data++; - size -= f->md.padded + 1; - vtc_log(s->vl, 4, "padding: %3d", f->md.padded); - } - - if (s->id) - s->win_self -= size; - - s->hp->h2_win_self->size -= size; - - if (!size) { - AZ(data); - vtc_log(s->vl, 4, "s%u - no data", s->id); - return; - } - - s->body = realloc(s->body, s->bodylen + size + 1L); - AN(s->body); - memcpy(s->body + s->bodylen, data, size); - s->bodylen += size; - s->body[s->bodylen] = '\0'; -} - -static void -decode_hdr(struct http *hp, struct hpk_hdr *h, const struct vsb *vsb) -{ - struct hpk_iter *iter; - enum hpk_result r = hpk_err; - int n; - - CHECK_OBJ_NOTNULL(vsb, VSB_MAGIC); - CAST_OBJ_NOTNULL(hp, hp, HTTP_MAGIC);; - - if (VSB_len(vsb) == 0) - return; - - iter = HPK_NewIter(hp->decctx, VSB_data(vsb), VSB_len(vsb)); - - n = 0; - while (n < MAX_HDR && h[n].t) - n++; - while (n < MAX_HDR) { - r = HPK_DecHdr(iter, h + n); - if (r == hpk_err ) - break; - vtc_log(hp->vl, 4, "header[%2d]: %s: %s", - n, h[n].key.ptr, h[n].value.ptr); - n++; - if (r == hpk_done) - break; - } - - if (r != hpk_done) { - vtc_log(hp->vl, hp->fatal ? 4 : 0, - "Header decoding failed (%d) %d", r, hp->fatal); - } else if (n == MAX_HDR) { - vtc_log(hp->vl, hp->fatal, - "Max number of headers reached (%d)", MAX_HDR); - } - - HPK_FreeIter(iter); -} - -static void -parse_hdr(struct stream *s, struct frame *f, struct vsb *vsb) -{ - int shift = 0; - int exclusive = 0; - uint32_t size = f->size; - char *data = f->data; - struct http *hp; - uint32_t n; - - CHECK_OBJ_NOTNULL(f, FRAME_MAGIC); - CHECK_OBJ_NOTNULL(s, STREAM_MAGIC); - CHECK_OBJ_NOTNULL(vsb, VSB_MAGIC); - CAST_OBJ_NOTNULL(hp, s->hp, HTTP_MAGIC);; - - if (f->flags & PADDED && f->type != TYPE_CONTINUATION) { - f->md.padded = *((uint8_t *)data); - if (f->md.padded >= size) { - vtc_log(s->vl, hp->fatal, - "invalid padding: %d reported," - "but size is only %d", - f->md.padded, size); - size = 0; - f->md.padded = 0; - } - shift += 1; - size -= f->md.padded; - vtc_log(s->vl, 4, "padding: %3d", f->md.padded); - } - - if (f->type == TYPE_HEADERS && f->flags & PRIORITY){ - shift += 5; - n = vbe32dec(f->data); - s->dependency = n & ~(1U << 31); - exclusive = n >> 31; - - s->weight = f->data[4]; - if (exclusive) - exclusive_stream_dependency(s); - - vtc_log(s->vl, 4, "stream->dependency: %u", s->dependency); - vtc_log(s->vl, 4, "stream->weight: %u", s->weight); - } else if (f->type == TYPE_PUSH_PROMISE){ - shift += 4; - n = vbe32dec(f->data); - f->md.promised = n & ~(1U << 31); - } - - AZ(VSB_bcat(vsb, data + shift, size - shift)); -} - -static void -parse_prio(struct stream *s, struct frame *f) -{ - struct http *hp; - char *buf; - uint32_t n; - - CHECK_OBJ_NOTNULL(f, FRAME_MAGIC); - CHECK_OBJ_NOTNULL(s, STREAM_MAGIC); - CAST_OBJ_NOTNULL(hp, s->hp, HTTP_MAGIC);; - - if (f->size != 5) - vtc_fatal(s->vl, "Size should be 5, but isn't (%d)", f->size); - - buf = f->data; - AN(buf); - - n = vbe32dec(f->data); - f->md.prio.stream = n & ~(1U << 31); - - s->dependency = f->md.prio.stream; - if (n >> 31){ - f->md.prio.exclusive = 1; - exclusive_stream_dependency(s); - } - - buf += 4; - f->md.prio.weight = *buf; - s->weight = f->md.prio.weight; - - vtc_log(s->vl, 3, "prio->stream: %u", f->md.prio.stream); - vtc_log(s->vl, 3, "prio->weight: %u", f->md.prio.weight); -} - -static void -parse_rst(const struct stream *s, struct frame *f) -{ - struct http *hp; - uint32_t err; - const char *buf; - CHECK_OBJ_NOTNULL(f, FRAME_MAGIC); - CHECK_OBJ_NOTNULL(s, STREAM_MAGIC); - CAST_OBJ_NOTNULL(hp, s->hp, HTTP_MAGIC);; - - if (f->size != 4) - vtc_fatal(s->vl, "Size should be 4, but isn't (%d)", f->size); - - err = vbe32dec(f->data); - f->md.rst_err = err; - - vtc_log(s->vl, 2, "ouch"); - if (err <= ERR_MAX) - buf = h2_errs[err]; - else - buf = "unknown"; - vtc_log(s->vl, 4, "rst->err: %s (%d)", buf, err); - -} - -static void -parse_settings(const struct stream *s, struct frame *f) -{ - struct http *hp; - int v; - unsigned u, t; - const char *buf; - enum hpk_result r; - CHECK_OBJ_NOTNULL(f, FRAME_MAGIC); - CHECK_OBJ_NOTNULL(s, STREAM_MAGIC); - CAST_OBJ_NOTNULL(hp, s->hp, HTTP_MAGIC);; - - if (f->size % 6) - vtc_fatal(s->vl, - "Size should be a multiple of 6, but isn't (%d)", f->size); - - if (s->id != 0) - vtc_fatal(s->vl, - "Setting frames should only be on stream 0, but received on stream: %d", s->id); - - for (u = 0; u <= SETTINGS_MAX; u++) - f->md.settings[u] = NAN; - - for (u = 0; u < f->size;) { - t = vbe16dec(f->data + u); - u += 2; - v = vbe32dec(f->data + u); - if (t <= SETTINGS_MAX) { - buf = h2_settings[t]; - f->md.settings[t] = v; - } else - buf = "unknown"; - u += 4; - - if (t == SETTINGS_HEADER_TABLE_SIZE) { - r = HPK_ResizeTbl(s->hp->encctx, v); - assert(r == hpk_done); - } - - vtc_log(s->vl, 4, "settings->%s (%u): %d", buf, t, v); - } - -} - -static void -parse_ping(const struct stream *s, struct frame *f) -{ - struct http *hp; - CHECK_OBJ_NOTNULL(f, FRAME_MAGIC); - CHECK_OBJ_NOTNULL(s, STREAM_MAGIC); - CAST_OBJ_NOTNULL(hp, s->hp, HTTP_MAGIC);; - if (f->size != 8) - vtc_fatal(s->vl, "Size should be 8, but isn't (%d)", f->size); - f->md.ping.ack = f->flags & ACK; - memcpy(f->md.ping.data, f->data, 8); - f->md.ping.data[8] = '\0'; - - vtc_log(s->vl, 4, "ping->data: %s", f->md.ping.data); - -} - -static void -parse_goaway(const struct stream *s, struct frame *f) -{ - struct http *hp; - const char *err_buf; - uint32_t err, stid; - CHECK_OBJ_NOTNULL(f, FRAME_MAGIC); - CHECK_OBJ_NOTNULL(s, STREAM_MAGIC); - CAST_OBJ_NOTNULL(hp, s->hp, HTTP_MAGIC);; - - if (f->size < 8) - vtc_fatal(s->vl, - "Size should be at least 8, but isn't (%d)", f->size); - if (f->data[0] & (1<<7)) - vtc_fatal(s->vl, - "First bit of data is reserved and should be 0"); - - stid = vbe32dec(f->data); - err = vbe32dec(f->data + 4); - f->md.goaway.err = err; - f->md.goaway.stream = stid; - - if (err <= ERR_MAX) - err_buf = h2_errs[err]; - else - err_buf = "unknown"; - - if (f->size > 8) { - f->md.goaway.debug = malloc((f->size - 8) + 1L); - AN(f->md.goaway.debug); - f->md.goaway.debug[f->size - 8] = '\0'; - - memcpy(f->md.goaway.debug, f->data + 8, f->size - 8); - } - - vtc_log(s->vl, 3, "goaway->laststream: %d", stid); - vtc_log(s->vl, 3, "goaway->err: %s (%d)", err_buf, err); - if (f->md.goaway.debug) - vtc_log(s->vl, 3, "goaway->debug: %s", f->md.goaway.debug); -} - -static void -parse_winup(const struct stream *s, struct frame *f) -{ - struct http *hp; - uint32_t size; - CHECK_OBJ_NOTNULL(f, FRAME_MAGIC); - CHECK_OBJ_NOTNULL(s, STREAM_MAGIC); - CAST_OBJ_NOTNULL(hp, s->hp, HTTP_MAGIC);; - - if (f->size != 4) - vtc_fatal(s->vl, "Size should be 4, but isn't (%d)", f->size); - if (f->data[0] & (1<<7)) - vtc_log(s->vl, s->hp->fatal, - "First bit of data is reserved and should be 0"); - - size = vbe32dec(f->data); - f->md.winup_size = size; - - vtc_log(s->vl, 3, "winup->size: %d", size); -} - -/* read a frame and queue it in the relevant stream, wait if not present yet. - */ -static void * -receive_frame(void *priv) -{ - struct http *hp; - char hdr[9]; - struct frame *f; - struct stream *s; - int expect_cont = 0; - struct vsb *vsb = NULL; - struct hpk_hdr *hdrs = NULL; - - CAST_OBJ_NOTNULL(hp, priv, HTTP_MAGIC); - - PTOK(pthread_mutex_lock(&hp->mtx)); - while (hp->h2) { - /*no wanted frames? */ - assert(hp->wf >= 0); - if (hp->wf == 0) { - PTOK(pthread_cond_wait(&hp->cond, &hp->mtx)); - continue; - } - PTOK(pthread_mutex_unlock(&hp->mtx)); - - if (get_bytes(hp, hdr, sizeof hdr) <= 0) { - PTOK(pthread_mutex_lock(&hp->mtx)); - VTAILQ_FOREACH(s, &hp->streams, list) - PTOK(pthread_cond_signal(&s->cond)); - PTOK(pthread_mutex_unlock(&hp->mtx)); - vtc_log(hp->vl, hp->fatal, - "could not get frame header"); - return (NULL); - } - ALLOC_OBJ(f, FRAME_MAGIC); - AN(f); - readFrameHeader(f, hdr); - - vtc_log(hp->vl, 3, "rx: stream: %d, type: %s (%d), " - "flags: 0x%02x, size: %d", - f->stid, - f->type < TYPE_MAX ? h2_types[f->type] : "?", - f->type, f->flags, f->size); - explain_flags(f->flags, f->type, hp->vl); - - if (f->size) { - f->data = malloc(f->size + 1L); - AN(f->data); - f->data[f->size] = '\0'; - if (get_bytes(hp, f->data, f->size) <= 0) { - PTOK(pthread_mutex_lock(&hp->mtx)); - VTAILQ_FOREACH(s, &hp->streams, list) - PTOK(pthread_cond_signal(&s->cond)); - clean_frame(&f); - PTOK(pthread_mutex_unlock(&hp->mtx)); - vtc_log(hp->vl, hp->fatal, - "could not get frame body"); - return (NULL); - } - } - - /* is the corresponding stream waiting? */ - PTOK(pthread_mutex_lock(&hp->mtx)); - s = NULL; - while (!s) { - VTAILQ_FOREACH(s, &hp->streams, list) - if (s->id == f->stid) - break; - if (!s) - PTOK(pthread_cond_wait(&hp->cond, &hp->mtx)); - if (!hp->h2) { - clean_frame(&f); - PTOK(pthread_mutex_unlock(&hp->mtx)); - return (NULL); - } - } - PTOK(pthread_mutex_unlock(&hp->mtx)); - - AN(s); - if (expect_cont && - (f->type != TYPE_CONTINUATION || expect_cont != s->id)) - vtc_fatal(s->vl, "Expected CONTINUATION frame for " - "stream %u", expect_cont); - - /* parse the frame according to it type, and fill the metadata */ - switch (f->type) { - case TYPE_DATA: - parse_data(s, f); - break; - case TYPE_PUSH_PROMISE: - hdrs = s->req; - /*FALLTHROUGH*/ - case TYPE_HEADERS: - if (!hdrs) { - if (hp->sfd) - hdrs = s->req; - else - hdrs = s->resp; - } - clean_headers(hdrs); - hdrs[0].t = hpk_unset; - AZ(vsb); - vsb = VSB_new_auto(); - /*FALLTHROUGH*/ - case TYPE_CONTINUATION: - AN(hdrs); - expect_cont = s->id; - parse_hdr(s, f, vsb); - if (f->flags & END_HEADERS) { - expect_cont = 0; - AZ(VSB_finish(vsb)); - decode_hdr(hp, hdrs, vsb); - VSB_destroy(&vsb); - hdrs = NULL; - } - break; - case TYPE_PRIORITY: - parse_prio(s, f); - break; - case TYPE_RST_STREAM: - parse_rst(s, f); - break; - case TYPE_SETTINGS: - parse_settings(s, f); - break; - case TYPE_PING: - parse_ping(s, f); - break; - case TYPE_GOAWAY: - parse_goaway(s, f); - break; - case TYPE_WINDOW_UPDATE: - parse_winup(s, f); - break; - default: - WRONG("wrong frame type"); - } - - PTOK(pthread_mutex_lock(&hp->mtx)); - VTAILQ_INSERT_HEAD(&s->fq, f, list); - if (s->wf) { - assert(hp->wf > 0); - hp->wf--; - s->wf = 0; - PTOK(pthread_cond_signal(&s->cond)); - } - continue; - } - PTOK(pthread_mutex_unlock(&hp->mtx)); - if (vsb != NULL) - VSB_destroy(&vsb); - return (NULL); -} - -#define STRTOU32(n, ss, p, v, c) \ - do { \ - n = strtoul(ss, &p, 0); \ - if (*p != '\0') \ - vtc_fatal(v, "%s takes an integer as argument " \ - "(found %s)", c, ss); \ - } while (0) - -#define STRTOU32_CHECK(n, sp, p, v, c, l) \ -do { \ - sp++; \ - AN(*sp); \ - STRTOU32(n, *sp, p, v, c); \ - if (l && n >= (1U << l)) \ - vtc_fatal(v, \ - c " must be a %d-bits integer (found %s)", l, *sp); \ -} while (0) - -#define CHECK_LAST_FRAME(TYPE) \ - if (!f || f->type != TYPE_ ## TYPE) { \ - vtc_fatal(s->vl, "Last frame was not of type " #TYPE); \ - } - -#define RETURN_SETTINGS(idx) \ -do { \ - if (isnan(f->md.settings[idx])) { \ - return (NULL); \ - } \ - snprintf(buf, 20, "%.0f", f->md.settings[idx]); \ - return (buf); \ -} while (0) - -#define RETURN_BUFFED(val) \ -do { \ - snprintf(buf, 20, "%ld", (long)val); \ - return (buf); \ -} while (0) - -static char * -find_header(const struct hpk_hdr *h, const char *k) -{ - AN(k); - - int kl = strlen(k); - while (h->t) { - if (kl == h->key.len && !strncasecmp(h->key.ptr, k, kl)) - return (h->value.ptr); - h++; - } - return (NULL); -} -/* SECTION: stream.spec.zexpect expect - * - * expect in stream works as it does in client or server, except that the - * elements compared will be different. - * - * Most of these elements will be frame specific, meaning that the last frame - * received on that stream must of the correct type. - * - * Here the list of keywords you can look at. - */ -static const char * -cmd_var_resolve(const struct stream *s, const char *spec, char *buf) -{ - uint32_t idx; - int n; - const struct hpk_hdr *h; - struct hpk_ctx *ctx; - struct frame *f = s->frame; - - CHECK_OBJ_NOTNULL(s, STREAM_MAGIC); - CHECK_OBJ_NOTNULL(s->hp, HTTP_MAGIC); - AN(spec); - AN(buf); - - n = 0; - /* SECTION: stream.spec.zexpect.ping PING specific - * - * ping.data - * The 8-bytes string of the PING frame payload. - * ping.ack (PING) - * "true" if the ACK flag was set, "false" otherwise. - */ - if (!strcmp(spec, "ping.data")) { - CHECK_LAST_FRAME(PING); - return (f->md.ping.data); - } - if (!strcmp(spec, "ping.ack")) { - CHECK_LAST_FRAME(PING); - snprintf(buf, 20, (f->flags & ACK) ? "true" : "false"); - return (buf); - } - /* SECTION: stream.spec.zexpect.winup WINDOW_UPDATE specific - * - * winup.size - * The size of the upgrade given by the WINDOW_UPDATE frame. - */ - if (!strcmp(spec, "winup.size")) { - CHECK_LAST_FRAME(WINDOW_UPDATE); - RETURN_BUFFED(f->md.winup_size); - } - /* SECTION: stream.spec.zexpect.prio PRIORITY specific - * - * prio.stream - * The stream ID announced. - * - * prio.exclusive - * "true" if the priority is exclusive, else "false". - * - * prio.weight - * The dependency weight. - */ - if (!strcmp(spec, "prio.stream")) { - CHECK_LAST_FRAME(PRIORITY); - RETURN_BUFFED(f->md.prio.stream); - } - if (!strcmp(spec, "prio.exclusive")) { - CHECK_LAST_FRAME(PRIORITY); - snprintf(buf, 20, f->md.prio.exclusive ? "true" : "false"); - return (buf); - } - if (!strcmp(spec, "prio.weight")) { - CHECK_LAST_FRAME(PRIORITY); - RETURN_BUFFED(f->md.prio.weight); - } - /* SECTION: stream.spec.zexpect.rst RESET_STREAM specific - * - * rst.err - * The error code (as integer) of the RESET_STREAM frame. - */ - if (!strcmp(spec, "rst.err")) { - CHECK_LAST_FRAME(RST_STREAM); - RETURN_BUFFED(f->md.rst_err); - } - /* SECTION: stream.spec.zexpect.settings SETTINGS specific - * - * settings.ack - * "true" if the ACK flag was set, else "false". - * - * settings.push - * "true" if the push settings was set to yes, "false" if set to - * no, and if not present. - * - * settings.hdrtbl - * Value of HEADER_TABLE_SIZE if set, otherwise. - * - * settings.maxstreams - * Value of MAX_CONCURRENT_STREAMS if set, otherwise. - * - * settings.winsize - * Value of INITIAL_WINDOW_SIZE if set, otherwise. - * - * setting.framesize - * Value of MAX_FRAME_SIZE if set, otherwise. - * - * settings.hdrsize - * Value of MAX_HEADER_LIST_SIZE if set, otherwise. - */ - if (!strncmp(spec, "settings.", 9)) { - CHECK_LAST_FRAME(SETTINGS); - spec += 9; - if (!strcmp(spec, "ack")) { - snprintf(buf, 20, (f->flags & ACK) ? "true" : "false"); - return (buf); - } - if (!strcmp(spec, "push")) { - if (isnan(f->md.settings[SETTINGS_ENABLE_PUSH])) - return (NULL); - else if (f->md.settings[SETTINGS_ENABLE_PUSH] == 1) - snprintf(buf, 20, "true"); - else - snprintf(buf, 20, "false"); - return (buf); - } - if (!strcmp(spec, "hdrtbl")) { RETURN_SETTINGS(1); } - if (!strcmp(spec, "maxstreams")) { RETURN_SETTINGS(3); } - if (!strcmp(spec, "winsize")) { RETURN_SETTINGS(4); } - if (!strcmp(spec, "framesize")) { RETURN_SETTINGS(5); } - if (!strcmp(spec, "hdrsize")) { RETURN_SETTINGS(6); } - } - /* SECTION: stream.spec.zexpect.push PUSH_PROMISE specific - * - * push.id - * The id of the promised stream. - */ - if (!strcmp(spec, "push.id")) { - CHECK_LAST_FRAME(PUSH_PROMISE); - RETURN_BUFFED(f->md.promised); - } - /* SECTION: stream.spec.zexpect.goaway GOAWAY specific - * - * goaway.err - * The error code (as integer) of the GOAWAY frame. - * - * goaway.laststream - * Last-Stream-ID - * - * goaway.debug - * Debug data, if any. - */ - if (!strncmp(spec, "goaway.", 7)) { - spec += 7; - CHECK_LAST_FRAME(GOAWAY); - - if (!strcmp(spec, "err")) - RETURN_BUFFED(f->md.goaway.err); - else if (!strcmp(spec, "laststream")) - RETURN_BUFFED(f->md.goaway.stream); - else if (!strcmp(spec, "debug")) - return (f->md.goaway.debug); - } - /* SECTION: stream.spec.zexpect.zframe Generic frame - * - * frame.data - * Payload of the last frame - * - * frame.type - * Type of the frame, as integer. - * - * frame.size - * Size of the frame. - * - * frame.stream - * Stream of the frame (correspond to the one you are executing - * this from, obviously). - * - * frame.padding (for DATA, HEADERS, PUSH_PROMISE frames) - * Number of padded bytes. - */ - if (!strncmp(spec, "frame.", 6)) { - spec += 6; - if (!f) - vtc_fatal(s->vl, "No frame received yet."); - if (!strcmp(spec, "data")) { return (f->data); } - else if (!strcmp(spec, "type")) { RETURN_BUFFED(f->type); } - else if (!strcmp(spec, "size")) { RETURN_BUFFED(f->size); } - else if (!strcmp(spec, "stream")) { RETURN_BUFFED(f->stid); } - else if (!strcmp(spec, "padding")) { - if (f->type != TYPE_DATA && - f->type != TYPE_HEADERS && - f->type != TYPE_PUSH_PROMISE) - vtc_fatal(s->vl, - "Last frame was not of type " - "DATA, HEADERS or PUSH"); - RETURN_BUFFED(f->md.padded); - } - } - /* SECTION: stream.spec.zexpect.zstream Stream - * - * stream.window - * The current local window size of the stream, or, if on stream 0, - * of the connection. - * - * stream.peer_window - * The current peer window size of the stream, or, if on stream 0, - * of the connection. - * - * stream.weight - * Weight of the stream - * - * stream.dependency - * Id of the stream this one depends on. - */ - if (!strcmp(spec, "stream.window")) { - snprintf(buf, 20, "%jd", - (intmax_t)(s->id ? s->win_self : s->hp->h2_win_self->size)); - return (buf); - } - if (!strcmp(spec, "stream.peer_window")) { - snprintf(buf, 20, "%jd", - (intmax_t)(s->id ? s->win_peer : s->hp->h2_win_peer->size)); - return (buf); - } - if (!strcmp(spec, "stream.weight")) { - if (s->id) { - snprintf(buf, 20, "%d", s->weight); - return (buf); - } else - return (NULL); - } - if (!strcmp(spec, "stream.dependency")) { - if (s->id) { - snprintf(buf, 20, "%d", s->dependency); - return (buf); - } else - return (NULL); - } - /* SECTION: stream.spec.zexpect.ztable Index tables - * - * tbl.dec.size / tbl.enc.size - * Size (bytes) of the decoding/encoding table. - * - * tbl.dec.size / tbl.enc.maxsize - * Maximum size (bytes) of the decoding/encoding table. - * - * tbl.dec.length / tbl.enc.length - * Number of headers in decoding/encoding table. - * - * tbl.dec[INT].key / tbl.enc[INT].key - * Name of the header at index INT of the decoding/encoding - * table. - * - * tbl.dec[INT].value / tbl.enc[INT].value - * Value of the header at index INT of the decoding/encoding - * table. - */ - if (!strncmp(spec, "tbl.dec", 7) || !strncmp(spec, "tbl.enc", 7)) { - if (spec[4] == 'd') - ctx = s->hp->decctx; - else - ctx = s->hp->encctx; - spec += 7; - - if (1 == sscanf(spec, "[%u].key%n", &idx, &n) && - spec[n] == '\0') { - h = HPK_GetHdr(ctx, idx + 61); - return (h ? h->key.ptr : NULL); - } - else if (1 == sscanf(spec, "[%u].value%n", &idx, &n) && - spec[n] == '\0') { - h = HPK_GetHdr(ctx, idx + 61); - return (h ? h->value.ptr : NULL); - } - else if (!strcmp(spec, ".size")) - RETURN_BUFFED(HPK_GetTblSize(ctx)); - else if (!strcmp(spec, ".maxsize")) - RETURN_BUFFED(HPK_GetTblMaxSize(ctx)); - else if (!strcmp(spec, ".length")) - RETURN_BUFFED(HPK_GetTblLength(ctx)); - } - /* SECTION: stream.spec.zexpect.zre Request and response - * - * Note: it's possible to inspect a request or response while it is - * still being construct (in-between two frames for example). - * - * req.bodylen / resp.bodylen - * Length in bytes of the request/response so far. - * - * req.body / resp.body - * Body of the request/response so far. - * - * req.http.STRING / resp.http.STRING - * Value of the header STRING in the request/response. - * - * req.status / resp.status - * :status pseudo-header's value. - * - * req.url / resp.url - * :path pseudo-header's value. - * - * req.method / resp.method - * :method pseudo-header's value. - * - * req.authority / resp.authority - * :method pseudo-header's value. - * - * req.scheme / resp.scheme - * :method pseudo-header's value. - */ - if (!strncmp(spec, "req.", 4) || !strncmp(spec, "resp.", 5)) { - if (spec[2] == 'q') { - h = s->req; - spec += 4; - } else { - h = s->resp; - spec += 5; - } - if (!strcmp(spec, "body")) - return (s->body); - else if (!strcmp(spec, "bodylen")) - RETURN_BUFFED(s->bodylen); - else if (!strcmp(spec, "status")) - return (find_header(h, ":status")); - else if (!strcmp(spec, "url")) - return (find_header(h, ":path")); - else if (!strcmp(spec, "method")) - return (find_header(h, ":method")); - else if (!strcmp(spec, "authority")) - return (find_header(h, ":authority")); - else if (!strcmp(spec, "scheme")) - return (find_header(h, ":scheme")); - else if (!strncmp(spec, "http.", 5)) - return (find_header(h, spec + 5)); - else - return (NULL); - } -#define H2_ERROR(U,v,sc,g,r,t) \ - if (!strcmp(spec, #U)) { return (#v); } -#include "tbl/h2_error.h" - return (spec); -} - -/* SECTION: stream.spec.frame_sendhex sendhex - * - * Push bytes directly on the wire. sendhex takes exactly one argument: a string - * describing the bytes, in hex notation, with possible whitespaces between - * them. Here's an example:: - * - * sendhex "00 00 08 00 0900 8d" - */ -static void -cmd_sendhex(CMD_ARGS) -{ - struct http *hp; - struct stream *s; - struct vsb *vsb; - - (void)vl; - CAST_OBJ_NOTNULL(s, priv, STREAM_MAGIC); - CAST_OBJ_NOTNULL(hp, s->hp, HTTP_MAGIC); - AN(av[1]); - AZ(av[2]); - vsb = vtc_hex_to_bin(hp->vl, av[1]); - assert(VSB_len(vsb) >= 0); - vtc_hexdump(hp->vl, 4, "sendhex", VSB_data(vsb), VSB_len(vsb)); - PTOK(pthread_mutex_lock(&hp->mtx)); - http_write(hp, 4, VSB_data(vsb), VSB_len(vsb), "sendhex"); - PTOK(pthread_mutex_unlock(&hp->mtx)); - VSB_destroy(&vsb); -} - -#define ENC(hdr, k, v) \ -{ \ - AN(k); \ - hdr.key.ptr = TRUST_ME(k); \ - hdr.key.len = strlen(k); \ - AN(v); \ - hdr.value.ptr = TRUST_ME(v); \ - hdr.value.len = strlen(v); \ - assert(HPK_EncHdr(iter, &hdr) != hpk_err); \ -} - -#define STR_ENC(av, field, str) \ -{ \ - av++; \ - if (AV_IS("plain")) { hdr.field.huff = 0; } \ - else if (AV_IS("huf")) { hdr.field.huff = 1; } \ - else \ - vtc_fatal(vl, str " arg can be huf or plain (got: %s)", *av); \ - av++; \ - AN(*av); \ - hdr.field.ptr = *av; \ - hdr.field.len = strlen(*av); \ -} - - -/* SECTION: stream.spec.data_0 txreq, txresp, txcont, txpush - * - * These four commands are about sending headers. txreq and txresp - * will send HEADER frames; txcont will send CONTINUATION frames; txpush - * PUSH frames. - * The only difference between txreq and txresp are the default headers - * set by each of them. - * - * \-noadd - * Do not add default headers. Useful to avoid duplicates when sending - * default headers using ``-hdr``, ``-idxHdr`` and ``-litIdxHdr``. - * - * \-status INT (txresp) - * Set the :status pseudo-header. - * - * \-url STRING (txreq, txpush) - * Set the :path pseudo-header. - * - * \-method STRING (txreq, txpush) - * Set the :method pseudo-header. - * - * \-req STRING (txreq, txpush) - * Alias for -method. - * - * \-scheme STRING (txreq, txpush) - * Set the :scheme pseudo-header. - * - * \-hdr STRING1 STRING2 - * Insert a header, STRING1 being the name, and STRING2 the value. - * - * \-idxHdr INT - * Insert an indexed header, using INT as index. - * - * \-litIdxHdr inc|not|never INT huf|plain STRING - * Insert an literal, indexed header. The first argument specify if the - * header should be added to the table, shouldn't, or mustn't be - * compressed if/when retransmitted. - * - * INT is the index of the header name to use. - * - * The third argument informs about the Huffman encoding: yes (huf) or - * no (plain). - * - * The last term is the literal value of the header. - * - * \-litHdr inc|not|never huf|plain STRING1 huf|plain STRING2 - * Insert a literal header, with the same first argument as - * ``-litIdxHdr``. - * - * The second and third terms tell what the name of the header is and if - * it should be Huffman-encoded, while the last two do the same - * regarding the value. - * - * \-body STRING (txreq, txresp) - * Specify a body, effectively putting STRING into a DATA frame after - * the HEADER frame is sent. - * - * \-bodyfrom FILE (txreq, txresp) - * Same as ``-body`` but content is read from FILE. - * - * \-bodylen INT (txreq, txresp) - * Do the same thing as ``-body`` but generate a string of INT length - * for you. - * - * \-gzipbody STRING (txreq, txresp) - * Gzip STRING and send it as body. - * - * \-gziplen NUMBER (txreq, txresp) - * Combine -bodylen and -gzipbody: generate a string of length NUMBER, - * gzip it and send as body. - * - * \-nostrend (txreq, txresp) - * Don't set the END_STREAM flag automatically, making the peer expect - * a body after the headers. - * - * \-nohdrend - * Don't set the END_HEADERS flag automatically, making the peer expect - * more HEADER frames. - * - * \-dep INT (txreq, txresp) - * Tell the peer that this content depends on the stream with the INT - * id. - * - * \-ex (txreq, txresp) - * Make the dependency exclusive (``-dep`` is still needed). - * - * \-weight (txreq, txresp) - * Set the weight for the dependency. - * - * \-promised INT (txpush) - * The id of the promised stream. - * - * \-pad STRING / -padlen INT (txreq, txresp, txpush) - * Add string as padding to the frame, either the one you provided with - * \-pad, or one that is generated for you, of length INT is -padlen - * case. - */ - -#define cmd_txreq cmd_tx11obj -#define cmd_txresp cmd_tx11obj -#define cmd_txpush cmd_tx11obj -#define cmd_txcont cmd_tx11obj - -static void -cmd_tx11obj(CMD_ARGS) -{ - struct stream *s; - int i; - int status_done = 1; - int method_done = 1; - int path_done = 1; - int scheme_done = 1; - long bodylen = 0; - ssize_t len; - uint32_t stid = 0, pstid; - uint32_t weight = 16; - uint32_t exclusive = 0; - char *buf; - struct hpk_iter *iter; - struct frame f; - char *body = NULL, *pad = NULL; - /*XXX: do we need a better api? yes we do */ - struct hpk_hdr hdr; - char *cmd_str = *av; - char *p; - - CAST_OBJ_NOTNULL(s, priv, STREAM_MAGIC); - INIT_FRAME(f, CONTINUATION, 0, s->id, END_HEADERS); - buf = malloc(BUF_SIZE); - AN(buf); - - if (!strcmp(cmd_str, "txreq")) { - ONLY_H2_CLIENT(s->hp, av); - f.type = TYPE_HEADERS; - f.flags |= END_STREAM; - method_done = 0; - path_done = 0; - scheme_done = 0; - } else if (!strcmp(cmd_str, "txresp")) { - ONLY_H2_SERVER(s->hp, av); - f.type = TYPE_HEADERS; - f.flags |= END_STREAM; - status_done = 0; - } else if (!strcmp(cmd_str, "txpush")) { - ONLY_H2_SERVER(s->hp, av); - f.type = TYPE_PUSH_PROMISE; - method_done = 0; - path_done = 0; - scheme_done = 0; - } - - if (f.type == TYPE_PUSH_PROMISE) { - *buf = 0; - iter = HPK_NewIter(s->hp->encctx, buf + 4, BUF_SIZE - 4); - } else - iter = HPK_NewIter(s->hp->encctx, buf, BUF_SIZE); - -#define AV_IS(str) !strcmp(*av, str) -#define CMD_IS(str) !strcmp(cmd_str, str) - while (*++av) { - memset(&hdr, 0, sizeof(hdr)); - hdr.t = hpk_not; - if (AV_IS("-noadd")) { - path_done = 1; - status_done = 1; - method_done = 1; - scheme_done = 1; - } - else if (AV_IS("-status") && CMD_IS("txresp")) { - ENC(hdr, ":status", av[1]); - av++; - status_done = 1; - } - else if (AV_IS("-url") && - (CMD_IS("txreq") || CMD_IS("txpush"))) { - ENC(hdr, ":path", av[1]); - av++; - path_done = 1; - } - else if ((AV_IS("-method") || AV_IS("-req")) && - (CMD_IS("txreq") || CMD_IS("txpush"))) { - ENC(hdr, ":method", av[1]); - av++; - method_done = 1; - } - else if (AV_IS("-scheme") && - (CMD_IS("txreq") || CMD_IS("txpush"))) { - ENC(hdr, ":scheme", av[1]); - av++; - scheme_done = 1; - } - else if (AV_IS("-hdr")) { - if (av[2] == NULL) - vtc_fatal(vl, "-hdr takes two arguments in http2"); - ENC(hdr, av[1], av[2]); - av += 2; - } - else if (AV_IS("-idxHdr")) { - hdr.t = hpk_idx; - STRTOU32_CHECK(hdr.i, av, p, vl, "-idxHdr", 0); - assert(HPK_EncHdr(iter, &hdr) != hpk_err); - } - else if (AV_IS("-litIdxHdr")) { - av++; - if (AV_IS("inc")) { hdr.t = hpk_inc; } - else if (AV_IS("not")) { hdr.t = hpk_not; } - else if (AV_IS("never")) { hdr.t = hpk_never; } - else - vtc_fatal(vl, "first -litidxHdr arg can be " - "inc, not, never (got: %s)", *av); - - STRTOU32_CHECK(hdr.i, av, p, vl, - "second -litidxHdr arg", 0); - - hdr.key.ptr = NULL; - hdr.key.len = 0; - STR_ENC(av, value, "third -litHdr"); - assert(HPK_EncHdr(iter, &hdr) != hpk_err); - } - else if (AV_IS("-litHdr")) { - av++; - if (AV_IS("inc")) { hdr.t = hpk_inc; } - else if (AV_IS("not")) { hdr.t = hpk_not; } - else if (AV_IS("never")) { hdr.t = hpk_never; } - else - vtc_fatal(vl, "first -litHdr arg can be inc, " - "not, never (got: %s)", *av); - - STR_ENC(av, key, "second -litHdr"); - STR_ENC(av, value, "fourth -litHdr"); - assert(HPK_EncHdr(iter, &hdr) != hpk_err); - } - else if (AV_IS("-nostrend")) { - f.flags &= ~END_STREAM; - } - else if (AV_IS("-nohdrend")) { - f.flags &= ~END_HEADERS; - } - else if (AV_IS("-promised") && CMD_IS("txpush")) { - STRTOU32_CHECK(pstid, av, p, vl, "-promised", 31); - vbe32enc(buf, pstid); - } - else if (AV_IS("-pad") && !CMD_IS("txcont")) { - AZ(pad); - av++; - AN(*av); - pad = strdup(*av); - } - else if (AV_IS("-padlen") && !CMD_IS("txcont")) { - AZ(pad); - av++; - pad = synth_body(*av, 0); - } - else if (CMD_IS("txreq") || CMD_IS("txresp")) { - if (AV_IS("-body")) { - AZ(body); - REPLACE(body, av[1]); - AN(body); - bodylen = strlen(body); - f.flags &= ~END_STREAM; - av++; - } - else if (AV_IS("-bodyfrom")) { - AZ(body); - body = VFIL_readfile(NULL, av[1], &len); - AN(body); - assert(len < INT_MAX); - bodylen = len; - f.flags &= ~END_STREAM; - av++; - } - else if (AV_IS("-bodylen")) { - AZ(body); - body = synth_body(av[1], 0); - bodylen = strlen(body); - f.flags &= ~END_STREAM; - av++; - } - else if (!strncmp(*av, "-gzip", 5)) { - i = vtc_gzip_cmd(s->hp, av, &body, &bodylen); - if (i == 0) - break; - av++; - if (i > 1) { - ENC(hdr, ":content-encoding", "gzip"); - f.flags &= ~END_STREAM; - } - } - else if (AV_IS("-dep")) { - STRTOU32_CHECK(stid, av, p, vl, "-dep", 0); - f.flags |= PRIORITY; - } - else if (AV_IS("-ex")) { - exclusive = 1U << 31; - f.flags |= PRIORITY; - } - else if (AV_IS("-weight")) { - STRTOU32_CHECK(weight, av, p, vl, "-weight", 8); - f.flags |= PRIORITY; - } else - break; - } else - break; - } -#undef CMD_IS -#undef AV_IS - if (*av != NULL) - vtc_fatal(vl, "Unknown %s spec: %s\n", cmd_str, *av); - - memset(&hdr, 0, sizeof(hdr)); - hdr.t = hpk_not; - - if (!status_done) { ENC(hdr, ":status", "200"); } - if (!path_done) { ENC(hdr, ":path", "/"); } - if (!method_done) { ENC(hdr, ":method", "GET"); } - if (!scheme_done) { ENC(hdr, ":scheme", "http"); } - - f.size = gethpk_iterLen(iter); - if (f.flags & PRIORITY) { - s->weight = weight & 0xff; - s->dependency = stid; - - assert(f.size + 5 < BUF_SIZE); - memmove(buf + 5, buf, f.size); - vbe32enc(buf, (stid | exclusive)); - buf[4] = s->weight; - f.size += 5; - - vtc_log(vl, 4, "stream->dependency: %u", s->dependency); - vtc_log(vl, 4, "stream->weight: %u", s->weight); - if (exclusive) - exclusive_stream_dependency(s); - } - if (pad) { - if (strlen(pad) > 255) - vtc_fatal(vl, "Padding is limited to 255 bytes"); - f.flags |= PADDED; - assert(f.size + strlen(pad) < BUF_SIZE); - memmove(buf + 1, buf, f.size); - buf[0] = strlen(pad); - f.size += 1; - memcpy(buf + f.size, pad, strlen(pad)); - f.size += strlen(pad); - free(pad); - } - if (f.type == TYPE_PUSH_PROMISE) - f.size += 4; - f.data = buf; - HPK_FreeIter(iter); - write_frame(s, &f, 1); - free(buf); - - if (!body) - return; - - INIT_FRAME(f, DATA, bodylen, s->id, END_STREAM); - f.data = body; - - write_frame(s, &f, 1); - free(body); -} - -/* SECTION: stream.spec.data_1 txdata - * - * By default, data frames are empty. The receiving end will know the whole body - * has been delivered thanks to the END_STREAM flag set in the last DATA frame, - * and txdata automatically set it. - * - * \-data STRING - * Data to be embedded into the frame. - * - * \-datalen INT - * Generate and INT-bytes long string to be sent in the frame. - * - * \-pad STRING / -padlen INT - * Add string as padding to the frame, either the one you provided with - * \-pad, or one that is generated for you, of length INT is -padlen - * case. - * - * \-nostrend - * Don't set the END_STREAM flag, allowing to send more data on this - * stream. - */ -static void -cmd_txdata(CMD_ARGS) -{ - struct stream *s; - char *pad = NULL; - struct frame f; - char *body = NULL; - char *data = NULL; - - CAST_OBJ_NOTNULL(s, priv, STREAM_MAGIC); - - INIT_FRAME(f, DATA, 0, s->id, END_STREAM); - - while (*++av) { - if (!strcmp(*av, "-data")) { - AZ(body); - av++; - body = strdup(*av); - } else if (!strcmp(*av, "-datalen")) { - AZ(body); - av++; - body = synth_body(*av, 0); - } else if (!strcmp(*av, "-pad")) { - AZ(pad); - av++; - AN(*av); - pad = strdup(*av); - } else if (!strcmp(*av, "-padlen")) { - AZ(pad); - av++; - pad = synth_body(*av, 0); - } else if (!strcmp(*av, "-nostrend")) - f.flags &= ~END_STREAM; - else - break; - } - if (*av != NULL) - vtc_fatal(vl, "Unknown txdata spec: %s\n", *av); - - if (!body) - body = strdup(""); - - if (pad) { - f.flags |= PADDED; - if (strlen(pad) > 255) - vtc_fatal(vl, "Padding is limited to 255 bytes"); - data = malloc( 1 + strlen(body) + strlen(pad)); - AN(data); - *((uint8_t *)data) = strlen(pad); - f.size = 1; - memcpy(data + f.size, body, strlen(body)); - f.size += strlen(body); - memcpy(data + f.size, pad, strlen(pad)); - f.size += strlen(pad); - f.data = data; - } else { - f.size = strlen(body); - f.data = body; - } - write_frame(s, &f, 1); - free(body); - free(pad); - free(data); -} - -/* SECTION: stream.spec.reset_txrst txrst - * - * Send a RST_STREAM frame. By default, txrst will send a 0 error code - * (NO_ERROR). - * - * \-err STRING|INT - * Sets the error code to be sent. The argument can be an integer or a - * string describing the error, such as NO_ERROR, or CANCEL (see - * rfc7540#11.4 for more strings). - */ -static void -cmd_txrst(CMD_ARGS) -{ - struct stream *s; - char *p; - uint32_t err = 0; - struct frame f; - - CAST_OBJ_NOTNULL(s, priv, STREAM_MAGIC); - - INIT_FRAME(f, RST_STREAM, 4, s->id, 0); - - while (*++av) { - if (!strcmp(*av, "-err")) { - ++av; - for (err = 0; h2_errs[err]; err++) { - if (!strcmp(h2_errs[err], *av)) - break; - } - - if (h2_errs[err]) - continue; - - STRTOU32(err, *av, p, vl, "-err"); - } else - break; - } - if (*av != NULL) - vtc_fatal(vl, "Unknown txrst spec: %s\n", *av); - - err = htonl(err); - f.data = (void *)&err; - write_frame(s, &f, 1); -} - -/* SECTION: stream.spec.prio_txprio txprio - * - * Send a PRIORITY frame - * - * \-stream INT - * indicate the id of the stream the sender stream depends on. - * - * \-ex - * the dependency should be made exclusive (only this streams depends on - * the parent stream). - * - * \-weight INT - * an 8-bits integer is used to balance priority between streams - * depending on the same streams. - */ -static void -cmd_txprio(CMD_ARGS) -{ - struct stream *s; - char *p; - uint32_t stid = 0; - struct frame f; - uint32_t weight = 0; - uint32_t exclusive = 0; - uint8_t buf[5]; - - CAST_OBJ_NOTNULL(s, priv, STREAM_MAGIC); - - INIT_FRAME(f, PRIORITY, 5, s->id, 0); - f.data = (void *)buf; - - while (*++av) { - if (!strcmp(*av, "-stream")) { - STRTOU32_CHECK(stid, av, p, vl, "-stream", 0); - } else if (!strcmp(*av, "-ex")) { - exclusive = 1U << 31; - } else if (!strcmp(*av, "-weight")) { - STRTOU32_CHECK(weight, av, p, vl, "-weight", 8); - } else - break; - } - if (*av != NULL) - vtc_fatal(vl, "Unknown txprio spec: %s\n", *av); - s->weight = weight & 0xff; - s->dependency = stid; - - if (exclusive) - exclusive_stream_dependency(s); - - vbe32enc(buf, (stid | exclusive)); - buf[4] = s->weight; - write_frame(s, &f, 1); -} - -#define PUT_KV(av, vl, name, val, code) \ - do {\ - STRTOU32_CHECK(val, av, p, vl, #name, 0); \ - vbe16enc(cursor, code); \ - cursor += sizeof(uint16_t); \ - vbe32enc(cursor, val); \ - cursor += sizeof(uint32_t); \ - f.size += 6; \ - } while(0) - -/* SECTION: stream.spec.settings_txsettings txsettings - * - * SETTINGS frames must be acknowledge, arguments are as follow (most of them - * are from rfc7540#6.5.2): - * - * \-hdrtbl INT - * headers table size - * - * \-push BOOL - * whether push frames are accepted or not - * - * \-maxstreams INT - * maximum concurrent streams allowed - * - * \-winsize INT - * sender's initial window size - * - * \-framesize INT - * largest frame size authorized - * - * \-hdrsize INT - * maximum size of the header list authorized - * - * \-0xHH[HH] INT - * tx arbitraty settings with tag xx - * - * \-ack - * set the ack bit - */ -static void -cmd_txsettings(CMD_ARGS) -{ - struct stream *s, *s2; - struct http *hp; - char *p, *e; - unsigned long u; - uint32_t val = 0; - struct frame f; - //TODO dynamic alloc - char buf[512]; - char *cursor = buf; - - CAST_OBJ_NOTNULL(s, priv, STREAM_MAGIC); - CAST_OBJ_NOTNULL(hp, s->hp, HTTP_MAGIC); - - memset(buf, 0, 512); - INIT_FRAME(f, SETTINGS, 0, s->id, 0); - f.data = buf; - - PTOK(pthread_mutex_lock(&hp->mtx)); - while (*++av) { - if (!strcmp(*av, "-push")) { - ++av; - vbe16enc(cursor, 0x2); - cursor += sizeof(uint16_t); - if (!strcmp(*av, "false")) - vbe32enc(cursor, 0); - else if (!strcmp(*av, "true")) - vbe32enc(cursor, 1); - else - vtc_fatal(vl, "Push parameter is either " - "\"true\" or \"false\", not %s", *av); - cursor += sizeof(uint32_t); - f.size += 6; - } - else if (!strcmp(*av, "-hdrtbl")) { - PUT_KV(av, vl, hdrtbl, val, 0x1); - assert(HPK_ResizeTbl(s->hp->decctx, val) != hpk_err); - } - else if (!strcmp(*av, "-maxstreams")) - PUT_KV(av, vl, maxstreams, val, 0x3); - else if (!strcmp(*av, "-winsize")) { - PUT_KV(av, vl, winsize, val, 0x4); - VTAILQ_FOREACH(s2, &hp->streams, list) - s2->win_self += (val - hp->h2_win_self->init); - hp->h2_win_self->init = val; - } - else if (!strcmp(*av, "-framesize")) - PUT_KV(av, vl, framesize, val, 0x5); - else if (!strcmp(*av, "-hdrsize")) - PUT_KV(av, vl, hdrsize, val, 0x6); - else if (!strncmp(*av, "-0x", 3)) { - p = *av + 3; - errno = 0; - u = strtoul(p, &e, 16); - if (*p == '\0' || *e != '\0' || u > 0xffff || errno != 0) - vtc_fatal(vl, "Invalid settings tag %s", p); - assert(u <= 0xffff); - PUT_KV(av, vl, hdrtbl, val, (uint16_t)u); - } - else if (!strcmp(*av, "-ack")) - f.flags |= 1; - else - break; - } - if (*av != NULL) - vtc_fatal(vl, "Unknown txsettings spec: %s\n", *av); - - AN(s->hp); - write_frame(s, &f, 0); - PTOK(pthread_mutex_unlock(&hp->mtx)); -} - -/* SECTION: stream.spec.ping_txping txping - * - * Send PING frame. - * - * \-data STRING - * specify the payload of the frame, with STRING being an 8-char string. - * - * \-ack - * set the ACK flag. - */ -static void -cmd_txping(CMD_ARGS) -{ - struct stream *s; - struct frame f; - char buf[8]; - - memset(buf, 0, 8); - CAST_OBJ_NOTNULL(s, priv, STREAM_MAGIC); - INIT_FRAME(f, PING, 8, s->id, 0); - - while (*++av) { - if (!strcmp(*av, "-data")) { - av++; - if (f.data) - vtc_fatal(vl, "this frame already has data"); - if (strlen(*av) != 8) - vtc_fatal(vl, "data must be a 8-char string, found (%s)", *av); - f.data = *av; - } else if (!strcmp(*av, "-ack")) - f.flags |= 1; - else - break; - } - if (*av != NULL) - vtc_fatal(vl, "Unknown txping spec: %s\n", *av); - if (!f.data) - f.data = buf; - write_frame(s, &f, 1); -} - -/* - * SECTION: stream.spec.goaway_txgoaway txgoaway - * - * Possible options include: - * - * \-err STRING|INT - * set the error code to explain the termination. The second argument - * can be a integer or the string version of the error code as found - * in rfc7540#7. - * - * \-laststream INT - * the id of the "highest-numbered stream identifier for which the - * sender of the GOAWAY frame might have taken some action on or might - * yet take action on". - * - * \-debug - * specify the debug data, if any to append to the frame. - */ -static void -cmd_txgoaway(CMD_ARGS) -{ - struct stream *s; - char *p; - uint32_t err = 0; - uint32_t ls = 0; - struct frame f; - - CAST_OBJ_NOTNULL(s, priv, STREAM_MAGIC); - - INIT_FRAME(f, GOAWAY, 8, s->id, 0); - - while (*++av) { - if (!strcmp(*av, "-err")) { - ++av; - for (err = 0; h2_errs[err]; err++) - if (!strcmp(h2_errs[err], *av)) - break; - - if (h2_errs[err]) - continue; - - STRTOU32(err, *av, p, vl, "-err"); - } else if (!strcmp(*av, "-laststream")) { - STRTOU32_CHECK(ls, av, p, vl, "-laststream", 31); - } else if (!strcmp(*av, "-debug")) { - ++av; - if (f.data) - vtc_fatal(vl, "this frame already has debug data"); - f.size = 8 + strlen(*av); - f.data = malloc(f.size); - AN(f.data); - memcpy(f.data + 8, *av, f.size - 8); - } else - break; - } - if (*av != NULL) - vtc_fatal(vl, "Unknown txgoaway spec: %s\n", *av); - - if (!f.data) { - f.data = malloc(8); - AN(f.data); - } - vbe32enc(f.data, ls); - vbe32enc(f.data + 4, err); - write_frame(s, &f, 1); - free(f.data); -} - -/* SECTION: stream.spec.winup_txwinup txwinup - * - * Transmit a WINDOW_UPDATE frame, increasing the amount of credit of the - * connection (from stream 0) or of the stream (any other stream). - * - * \-size INT - * give INT credits to the peer. - */ -static void -cmd_txwinup(CMD_ARGS) -{ - struct http *hp; - struct stream *s; - char *p; - struct frame f; - char buf[8]; - uint32_t size = 0; - - CAST_OBJ_NOTNULL(s, priv, STREAM_MAGIC); - CAST_OBJ_NOTNULL(hp, s->hp, HTTP_MAGIC); - memset(buf, 0, 8); - - AN(av[1]); - AN(av[2]); - - INIT_FRAME(f, WINDOW_UPDATE, 4, s->id, 0); - f.data = buf; - - while (*++av) - if (!strcmp(*av, "-size")) { - STRTOU32_CHECK(size, av, p, vl, "-size", 0); - } else - break; - if (*av != NULL) - vtc_fatal(vl, "Unknown txwinup spec: %s\n", *av); - - PTOK(pthread_mutex_lock(&hp->mtx)); - if (s->id == 0) - hp->h2_win_self->size += size; - s->win_self += size; - PTOK(pthread_mutex_unlock(&hp->mtx)); - - size = htonl(size); - f.data = (void *)&size; - write_frame(s, &f, 1); -} - -static struct frame * -rxstuff(struct stream *s) -{ - struct frame *f; - - CHECK_OBJ_NOTNULL(s, STREAM_MAGIC); - - PTOK(pthread_mutex_lock(&s->hp->mtx)); - if (VTAILQ_EMPTY(&s->fq)) { - assert(s->hp->wf >= 0); - s->hp->wf++; - s->wf = 1; - PTOK(pthread_cond_signal(&s->hp->cond)); - PTOK(pthread_cond_wait(&s->cond, &s->hp->mtx)); - } - if (VTAILQ_EMPTY(&s->fq)) { - PTOK(pthread_mutex_unlock(&s->hp->mtx)); - return (NULL); - } - clean_frame(&s->frame); - f = VTAILQ_LAST(&s->fq, fq_head); - CHECK_OBJ_NOTNULL(f, FRAME_MAGIC); - VTAILQ_REMOVE(&s->fq, f, list); - PTOK(pthread_mutex_unlock(&s->hp->mtx)); - return (f); -} - -#define CHKFRAME(rt, wt, rcv, func) \ - do { \ - if (rt != wt) \ - vtc_fatal(vl, "Frame #%d for %s was of type %s (%d) " \ - "instead of %s (%d)", \ - rcv, func, \ - rt < TYPE_MAX ? h2_types[rt] : "?", rt, \ - wt < TYPE_MAX ? h2_types[wt] : "?", wt); \ - } while (0); - -/* SECTION: stream.spec.data_11 rxhdrs - * - * ``rxhdrs`` will expect one HEADER frame, then, depending on the arguments, - * zero or more CONTINUATION frame. - * - * \-all - * Keep waiting for CONTINUATION frames until END_HEADERS flag is seen. - * - * \-some INT - * Retrieve INT - 1 CONTINUATION frames after the HEADER frame. - * - */ -static void -cmd_rxhdrs(CMD_ARGS) -{ - struct stream *s; - struct frame *f = NULL; - char *p; - int loop = 0; - unsigned long int times = 1; - unsigned rcv = 0; - enum h2_type_e expect = TYPE_HEADERS; - - CAST_OBJ_NOTNULL(s, priv, STREAM_MAGIC); - - while (*++av) { - if (!strcmp(*av, "-some")) { - STRTOU32_CHECK(times, av, p, vl, "-some", 0); - if (!times) - vtc_fatal(vl, "-some argument must be more" - "than 0 (found \"%s\")\n", *av); - } else if (!strcmp(*av, "-all")) - loop = 1; - else - break; - } - if (*av != NULL) - vtc_fatal(vl, "Unknown rxhdrs spec: %s\n", *av); - - do { - replace_frame(&f, rxstuff(s)); - if (f == NULL) - break; - rcv++; - CHKFRAME(f->type, expect, rcv, "rxhdrs"); - expect = TYPE_CONTINUATION; - } while (rcv < times || (loop && !(f->flags & END_HEADERS))); - replace_frame(&s->frame, f); -} - -static void -cmd_rxcont(CMD_ARGS) -{ - struct stream *s; - struct frame *f = NULL; - char *p; - int loop = 0; - unsigned long int times = 1; - unsigned rcv = 0; - - (void)av; - CAST_OBJ_NOTNULL(s, priv, STREAM_MAGIC); - - while (*++av) - if (!strcmp(*av, "-some")) { - STRTOU32(times, *av, p, vl, "-some"); - if (!times) - vtc_fatal(vl, "-some argument must be more" - "than 0 (found \"%s\")\n", *av); - } else if (!strcmp(*av, "-all")) - loop = 1; - else - break; - if (*av != NULL) - vtc_fatal(vl, "Unknown rxcont spec: %s\n", *av); - - do { - replace_frame(&f, rxstuff(s)); - if (f == NULL) - break; - rcv++; - CHKFRAME(f->type, TYPE_CONTINUATION, rcv, "rxcont"); - } while (rcv < times || (loop && !(f->flags & END_HEADERS))); - replace_frame(&s->frame, f); -} - - -/* SECTION: stream.spec.data_13 rxdata - * - * Receiving data is done using the ``rxdata`` keywords and will retrieve one - * DATA frame, if you wish to receive more, you can use these two convenience - * arguments: - * - * \-all - * keep waiting for DATA frame until one sets the END_STREAM flag - * - * \-some INT - * retrieve INT DATA frames. - * - */ -static void -cmd_rxdata(CMD_ARGS) -{ - struct stream *s; - struct frame *f = NULL; - char *p; - int loop = 0; - unsigned long int times = 1; - unsigned rcv = 0; - - (void)av; - CAST_OBJ_NOTNULL(s, priv, STREAM_MAGIC); - - while (*++av) - if (!strcmp(*av, "-some")) { - av++; - STRTOU32(times, *av, p, vl, "-some"); - if (!times) - vtc_fatal(vl, "-some argument must be more" - "than 0 (found \"%s\")\n", *av); - } else if (!strcmp(*av, "-all")) - loop = 1; - else - break; - if (*av != NULL) - vtc_fatal(vl, "Unknown rxdata spec: %s\n", *av); - - do { - replace_frame(&f, rxstuff(s)); - if (f == NULL) - break; - rcv++; - CHKFRAME(f->type, TYPE_DATA, rcv, "rxhdata"); - } while (rcv < times || (loop && !(f->flags & END_STREAM))); - replace_frame(&s->frame, f); -} - -/* SECTION: stream.spec.data_10 rxreq, rxresp - * - * These are two convenience functions to receive headers and body of an - * incoming request or response. The only difference is that rxreq can only be - * by a server, and rxresp by a client. - * - */ - -#define cmd_rxreq cmd_rxmsg -#define cmd_rxresp cmd_rxmsg - -static void -cmd_rxmsg(CMD_ARGS) -{ - struct stream *s; - struct frame *f = NULL; - int end_stream; - int rcv = 0; - - CAST_OBJ_NOTNULL(s, priv, STREAM_MAGIC); - - if (!strcmp(av[0], "rxreq")) - ONLY_H2_SERVER(s->hp, av); - else - ONLY_H2_CLIENT(s->hp, av); - - do { - replace_frame(&f, rxstuff(s)); - CHECK_OBJ_ORNULL(f, FRAME_MAGIC); - if (f == NULL) - return; - } while (f->type == TYPE_WINDOW_UPDATE); - - rcv++; - CHKFRAME(f->type, TYPE_HEADERS, rcv, *av); - - end_stream = f->flags & END_STREAM; - - while (!(f->flags & END_HEADERS)) { - replace_frame(&f, rxstuff(s)); - CHECK_OBJ_ORNULL(f, FRAME_MAGIC); - if (f == NULL) - return; - rcv++; - CHKFRAME(f->type, TYPE_CONTINUATION, rcv, *av); - } - - while (!end_stream) { - replace_frame(&f, rxstuff(s)); - CHECK_OBJ_ORNULL(f, FRAME_MAGIC); - if (f == NULL) - break; - rcv++; - CHKFRAME(f->type, TYPE_DATA, rcv, *av); - end_stream = f->flags & END_STREAM; - } - replace_frame(&s->frame, f); -} - -/* SECTION: stream.spec.data_12 rxpush - * - * This works like ``rxhdrs``, expecting a PUSH frame and then zero or more - * CONTINUATION frames. - * - * \-all - * Keep waiting for CONTINUATION frames until END_HEADERS flag is seen. - * - * \-some INT - * Retrieve INT - 1 CONTINUATION frames after the PUSH frame. - * - */ -static void -cmd_rxpush(CMD_ARGS) -{ - struct stream *s; - struct frame *f = NULL; - char *p; - int loop = 0; - unsigned long int times = 1; - unsigned rcv = 0; - enum h2_type_e expect = TYPE_PUSH_PROMISE; - - CAST_OBJ_NOTNULL(s, priv, STREAM_MAGIC); - - while (*++av) { - if (!strcmp(*av, "-some")) { - STRTOU32_CHECK(times, av, p, vl, "-some", 0); - if (!times) - vtc_fatal(vl, "-some argument must be more" - "than 0 (found \"%s\")\n", *av); - } else if (!strcmp(*av, "-all")) { - loop = 1; - } else - break; - } - if (*av != NULL) - vtc_fatal(vl, "Unknown rxpush spec: %s\n", *av); - - do { - f = rxstuff(s); - if (!f) - return; - rcv++; - CHKFRAME(f->type, expect, rcv, "rxpush"); - expect = TYPE_CONTINUATION; - } while (rcv < times || (loop && !(f->flags & END_HEADERS))); - s->frame = f; -} - -/* SECTION: stream.spec.winup_rxwinup rxwinup - * - * Receive a WINDOW_UPDATE frame. - */ -static void -cmd_rxwinup(CMD_ARGS) -{ - struct stream *s; - struct frame *f; - - CAST_OBJ_NOTNULL(s, priv, STREAM_MAGIC); - s->frame = rxstuff(s); - CAST_OBJ_NOTNULL(f, s->frame, FRAME_MAGIC); - CHKFRAME(f->type, TYPE_WINDOW_UPDATE, 0, *av); - if (s->id == 0) - s->hp->h2_win_peer->size += s->frame->md.winup_size; - s->win_peer += s->frame->md.winup_size; -} - -/* SECTION: stream.spec.settings_rxsettings rxsettings - * - * Receive a SETTINGS frame. - */ -static void -cmd_rxsettings(CMD_ARGS) -{ - struct stream *s, *s2; - uint32_t val = 0; - struct http *hp; - struct frame *f; - - CAST_OBJ_NOTNULL(s, priv, STREAM_MAGIC); - CAST_OBJ_NOTNULL(hp, s->hp, HTTP_MAGIC); - s->frame = rxstuff(s); - CAST_OBJ_NOTNULL(f, s->frame, FRAME_MAGIC); - CHKFRAME(f->type, TYPE_SETTINGS, 0, *av); - if (! isnan(f->md.settings[SETTINGS_INITIAL_WINDOW_SIZE])) { - val = (uint32_t)f->md.settings[SETTINGS_INITIAL_WINDOW_SIZE]; - VTAILQ_FOREACH(s2, &hp->streams, list) - s2->win_peer += (val - hp->h2_win_peer->init); - hp->h2_win_peer->init = val; - } -} -/* SECTION: stream.spec.prio_rxprio rxprio - * - * Receive a PRIORITY frame. - */ -static void -cmd_rxprio (CMD_ARGS) -{ - struct stream *s; - (void)av; - CAST_OBJ_NOTNULL(s, priv, STREAM_MAGIC); - if (s->hp->no_rfc7540_priorities) { - vtc_log(vl, 4, "skipping rxprio: no_rfc7540_priorities is set"); - return; - } - s->frame = rxstuff(s); - if (s->frame != NULL && s->frame->type != TYPE_PRIORITY) { - vtc_fatal(vl, - "Wrong frame type %s (%d) wanted %s", - s->frame->type < TYPE_MAX ? - h2_types[s->frame->type] : "?", - s->frame->type, "PRIORITY"); - } -} - -#define RXFUNC(lctype, upctype) \ - static void \ - cmd_rx ## lctype(CMD_ARGS) \ - { \ - struct stream *s; \ - (void)av; \ - CAST_OBJ_NOTNULL(s, priv, STREAM_MAGIC); \ - s->frame = rxstuff(s); \ - if (s->frame != NULL && s->frame->type != TYPE_ ## upctype) { \ - vtc_fatal(vl, \ - "Wrong frame type %s (%d) wanted %s", \ - s->frame->type < TYPE_MAX ? \ - h2_types[s->frame->type] : "?", \ - s->frame->type, #upctype); \ - } \ - } - -/* SECTION: stream.spec.reset_rxrst rxrst - * - * Receive a RST_STREAM frame. - */ -RXFUNC(rst, RST_STREAM) - -/* SECTION: stream.spec.ping_rxping rxping - * - * Receive a PING frame. - */ -RXFUNC(ping, PING) - -/* SECTION: stream.spec.goaway_rxgoaway rxgoaway - * - * Receive a GOAWAY frame. - */ -RXFUNC(goaway, GOAWAY) - -/* SECTION: stream.spec.frame_rxframe - * - * Receive a frame, any frame. - */ -static void -cmd_rxframe(CMD_ARGS) -{ - struct stream *s; - - (void)vl; - (void)av; - CAST_OBJ_NOTNULL(s, priv, STREAM_MAGIC); - if (rxstuff(s) == NULL) - vtc_fatal(s->vl, "No frame received"); -} - -static void -cmd_expect(CMD_ARGS) -{ - struct http *hp; - struct stream *s; - const char *lhs; - char *cmp; - const char *rhs; - char buf[20]; - - CAST_OBJ_NOTNULL(s, priv, STREAM_MAGIC); - hp = s->hp; - CHECK_OBJ_NOTNULL(hp, HTTP_MAGIC); - - AZ(strcmp(av[0], "expect")); - av++; - - AN(av[0]); - AN(av[1]); - AN(av[2]); - AZ(av[3]); - PTOK(pthread_mutex_lock(&s->hp->mtx)); - lhs = cmd_var_resolve(s, av[0], buf); - cmp = av[1]; - rhs = cmd_var_resolve(s, av[2], buf); - vtc_expect(vl, av[0], lhs, cmp, av[2], rhs); - PTOK(pthread_mutex_unlock(&s->hp->mtx)); -} - -/* SECTION: stream.spec.gunzip gunzip - * - * Same as the ``gunzip`` command for HTTP/1. - */ -static void -cmd_gunzip(CMD_ARGS) -{ - struct http *hp; - struct stream *s; - - (void)av; - (void)vl; - - CAST_OBJ_NOTNULL(s, priv, STREAM_MAGIC); - hp = s->hp; - CHECK_OBJ_NOTNULL(hp, HTTP_MAGIC); - - vtc_gunzip(s->hp, s->body, &s->bodylen); -} - -/* SECTION: stream.spec.write_body - * - * write_body STRING - * Same as the ``write_body`` command for HTTP/1. - */ -static void -cmd_write_body(CMD_ARGS) -{ - struct stream *s; - - (void)vl; - CAST_OBJ_NOTNULL(s, priv, STREAM_MAGIC); - AN(av[0]); - AN(av[1]); - AZ(av[2]); - AZ(strcmp(av[0], "write_body")); - if (VFIL_writefile(NULL, av[1], s->body, s->bodylen) != 0) - vtc_fatal(s->vl, "failed to write body: %s (%d)", - strerror(errno), errno); -} - -/* SECTION: stream.spec Specification - * - * The specification of a stream follows the exact same rules as one for a - * client or a server. - */ -static const struct cmds stream_cmds[] = { -#define CMD_STREAM(n) { #n, cmd_##n }, - /* spec */ - CMD_STREAM(expect) - CMD_STREAM(gunzip) - CMD_STREAM(rxcont) - CMD_STREAM(rxdata) - CMD_STREAM(rxframe) - CMD_STREAM(rxgoaway) - CMD_STREAM(rxhdrs) - CMD_STREAM(rxping) - CMD_STREAM(rxprio) - CMD_STREAM(rxpush) - CMD_STREAM(rxreq) - CMD_STREAM(rxresp) - CMD_STREAM(rxrst) - CMD_STREAM(rxsettings) - CMD_STREAM(rxwinup) - CMD_STREAM(sendhex) - CMD_STREAM(txcont) - CMD_STREAM(txdata) - CMD_STREAM(txgoaway) - CMD_STREAM(txping) - CMD_STREAM(txprio) - CMD_STREAM(txpush) - CMD_STREAM(txreq) - CMD_STREAM(txresp) - CMD_STREAM(txrst) - CMD_STREAM(txsettings) - CMD_STREAM(txwinup) - CMD_STREAM(write_body) - { NULL, NULL } -#undef CMD_STREAM -}; - -static void * -stream_thread(void *priv) -{ - struct stream *s; - - CAST_OBJ_NOTNULL(s, priv, STREAM_MAGIC); - parse_string(s->vl, s, s->spec); - vtc_log(s->vl, 2, "Ending stream %u", s->id); - return (NULL); -} -/********************************************************************** - * Allocate and initialize a stream - */ - -static struct stream * -stream_new(const char *name, struct http *h) -{ - char *p, buf[20]; - struct stream *s; - - if (!strcmp("next", name)) { - if (h->last_stream > 0) - bprintf(buf, "%d", h->last_stream + 2); - else - bprintf(buf, "%d", 1); - name = buf; - } - - ALLOC_OBJ(s, STREAM_MAGIC); - AN(s); - PTOK(pthread_cond_init(&s->cond, NULL)); - REPLACE(s->name, name); - AN(s->name); - VTAILQ_INIT(&s->fq); - s->win_self = h->h2_win_self->init; - s->win_peer = h->h2_win_peer->init; - s->vl = vtc_logopen("%s.%s", h->sess->name, name); - vtc_log_set_cmd(s->vl, stream_cmds); - - s->weight = 16; - s->dependency = 0; - - STRTOU32(s->id, name, p, s->vl, "stream"); - if (s->id & (1U << 31)) - vtc_fatal(s->vl, "Stream id must be a 31-bits integer " - "(found %s)", name); - - CHECK_OBJ_NOTNULL(h, HTTP_MAGIC); - s->hp = h; - h->last_stream = s->id; - - //bprintf(s->connect, "%s", "${v1_sock}"); - PTOK(pthread_mutex_lock(&h->mtx)); - VTAILQ_INSERT_HEAD(&h->streams, s, list); - PTOK(pthread_mutex_unlock(&h->mtx)); - return (s); -} - -/********************************************************************** - * Clean up stream - */ - -static void -stream_delete(struct stream *s) -{ - struct frame *f, *f2; - - CHECK_OBJ_NOTNULL(s, STREAM_MAGIC); - - VTAILQ_FOREACH_SAFE(f, &s->fq, list, f2) { - VTAILQ_REMOVE(&s->fq, f, list); - clean_frame(&f); - } - vtc_logclose(s->vl); - clean_headers(s->req); - clean_headers(s->resp); - AZ(s->frame); - free(s->body); - free(s->spec); - free(s->name); - FREE_OBJ(s); -} - -/********************************************************************** - * Start the stream thread - */ - -static void -stream_start(struct stream *s) -{ - CHECK_OBJ_NOTNULL(s, STREAM_MAGIC); - vtc_log(s->hp->vl, 2, "Starting stream %s (%p)", s->name, s); - PTOK(pthread_create(&s->tp, NULL, stream_thread, s)); - s->running = 1; -} - -/********************************************************************** - * Wait for stream thread to stop - */ -static void -stream_wait(struct stream *s) -{ - void *res; - struct frame *f, *f2; - - CHECK_OBJ_NOTNULL(s, STREAM_MAGIC); - vtc_log(s->hp->vl, 2, "Waiting for stream %u", s->id); - PTOK(pthread_join(s->tp, &res)); - if (res != NULL) - vtc_fatal(s->hp->vl, "Stream %u returned \"%s\"", s->id, - (char *)res); - - VTAILQ_FOREACH_SAFE(f, &s->fq, list, f2) { - VTAILQ_REMOVE(&s->fq, f, list); - clean_frame(&f); - } - clean_frame(&s->frame); - s->tp = 0; - s->running = 0; -} - -/********************************************************************** - * Run the stream thread - */ - -static void -stream_run(struct stream *s) -{ - stream_start(s); - stream_wait(s); -} - - - -/* SECTION: client-server.spec.stream - * - * stream - * HTTP/2 introduces the concept of streams, and these come with - * their own specification, and as it's quite big, have been moved - * to their own chapter. - * - * SECTION: stream stream - * - * (note: this section is at the top-level for easier navigation, but - * it's part of the client/server specification) - * - * Streams map roughly to a request in HTTP/2, a request is sent on - * stream N, the response too, then the stream is discarded. The main - * exception is the first stream, 0, that serves as coordinator. - * - * Stream syntax follow the client/server one:: - * - * stream ID [SPEC] [ACTION] - * - * ID is the HTTP/2 stream number, while SPEC describes what will be - * done in that stream. If ID has the value ``next``, the actual stream - * number is computed based on the last one. - * - * Note that, when parsing a stream action, if the entity isn't operating - * in HTTP/2 mode, these spec is ran before:: - * - * txpri/rxpri # client/server - * stream 0 { - * txsettings - * rxsettings - * txsettings -ack - * rxsettings - * expect settings.ack == true - * } -run - * - * And HTTP/2 mode is then activated before parsing the specification. - * - * SECTION: stream.actions Actions - * - * \-start - * Run the specification in a thread, giving back control immediately. - * - * \-wait - * Wait for the started thread to finish running the spec. - * - * \-run - * equivalent to calling ``-start`` then ``-wait``. - */ - -void -cmd_stream(CMD_ARGS) -{ - struct stream *s; - struct http *h; - - (void)vl; - CAST_OBJ_NOTNULL(h, priv, HTTP_MAGIC); - - AZ(strcmp(av[0], "stream")); - av++; - - VTAILQ_FOREACH(s, &h->streams, list) - if (!strcmp(s->name, av[0])) - break; - if (s == NULL) - s = stream_new(av[0], h); - av++; - - for (; *av != NULL; av++) { - if (vtc_error) - break; - - if (!strcmp(*av, "-wait")) { - stream_wait(s); - continue; - } - - /* Don't muck about with a running client */ - if (s->running) - stream_wait(s); - - if (!strcmp(*av, "-start")) { - stream_start(s); - continue; - } - if (!strcmp(*av, "-run")) { - stream_run(s); - continue; - } - if (**av == '-') - vtc_fatal(vl, "Unknown stream argument: %s", *av); - REPLACE(s->spec, *av); - } -} - -void -b64_settings(struct http *hp, const char *s) -{ - uint16_t i; - uint64_t v, vv; - const char *buf; - int shift; - - while (*s) { - v = 0; - for (shift = 42; shift >= 0; shift -= 6) { - if (*s >= 'A' && *s <= 'Z') - vv = (*s - 'A'); - else if (*s >= 'a' && *s <= 'z') - vv = (*s - 'a') + 26; - else if (*s >= '0' && *s <= '9') - vv = (*s - '0') + 52; - else if (*s == '-') - vv = 62; - else if (*s == '_') - vv = 63; - else - vtc_fatal(hp->vl, - "Bad \"HTTP2-Settings\" header"); - v |= vv << shift; - s++; - } - i = v >> 32; - v &= 0xffff; - - if (i <= SETTINGS_MAX) - buf = h2_settings[i]; - else - buf = "unknown"; - - if (i == SETTINGS_NO_RFC7540_PRIORITIES) { - hp->no_rfc7540_priorities = v; - } - if (i == SETTINGS_HEADER_TABLE_SIZE) { - enum hpk_result hrs; - if (hp->sfd) { - AN(hp->encctx); - hrs = HPK_ResizeTbl(hp->encctx, v); - } else { - AN(hp->decctx); - hrs = HPK_ResizeTbl(hp->decctx, v); - } - if (hrs != hpk_done) - vtc_fatal(hp->vl, "HPK resize failed %d\n", hrs); - } - - vtc_log(hp->vl, 4, "Upgrade: %s (%d): %ju", - buf, i, (intmax_t)v); - } -} - -void -start_h2(struct http *hp) -{ - CHECK_OBJ_NOTNULL(hp, HTTP_MAGIC); - PTOK(pthread_mutex_init(&hp->mtx, NULL)); - PTOK(pthread_cond_init(&hp->cond, NULL)); - VTAILQ_INIT(&hp->streams); - hp->h2_win_self->init = 0xffff; - hp->h2_win_self->size = 0xffff; - hp->h2_win_peer->init = 0xffff; - hp->h2_win_peer->size = 0xffff; - hp->h2 = 1; - - hp->decctx = HPK_NewCtx(4096); - hp->encctx = HPK_NewCtx(4096); - PTOK(pthread_create(&hp->tp, NULL, receive_frame, hp)); -} - -void -stop_h2(struct http *hp) -{ - struct stream *s, *s2; - - CHECK_OBJ_NOTNULL(hp, HTTP_MAGIC); - VTAILQ_FOREACH_SAFE(s, &hp->streams, list, s2) { - if (s->running) - stream_wait(s); - PTOK(pthread_mutex_lock(&hp->mtx)); - VTAILQ_REMOVE(&hp->streams, s, list); - PTOK(pthread_mutex_unlock(&hp->mtx)); - stream_delete(s); - } - - PTOK(pthread_mutex_lock(&hp->mtx)); - hp->h2 = 0; - PTOK(pthread_cond_signal(&hp->cond)); - PTOK(pthread_mutex_unlock(&hp->mtx)); - PTOK(pthread_join(hp->tp, NULL)); - - HPK_FreeCtx(hp->decctx); - HPK_FreeCtx(hp->encctx); - - PTOK(pthread_mutex_destroy(&hp->mtx)); - PTOK(pthread_cond_destroy(&hp->cond)); -} diff --git a/bin/varnishtest/vtc_log.c b/bin/varnishtest/vtc_log.c deleted file mode 100644 index c42a94553..000000000 --- a/bin/varnishtest/vtc_log.c +++ /dev/null @@ -1,342 +0,0 @@ -/*- - * Copyright (c) 2008-2011 Varnish Software AS - * All rights reserved. - * - * Author: Poul-Henning Kamp - * - * SPDX-License-Identifier: BSD-2-Clause - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#include "config.h" - -#include -#include -#include -#include -#include - -#include "vtc.h" -#include "vtc_log.h" - -#include "vtim.h" -#include "vbt.h" - -static pthread_mutex_t vtclog_mtx; -static char *vtclog_buf; -static unsigned vtclog_left; - -static pthread_key_t log_key; -static double t0; - -void -vtc_log_set_cmd(struct vtclog *vl, const struct cmds *cmds) -{ - AN(cmds); - vl->cmds = cmds; -} - -/**********************************************************************/ - -#define GET_VL(vl) \ - do { \ - CHECK_OBJ_NOTNULL(vl, VTCLOG_MAGIC); \ - PTOK(pthread_mutex_lock(&vl->mtx)); \ - vl->act = 1; \ - VSB_clear(vl->vsb); \ - } while(0) - -#define REL_VL(vl) \ - do { \ - AZ(VSB_finish(vl->vsb)); \ - vtc_log_emit(vl); \ - VSB_clear(vl->vsb); \ - vl->act = 0; \ - PTOK(pthread_mutex_unlock(&vl->mtx)); \ - } while(0) - - -struct vtclog * -vtc_logopen(const char *fmt, ...) -{ - struct vtclog *vl; - va_list ap; - char buf[BUFSIZ]; - - va_start(ap, fmt); - vbprintf(buf, fmt, ap); - va_end(ap); - - ALLOC_OBJ(vl, VTCLOG_MAGIC); - AN(vl); - REPLACE(vl->id, buf); - vl->vsb = VSB_new_auto(); - PTOK(pthread_mutex_init(&vl->mtx, NULL)); - PTOK(pthread_setspecific(log_key, vl)); - return (vl); -} - -void -vtc_logclose(void *arg) -{ - struct vtclog *vl; - - CAST_OBJ_NOTNULL(vl, arg, VTCLOG_MAGIC); - if (pthread_getspecific(log_key) == vl) - PTOK(pthread_setspecific(log_key, NULL)); - VSB_destroy(&vl->vsb); - PTOK(pthread_mutex_destroy(&vl->mtx)); - REPLACE(vl->id, NULL); - FREE_OBJ(vl); -} - -static void v_noreturn_ -vtc_logfail(void) -{ - - vtc_error = 2; - if (!pthread_equal(pthread_self(), vtc_thread)) - pthread_exit(NULL); - else - exit(fail_out()); - - WRONG("unreachable"); /*lint !e827 Help Coverity Scan see noreturn. */ -} - -static const char * const lead[] = { - "----", - "* ", - "** ", - "*** ", - "****" -}; - -#define NLEAD (sizeof(lead)/sizeof(lead[0])) - -static void -vtc_leadinv(const struct vtclog *vl, int lvl, const char *fmt, va_list ap) -{ - - assert(lvl < (int)NLEAD); - assert(lvl >= 0); - VSB_printf(vl->vsb, "%s %-5s ", - lead[lvl < 0 ? 1: lvl], vl->id); - if (fmt != NULL) - (void)VSB_vprintf(vl->vsb, fmt, ap); -} - -static void -vtc_leadin(const struct vtclog *vl, int lvl, const char *fmt, ...) -{ - va_list ap; - - va_start(ap, fmt); - vtc_leadinv(vl, lvl, fmt, ap); - va_end(ap); -} - -static void -vtc_log_emit(const struct vtclog *vl) -{ - unsigned l; - int i; - int t_this; - static int t_last = -1; - - l = VSB_len(vl->vsb); - if (l == 0) - return; - t_this = (int)round((VTIM_mono() - t0) * 1000); - PTOK(pthread_mutex_lock(&vtclog_mtx)); - if (t_last != t_this) { - assert(vtclog_left > 25); - i = snprintf(vtclog_buf, vtclog_left, - "**** dT %d.%03d\n", t_this / 1000, t_this % 1000); - t_last = t_this; - vtclog_buf += i; - vtclog_left -= i; - } - assert(vtclog_left > l); - memcpy(vtclog_buf, VSB_data(vl->vsb), l); - vtclog_buf += l; - *vtclog_buf = '\0'; - vtclog_left -= l; - PTOK(pthread_mutex_unlock(&vtclog_mtx)); -} - -void -vtc_fatal(struct vtclog *vl, const char *fmt, ...) -{ - - GET_VL(vl); - va_list ap; - va_start(ap, fmt); - vtc_leadinv(vl, 0, fmt, ap); - VSB_putc(vl->vsb, '\n'); - va_end(ap); - REL_VL(vl); - - vtc_logfail(); -} - -void -vtc_log(struct vtclog *vl, int lvl, const char *fmt, ...) -{ - - GET_VL(vl); - va_list ap; - va_start(ap, fmt); - if (lvl >= 0) { - vtc_leadinv(vl, lvl, fmt, ap); - VSB_putc(vl->vsb, '\n'); - } - va_end(ap); - REL_VL(vl); - - if (lvl == 0) - vtc_logfail(); -} - -/********************************************************************** - * Dump a string - */ - -#define MAX_DUMP 8192 - -void -vtc_dump(struct vtclog *vl, int lvl, const char *pfx, const char *str, int len) -{ - char buf[64]; - int quote = VSB_QUOTE_UNSAFE | VSB_QUOTE_ESCHEX; - - AN(pfx); - GET_VL(vl); - if (str == NULL) - vtc_leadin(vl, lvl, "%s(null)\n", pfx); - else { - bprintf(buf, "%s %-5s %s|", - lead[lvl < 0 ? 1: lvl], vl->id, pfx); - if (len < 0) - len = strlen(str); - else if (str[0] == 0x1f && (uint8_t)str[1] == 0x8b) - quote = VSB_QUOTE_HEX; // Dump gzip data in HEX - VSB_quote_pfx(vl->vsb, buf, str, - len > MAX_DUMP ? MAX_DUMP : len, quote); - if (quote == VSB_QUOTE_HEX) - VSB_putc(vl->vsb, '\n'); - if (len > MAX_DUMP) - VSB_printf(vl->vsb, "%s [...] (%d)\n", - buf, len - MAX_DUMP); - } - REL_VL(vl); - if (lvl == 0) - vtc_logfail(); -} - -/********************************************************************** - * Hexdump - */ - -void -vtc_hexdump(struct vtclog *vl, int lvl, const char *pfx, - const void *ptr, unsigned len) -{ - int nl = 1; - unsigned l; - const uint8_t *ss = ptr; - - AN(pfx); - GET_VL(vl); - if (ss == NULL) - vtc_leadin(vl, lvl, "%s(null)\n", pfx); - else { - for (l = 0; l < len; l++, ss++) { - if (l > 512) { - VSB_cat(vl->vsb, "..."); - break; - } - if (nl) { - vtc_leadin(vl, lvl, "%s| ", pfx); - nl = 0; - } - VSB_printf(vl->vsb, " %02x", *ss); - if ((l & 0xf) == 0xf) { - VSB_cat(vl->vsb, "\n"); - nl = 1; - } - } - } - if (!nl) - VSB_cat(vl->vsb, "\n"); - REL_VL(vl); - if (lvl == 0) - vtc_logfail(); -} - -/**********************************************************************/ - -static void v_noreturn_ -vtc_log_VAS_Fail(const char *func, const char *file, int line, - const char *cond, enum vas_e why) -{ - char buf[4096] = ""; - struct vtclog *vl; - int e = errno; - - (void)why; - - if (VBT_dump(sizeof buf, buf) < 0) { - bprintf(buf, "Failed to print backtrace: %d (%s)\n", - errno, strerror(errno)); - } - - vl = pthread_getspecific(log_key); - if (vl == NULL || vl->act) { - fprintf(stderr, - "Assert error in %s(), %s line %d:\n" - " Condition(%s) not true. (errno=%d %s)\n" - "%s\n", - func, file, line, cond, e, strerror(e), buf); - } else { - vtc_fatal(vl, "Assert error in %s(), %s line %d:" - " Condition(%s) not true." - " Errno=%d %s\n" - "%s\n", - func, file, line, cond, e, strerror(e), buf); - } - abort(); -} - -/**********************************************************************/ - -void -vtc_loginit(char *buf, unsigned buflen) -{ - - VAS_Fail_Func = vtc_log_VAS_Fail; - t0 = VTIM_mono(); - vtclog_buf = buf; - vtclog_left = buflen; - PTOK(pthread_mutex_init(&vtclog_mtx, NULL)); - PTOK(pthread_key_create(&log_key, NULL)); -} diff --git a/bin/varnishtest/vtc_log.h b/bin/varnishtest/vtc_log.h deleted file mode 100644 index 0075a9001..000000000 --- a/bin/varnishtest/vtc_log.h +++ /dev/null @@ -1,40 +0,0 @@ -/*- - * Copyright (c) 2008-2011 Varnish Software AS - * All rights reserved. - * - * Author: Poul-Henning Kamp - * - * SPDX-License-Identifier: BSD-2-Clause - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - - -struct vtclog { - unsigned magic; -#define VTCLOG_MAGIC 0x82731202 - char *id; - struct vsb *vsb; - pthread_mutex_t mtx; - int act; - const struct cmds *cmds; -}; diff --git a/bin/varnishtest/vtc_logexp.c b/bin/varnishtest/vtc_logexp.c deleted file mode 100644 index 705cd96d1..000000000 --- a/bin/varnishtest/vtc_logexp.c +++ /dev/null @@ -1,906 +0,0 @@ -/*- - * Copyright (c) 2008-2015 Varnish Software AS - * All rights reserved. - * - * Author: Martin Blix Grydeland - * - * SPDX-License-Identifier: BSD-2-Clause - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#ifdef VTEST_WITH_VTC_LOGEXPECT - -/* SECTION: logexpect logexpect - * - * Reads the VSL and looks for records matching a given specification. It will - * process records trying to match the first pattern, and when done, will - * continue processing, trying to match the following pattern. If a pattern - * isn't matched, the test will fail. - * - * logexpect threads are declared this way:: - * - * logexpect lNAME -v [-g ] [-d 0|1] [-q query] \ - * [vsl arguments] { - * expect - * expect - * fail add - * fail clear - * abort - * ... - * } [-start|-wait|-run] - * - * And once declared, you can start them, or wait on them:: - * - * logexpect lNAME <-start|-wait> - * - * With: - * - * lNAME - * Name the logexpect thread, it must start with 'l'. - * - * \-v id - * Specify the varnish instance to use (most of the time, id=v1). - * - * \-g - * Start processing log records at the head of the log instead of the - * tail. - * - * \-q query - * Filter records using a query expression, see ``man vsl-query`` for - * more information. Multiple -q options are not supported. - * - * \-m - * Also emit log records for misses (only for debugging) - * - * \-err - * Invert the meaning of success. Usually called once to expect the - * logexpect to fail - * - * \-start - * Start the logexpect thread in the background. - * - * \-wait - * Wait for the logexpect thread to finish - * - * \-run - * Equivalent to "-start -wait". - * - * VSL arguments (similar to the varnishlog options): - * - * \-C - * Use caseless regex - * - * \-i - * Include tags - * - * \-I <[taglist:]regex> - * Include by regex - * - * \-T - * Transaction end timeout - * - * expect specification: - * - * skip: [uint|*|?] - * Max number of record to skip - * - * vxid: [uint|*|=] - * vxid to match - * - * tag: [tagname|*|=] - * Tag to match against - * - * regex: - * regular expression to match against (optional) - * - * For skip, vxid and tag, '*' matches anything, '=' expects the value of the - * previous matched record. The '?' marker is equivalent to zero, expecting a - * match on the next record. The difference is that '?' can be used when the - * order of individual consecutive logs is not deterministic. In other words, - * lines from a block of alternatives marked by '?' can be matched in any order, - * but all need to match eventually. - * - * fail specification: - * - * add: Add to the fail list - * - * Arguments are equivalent to expect, except for skip missing - * - * clear: Clear the fail list - * - * Any number of fail specifications can be active during execution of - * a logexpect. All active fail specifications are matched against every - * log line and, if any match, the logexpect fails immediately. - * - * For a logexpect to end successfully, there must be no specs on the fail list, - * so logexpects should always end with - * - * expect - * fail clear - * - * .. XXX can we come up with a better solution which is still safe? - * - * abort specification: - * - * abort(3) varnishtest, intended to help debugging of the VSL client library - * itself. - */ - -#include "config.h" - -#include -#include -#include -#include - -#include "vapi/vsm.h" -#include "vapi/vsl.h" - -#include "vtc.h" - -#include "vtim.h" -#include "vre.h" - -#define LE_ANY (-1) -#define LE_LAST (-2) -#define LE_ALT (-3) -#define LE_SEEN (-4) -#define LE_FAIL (-5) -#define LE_CLEAR (-6) // clear fail list -#define LE_ABORT (-7) - -struct logexp_test { - unsigned magic; -#define LOGEXP_TEST_MAGIC 0x6F62B350 - VTAILQ_ENTRY(logexp_test) list; - VTAILQ_ENTRY(logexp_test) faillist; - - struct vsb *str; - int64_t vxid; - int tag; - vre_t *vre; - int skip_max; -}; - -VTAILQ_HEAD(tests_head,logexp_test); - -struct logexp { - unsigned magic; -#define LOGEXP_MAGIC 0xE81D9F1B - VTAILQ_ENTRY(logexp) list; - - char *name; - char *vname; - struct vtclog *vl; - char run; - struct tests_head tests; - - struct logexp_test *test; - int skip_cnt; - int64_t vxid_last; - int tag_last; - - struct tests_head fail; - - int m_arg; - int err_arg; - int d_arg; - enum VSL_grouping_e g_arg; - char *query; - - struct vsm *vsm; - struct VSL_data *vsl; - struct VSLQ *vslq; - pthread_t tp; -}; - -static VTAILQ_HEAD(, logexp) logexps = - VTAILQ_HEAD_INITIALIZER(logexps); - -static cmd_f cmd_logexp_expect; -static cmd_f cmd_logexp_fail; -static cmd_f cmd_logexp_abort; - -static const struct cmds logexp_cmds[] = { - { "expect", cmd_logexp_expect }, - { "fail", cmd_logexp_fail }, - { "abort", cmd_logexp_abort }, - { NULL, NULL }, -}; - -static void -logexp_delete_tests(struct logexp *le) -{ - struct logexp_test *test; - - CHECK_OBJ_NOTNULL(le, LOGEXP_MAGIC); - while (!VTAILQ_EMPTY(&le->tests)) { - test = VTAILQ_FIRST(&le->tests); - CHECK_OBJ_NOTNULL(test, LOGEXP_TEST_MAGIC); - VTAILQ_REMOVE(&le->tests, test, list); - VSB_destroy(&test->str); - if (test->vre) - VRE_free(&test->vre); - FREE_OBJ(test); - } -} - -static void -logexp_delete(struct logexp *le) -{ - CHECK_OBJ_NOTNULL(le, LOGEXP_MAGIC); - AZ(le->run); - AN(le->vsl); - VSL_Delete(le->vsl); - AZ(le->vslq); - logexp_delete_tests(le); - free(le->name); - free(le->vname); - free(le->query); - VSM_Destroy(&le->vsm); - vtc_logclose(le->vl); - FREE_OBJ(le); -} - -static struct logexp * -logexp_new(const char *name, const char *varg) -{ - struct logexp *le; - struct vsb *n_arg; - - ALLOC_OBJ(le, LOGEXP_MAGIC); - AN(le); - REPLACE(le->name, name); - le->vl = vtc_logopen("%s", name); - vtc_log_set_cmd(le->vl, logexp_cmds); - VTAILQ_INIT(&le->tests); - - le->d_arg = 0; - le->g_arg = VSL_g_vxid; - le->vsm = VSM_New(); - le->vsl = VSL_New(); - AN(le->vsm); - AN(le->vsl); - - VTAILQ_INSERT_TAIL(&logexps, le, list); - - REPLACE(le->vname, varg); - - n_arg = macro_expandf(le->vl, "${tmpdir}/%s", varg); - if (n_arg == NULL) - vtc_fatal(le->vl, "-v argument problems"); - if (VSM_Arg(le->vsm, 'n', VSB_data(n_arg)) <= 0) - vtc_fatal(le->vl, "-v argument error: %s", - VSM_Error(le->vsm)); - VSB_destroy(&n_arg); - if (VSM_Attach(le->vsm, -1)) - vtc_fatal(le->vl, "VSM_Attach: %s", VSM_Error(le->vsm)); - return (le); -} - -static void -logexp_clean(const struct tests_head *head) -{ - struct logexp_test *test; - - VTAILQ_FOREACH(test, head, list) - if (test->skip_max == LE_SEEN) - test->skip_max = LE_ALT; -} - -static struct logexp_test * -logexp_alt(struct logexp_test *test) -{ - assert(test->skip_max == LE_ALT); - - do - test = VTAILQ_NEXT(test, list); - while (test != NULL && test->skip_max == LE_SEEN); - - if (test == NULL || test->skip_max != LE_ALT) - return (NULL); - - return (test); -} - -static void -logexp_next(struct logexp *le) -{ - CHECK_OBJ_NOTNULL(le, LOGEXP_MAGIC); - - if (le->test && le->test->skip_max == LE_ALT) { - /* - * if an alternative was not seen, continue at this expection - * with the next vsl - */ - (void)0; - } else if (le->test) { - CHECK_OBJ_NOTNULL(le->test, LOGEXP_TEST_MAGIC); - le->test = VTAILQ_NEXT(le->test, list); - } else { - logexp_clean(&le->tests); - VTAILQ_INIT(&le->fail); - le->test = VTAILQ_FIRST(&le->tests); - } - - if (le->test == NULL) - return; - - CHECK_OBJ(le->test, LOGEXP_TEST_MAGIC); - - switch (le->test->skip_max) { - case LE_SEEN: - logexp_next(le); - return; - case LE_CLEAR: - vtc_log(le->vl, 3, "cond | fail clear"); - VTAILQ_INIT(&le->fail); - logexp_next(le); - return; - case LE_FAIL: - vtc_log(le->vl, 3, "cond | %s", VSB_data(le->test->str)); - VTAILQ_INSERT_TAIL(&le->fail, le->test, faillist); - logexp_next(le); - return; - case LE_ABORT: - abort(); - NEEDLESS(return); - default: - vtc_log(le->vl, 3, "test | %s", VSB_data(le->test->str)); - } -} - -enum le_match_e { - LEM_OK, - LEM_SKIP, - LEM_FAIL -}; - -static enum le_match_e -logexp_match(const struct logexp *le, struct logexp_test *test, - const char *data, int64_t vxid, int tag, int type, int len) -{ - const char *legend; - int ok = 1, skip = 0, alt, fail, vxid_ok = 0; - - AN(le); - AN(test); - assert(test->skip_max != LE_SEEN); - assert(test->skip_max != LE_CLEAR); - - if (test->vxid == LE_LAST) { - if (le->vxid_last != vxid) - ok = 0; - vxid_ok = ok; - } else if (test->vxid >= 0) { - if (test->vxid != vxid) - ok = 0; - vxid_ok = ok; - } - if (test->tag == LE_LAST) { - if (le->tag_last != tag) - ok = 0; - } else if (test->tag >= 0) { - if (test->tag != tag) - ok = 0; - } - if (test->vre && - test->tag >= 0 && - test->tag == tag && - VRE_ERROR_NOMATCH == VRE_match(test->vre, data, len, 0, NULL)) - ok = 0; - - alt = (test->skip_max == LE_ALT); - fail = (test->skip_max == LE_FAIL); - - if (!ok && !alt && (test->skip_max == LE_ANY || - test->skip_max > le->skip_cnt)) - skip = 1; - - if (skip && vxid_ok && tag == SLT_End) - fail = 1; - - if (fail) { - if (ok) { - legend = "fail"; - } else if (skip) { - legend = "end"; - skip = 0; - } else if (le->m_arg) { - legend = "fmiss"; - } else { - legend = NULL; - } - } - else if (ok) - legend = "match"; - else if (skip && le->m_arg) - legend = "miss"; - else if (skip || alt) - legend = NULL; - else - legend = "err"; - - if (legend != NULL) - vtc_log(le->vl, 4, "%-5s| %10ju %-15s %c %.*s", - legend, (intmax_t)vxid, VSL_tags[tag], type, len, - data); - - if (ok) { - if (alt) - test->skip_max = LE_SEEN; - return (LEM_OK); - } - if (alt) { - test = logexp_alt(test); - if (test == NULL) - return (LEM_FAIL); - vtc_log(le->vl, 3, "alt | %s", VSB_data(test->str)); - return (logexp_match(le, test, data, vxid, tag, type, len)); - } - if (skip) - return (LEM_SKIP); - return (LEM_FAIL); -} - -static enum le_match_e -logexp_failchk(const struct logexp *le, - const char *data, int64_t vxid, int tag, int type, int len) -{ - struct logexp_test *test; - static enum le_match_e r; - - if (VTAILQ_FIRST(&le->fail) == NULL) - return (LEM_SKIP); - - VTAILQ_FOREACH(test, &le->fail, faillist) { - r = logexp_match(le, test, data, vxid, tag, type, len); - if (r == LEM_OK) - return (LEM_FAIL); - assert (r == LEM_FAIL); - } - return (LEM_OK); -} - -static int -logexp_done(const struct logexp *le) -{ - return ((VTAILQ_FIRST(&le->fail) == NULL) && le->test == NULL); -} - -static int v_matchproto_(VSLQ_dispatch_f) -logexp_dispatch(struct VSL_data *vsl, struct VSL_transaction * const pt[], - void *priv) -{ - struct logexp *le; - struct VSL_transaction *t; - int i; - enum le_match_e r; - int64_t vxid; - int tag, type, len; - const char *data; - - CAST_OBJ_NOTNULL(le, priv, LOGEXP_MAGIC); - - for (i = 0; (t = pt[i]) != NULL; i++) { - while (1 == VSL_Next(t->c)) { - if (!VSL_Match(vsl, t->c)) - continue; - - AN(t->c->rec.ptr); - tag = VSL_TAG(t->c->rec.ptr); - - if (tag == SLT__Batch || tag == SLT_Witness) - continue; - - vxid = VSL_ID(t->c->rec.ptr); - data = VSL_CDATA(t->c->rec.ptr); - len = VSL_LEN(t->c->rec.ptr) - 1; - type = VSL_CLIENT(t->c->rec.ptr) ? 'c' : - VSL_BACKEND(t->c->rec.ptr) ? 'b' : '-'; - - r = logexp_failchk(le, data, vxid, tag, type, len); - if (r == LEM_FAIL) - return (r); - if (le->test == NULL) { - assert (r == LEM_OK); - continue; - } - - CHECK_OBJ_NOTNULL(le->test, LOGEXP_TEST_MAGIC); - - r = logexp_match(le, le->test, - data, vxid, tag, type, len); - if (r == LEM_FAIL) - return (r); - if (r == LEM_SKIP) { - le->skip_cnt++; - continue; - } - assert(r == LEM_OK); - le->vxid_last = vxid; - le->tag_last = tag; - le->skip_cnt = 0; - logexp_next(le); - if (logexp_done(le)) - return (1); - } - } - return (0); -} - -static void * -logexp_thread(void *priv) -{ - struct logexp *le; - int i; - - CAST_OBJ_NOTNULL(le, priv, LOGEXP_MAGIC); - AN(le->run); - AN(le->vsm); - AN(le->vslq); - - AZ(le->test); - vtc_log(le->vl, 4, "begin|"); - if (le->query != NULL) - vtc_log(le->vl, 4, "qry | %s", le->query); - logexp_next(le); - while (!logexp_done(le) && !vtc_stop && !vtc_error) { - i = VSLQ_Dispatch(le->vslq, logexp_dispatch, le); - if (i == 2 && le->err_arg) { - vtc_log(le->vl, 4, "done | failed as expected"); - return (NULL); - } - if (i == 2) - vtc_fatal(le->vl, "bad | expectation failed"); - else if (i < 0) - vtc_fatal(le->vl, "bad | dispatch failed (%d)", i); - else if (i == 0 && ! logexp_done(le)) - VTIM_sleep(0.01); - } - if (!logexp_done(le)) - vtc_fatal(le->vl, "bad | outstanding expectations"); - vtc_log(le->vl, 4, "done |"); - - return (NULL); -} - -static void -logexp_close(struct logexp *le) -{ - - CHECK_OBJ_NOTNULL(le, LOGEXP_MAGIC); - AN(le->vsm); - if (le->vslq) - VSLQ_Delete(&le->vslq); - AZ(le->vslq); -} - -static void -logexp_start(struct logexp *le) -{ - struct VSL_cursor *c; - - CHECK_OBJ_NOTNULL(le, LOGEXP_MAGIC); - AN(le->vsl); - AZ(le->vslq); - - AN(le->vsl); - (void)VSM_Status(le->vsm); - c = VSL_CursorVSM(le->vsl, le->vsm, - (le->d_arg ? 0 : VSL_COPT_TAIL) | VSL_COPT_BATCH); - if (c == NULL) - vtc_fatal(le->vl, "VSL_CursorVSM: %s", VSL_Error(le->vsl)); - le->vslq = VSLQ_New(le->vsl, &c, le->g_arg, le->query); - if (le->vslq == NULL) { - VSL_DeleteCursor(c); - vtc_fatal(le->vl, "VSLQ_New: %s", VSL_Error(le->vsl)); - } - AZ(c); - - le->test = NULL; - le->skip_cnt = 0; - le->vxid_last = le->tag_last = -1; - le->run = 1; - PTOK(pthread_create(&le->tp, NULL, logexp_thread, le)); -} - -static void -logexp_wait(struct logexp *le) -{ - void *res; - - CHECK_OBJ_NOTNULL(le, LOGEXP_MAGIC); - vtc_log(le->vl, 2, "Waiting for logexp"); - PTOK(pthread_join(le->tp, &res)); - logexp_close(le); - if (res != NULL && !vtc_stop) - vtc_fatal(le->vl, "logexp returned \"%p\"", (char *)res); - le->run = 0; -} - -/* shared by expect and fail: parse from av[2] (vxid) onwards */ - -static void -cmd_logexp_common(struct logexp *le, struct vtclog *vl, - int skip_max, char * const *av) -{ - vre_t *vre; - struct vsb vsb[1]; - int64_t vxid; - int err, pos, tag; - struct logexp_test *test; - char *end, errbuf[VRE_ERROR_LEN]; - - if (!strcmp(av[2], "*")) - vxid = LE_ANY; - else if (!strcmp(av[2], "=")) - vxid = LE_LAST; - else { - vxid = strtoll(av[2], &end, 10); - if (*end != '\0' || vxid < 0) - vtc_fatal(vl, "Not a positive integer: '%s'", av[2]); - } - if (!strcmp(av[3], "*")) - tag = LE_ANY; - else if (!strcmp(av[3], "=")) - tag = LE_LAST; - else { - tag = VSL_Name2Tag(av[3], strlen(av[3])); - if (tag < 0) - vtc_fatal(vl, "Unknown tag name: '%s'", av[3]); - } - vre = NULL; - if (av[4]) { - vre = VRE_compile(av[4], 0, &err, &pos, 1); - if (vre == NULL) { - AN(VSB_init(vsb, errbuf, sizeof errbuf)); - AZ(VRE_error(vsb, err)); - AZ(VSB_finish(vsb)); - VSB_fini(vsb); - vtc_fatal(vl, "Regex error (%s): '%s' pos %d", - errbuf, av[4], pos); - } - } - - ALLOC_OBJ(test, LOGEXP_TEST_MAGIC); - AN(test); - test->str = VSB_new_auto(); - AN(test->str); - AZ(VSB_printf(test->str, "%s %s %s %s ", av[0], av[1], av[2], av[3])); - if (av[4]) - VSB_quote(test->str, av[4], -1, 0); - AZ(VSB_finish(test->str)); - test->skip_max = skip_max; - test->vxid = vxid; - test->tag = tag; - test->vre = vre; - VTAILQ_INSERT_TAIL(&le->tests, test, list); -} - -static void -cmd_logexp_expect(CMD_ARGS) -{ - struct logexp *le; - int skip_max; - char *end; - - CAST_OBJ_NOTNULL(le, priv, LOGEXP_MAGIC); - if (av[1] == NULL || av[2] == NULL || av[3] == NULL) - vtc_fatal(vl, "Syntax error"); - - if (av[4] != NULL && av[5] != NULL) - vtc_fatal(vl, "Syntax error"); - - if (!strcmp(av[1], "*")) - skip_max = LE_ANY; - else if (!strcmp(av[1], "?")) - skip_max = LE_ALT; - else { - skip_max = (int)strtol(av[1], &end, 10); - if (*end != '\0' || skip_max < 0) - vtc_fatal(vl, "Not a positive integer: '%s'", av[1]); - } - cmd_logexp_common(le, vl, skip_max, av); -} - -static void -cmd_logexp_fail(CMD_ARGS) -{ - struct logexp *le; - struct logexp_test *test; - - CAST_OBJ_NOTNULL(le, priv, LOGEXP_MAGIC); - - if (av[1] == NULL) - vtc_fatal(vl, "Syntax error"); - - if (!strcmp(av[1], "clear")) { - ALLOC_OBJ(test, LOGEXP_TEST_MAGIC); - AN(test); - test->skip_max = LE_CLEAR; - test->str = VSB_new_auto(); - AN(test->str); - AZ(VSB_printf(test->str, "%s %s", - av[0], av[1])); - AZ(VSB_finish(test->str)); - - VTAILQ_INSERT_TAIL(&le->tests, test, list); - return; - } - - if (strcmp(av[1], "add")) - vtc_fatal(vl, "Unknown fail argument '%s'", av[1]); - - if (av[2] == NULL || av[3] == NULL) - vtc_fatal(vl, "Syntax error"); - - cmd_logexp_common(le, vl, LE_FAIL, av); -} - -/* aid vsl debugging */ -static void -cmd_logexp_abort(CMD_ARGS) -{ - - struct logexp *le; - - CAST_OBJ_NOTNULL(le, priv, LOGEXP_MAGIC); - - cmd_logexp_common(le, vl, LE_ABORT, av); -} - -static void -logexp_spec(struct logexp *le, const char *spec) -{ - CHECK_OBJ_NOTNULL(le, LOGEXP_MAGIC); - - logexp_delete_tests(le); - - parse_string(le->vl, le, spec); -} - -void -cmd_logexpect(CMD_ARGS) -{ - struct logexp *le, *le2; - int i; - - (void)priv; - - if (av == NULL) { - /* Reset and free */ - VTAILQ_FOREACH_SAFE(le, &logexps, list, le2) { - CHECK_OBJ_NOTNULL(le, LOGEXP_MAGIC); - VTAILQ_REMOVE(&logexps, le, list); - if (le->run) { - (void)pthread_cancel(le->tp); - logexp_wait(le); - } - logexp_delete(le); - } - return; - } - - AZ(strcmp(av[0], "logexpect")); - av++; - - VTC_CHECK_NAME(vl, av[0], "Logexpect", 'l'); - VTAILQ_FOREACH(le, &logexps, list) { - if (!strcmp(le->name, av[0])) - break; - } - if (le == NULL) { - if (strcmp(av[1], "-v") || av[2] == NULL) - vtc_fatal(vl, "new logexp lacks -v"); - le = logexp_new(av[0], av[2]); - av += 2; - } - av++; - - for (; *av != NULL; av++) { - if (vtc_error) - break; - if (!strcmp(*av, "-wait")) { - if (!le->run) - vtc_fatal(le->vl, "logexp not -started '%s'", - *av); - logexp_wait(le); - continue; - } - - /* - * We do an implicit -wait if people muck about with a - * running logexp. - */ - if (le->run) - logexp_wait(le); - AZ(le->run); - - if (!strcmp(*av, "-v")) { - if (av[1] == NULL || strcmp(av[1], le->vname)) - vtc_fatal(le->vl, "-v argument cannot change"); - av++; - continue; - } - if (!strcmp(*av, "-d")) { - if (av[1] == NULL) - vtc_fatal(le->vl, "Missing -d argument"); - le->d_arg = atoi(av[1]); - av++; - continue; - } - if (!strcmp(*av, "-g")) { - if (av[1] == NULL) - vtc_fatal(le->vl, "Missing -g argument"); - i = VSLQ_Name2Grouping(av[1], strlen(av[1])); - if (i < 0) - vtc_fatal(le->vl, "Unknown grouping '%s'", - av[1]); - le->g_arg = (enum VSL_grouping_e)i; - av++; - continue; - } - if (!strcmp(*av, "-q")) { - if (av[1] == NULL) - vtc_fatal(le->vl, "Missing -q argument"); - REPLACE(le->query, av[1]); - av++; - continue; - } - if (!strcmp(*av, "-m")) { - le->m_arg = !le->m_arg; - continue; - } - if (!strcmp(*av, "-err")) { - le->err_arg = !le->err_arg; - continue; - } - if (!strcmp(*av, "-start")) { - logexp_start(le); - continue; - } - if (!strcmp(*av, "-run")) { - logexp_start(le); - logexp_wait(le); - continue; - } - if (**av == '-') { - if (av[1] != NULL) { - if (VSL_Arg(le->vsl, av[0][1], av[1])) { - av++; - continue; - } - vtc_fatal(le->vl, "%s", VSL_Error(le->vsl)); - } - vtc_fatal(le->vl, "Unknown logexp argument: %s", *av); - } - logexp_spec(le, *av); - } -} - -#endif /* VTEST_WITH_VTC_LOGEXPECT */ diff --git a/bin/varnishtest/vtc_main.c b/bin/varnishtest/vtc_main.c deleted file mode 100644 index 83b6ef15d..000000000 --- a/bin/varnishtest/vtc_main.c +++ /dev/null @@ -1,978 +0,0 @@ -/*- - * Copyright (c) 2008-2011 Varnish Software AS - * All rights reserved. - * - * Author: Poul-Henning Kamp - * - * SPDX-License-Identifier: BSD-2-Clause - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#include "config.h" - -#include -#include -#include -#include - -#include -#include -#include -#include -#include -#include -#include - -#include "vtc.h" - -#include "vev.h" -#include "vfil.h" -#include "vnum.h" -#include "vrnd.h" -#include "vsa.h" -#include "vss.h" -#include "vsub.h" -#include "vtcp.h" -#include "vtim.h" -#include "vct.h" - -static const char *argv0; - -struct buf { - unsigned magic; -#define BUF_MAGIC 0x39d1258a - VTAILQ_ENTRY(buf) list; - char *buf; - struct vsb *diag; - size_t bufsiz; -}; - -static VTAILQ_HEAD(, buf) free_bufs = VTAILQ_HEAD_INITIALIZER(free_bufs); - -struct vtc_tst { - unsigned magic; -#define TST_MAGIC 0x618d8b88 - VTAILQ_ENTRY(vtc_tst) list; - const char *filename; - char *script; - unsigned ntodo; - unsigned nwait; -}; - -struct vtc_job { - unsigned magic; -#define JOB_MAGIC 0x1b5fc419 - struct vtc_tst *tst; - pid_t child; - struct vev *ev; - struct vev *evt; - struct buf *bp; - char *tmpdir; - double t0; - int killed; -}; - - -int iflg = 0; -vtim_dur vtc_maxdur = 60; -static unsigned vtc_bufsiz = 1024 * 1024; - -static VTAILQ_HEAD(, vtc_tst) tst_head = VTAILQ_HEAD_INITIALIZER(tst_head); -static struct vev_root *vb; -static int njob = 0; -static int npar = 1; /* Number of parallel tests */ -static int vtc_continue; /* Continue on error */ -static int vtc_verbosity = 1; /* Verbosity Level */ -static int vtc_good; -static int vtc_fail; -static int vtc_skip; -static char *tmppath; -static char *cwd = NULL; -char *vmod_path = NULL; -struct vsb *params_vsb = NULL; -int leave_temp; -static struct vsb *cbvsb; -static int bad_backend_fd; - -static int cleaner_fd = -1; -static pid_t cleaner_pid; -const char *default_listen_addr; - -static struct buf * -get_buf(void) -{ - struct buf *bp; - - bp = VTAILQ_FIRST(&free_bufs); - CHECK_OBJ_ORNULL(bp, BUF_MAGIC); - if (bp != NULL) { - VTAILQ_REMOVE(&free_bufs, bp, list); - VSB_clear(bp->diag); - } else { - ALLOC_OBJ(bp, BUF_MAGIC); - AN(bp); - bp->bufsiz = vtc_bufsiz; - bp->buf = mmap(NULL, bp->bufsiz, PROT_READ|PROT_WRITE, - MAP_ANON | MAP_SHARED, -1, 0); - assert(bp->buf != MAP_FAILED); - bp->diag = VSB_new_auto(); - AN(bp->diag); - } - memset(bp->buf, 0, bp->bufsiz); - return (bp); -} - -static void -rel_buf(struct buf **bp) -{ - CHECK_OBJ_NOTNULL(*bp, BUF_MAGIC); - - VTAILQ_INSERT_HEAD(&free_bufs, (*bp), list); - *bp = NULL; -} - -/********************************************************************** - * Parse a -D option argument into a name/val pair, and insert - * into extmacro list - */ - -static int -parse_D_opt(char *arg) -{ - char *p, *q; - - p = arg; - q = strchr(p, '='); - if (!q) - return (0); - *q++ = '\0'; - extmacro_def(p, NULL, "%s", q); - - return (1); -} - -/********************************************************************** - * Print usage - */ - -static void v_noreturn_ -usage(void) -{ - fprintf(stderr, "usage: %s [options] file ...\n", argv0); -#define FMT " %-28s # %s\n" - fprintf(stderr, FMT, "-b size", - "Set internal buffer size (default: 1M)"); - fprintf(stderr, FMT, "-C", "Use cleaner subprocess"); - fprintf(stderr, FMT, "-D name=val", "Define macro"); - fprintf(stderr, FMT, "-i", "Find varnish binaries in build tree"); - fprintf(stderr, FMT, "-j jobs", "Run this many tests in parallel"); - fprintf(stderr, FMT, "-k", "Continue on test failure"); - fprintf(stderr, FMT, "-L", "Always leave temporary vtc.*"); - fprintf(stderr, FMT, "-l", "Leave temporary vtc.* if test fails"); - fprintf(stderr, FMT, "-n iterations", "Run tests this many times"); - fprintf(stderr, FMT, "-p name=val", "Pass a varnishd parameter"); - fprintf(stderr, FMT, "-q", "Quiet mode: report only failures"); - fprintf(stderr, FMT, "-t duration", "Time tests out after this long"); - fprintf(stderr, FMT, "-v", "Verbose mode: always report test log"); - exit(1); -} - -/********************************************************************** - * When running many tests, cleaning the tmpdir with "rm -rf" becomes - * chore which limits our performance. - * When the number of tests are above 100, we spawn a child-process - * to do that for us. - */ - -static void -cleaner_do(const char *dirname) -{ - char buf[BUFSIZ]; - - AZ(memcmp(dirname, tmppath, strlen(tmppath))); - if (cleaner_pid > 0) { - bprintf(buf, "%s\n", dirname); - assert(write(cleaner_fd, buf, strlen(buf)) == strlen(buf)); - return; - } - bprintf(buf, "exec /bin/rm -rf %s\n", dirname); - AZ(system(buf)); -} - -static void -cleaner_setup(void) -{ - int p[2], st; - char buf[BUFSIZ]; - char *q; - pid_t pp; - - AZ(pipe(p)); - assert(p[0] > STDERR_FILENO); - assert(p[1] > STDERR_FILENO); - cleaner_pid = fork(); - assert(cleaner_pid >= 0); - if (cleaner_pid == 0) { - closefd(&p[1]); - (void)nice(1); /* Not important */ - setbuf(stdin, NULL); - AZ(dup2(p[0], STDIN_FILENO)); - while (fgets(buf, sizeof buf, stdin)) { - AZ(memcmp(buf, tmppath, strlen(tmppath))); - q = buf + strlen(buf); - assert(q > buf); - assert(q[-1] == '\n'); - q[-1] = '\0'; - - /* Dont expend a shell on running /bin/rm */ - pp = fork(); - assert(pp >= 0); - if (pp == 0) - exit(execlp( - "rm", "rm", "-rf", buf, (char*)0)); - assert(waitpid(pp, &st, 0) == pp); - AZ(st); - } - exit(0); - } - closefd(&p[0]); - cleaner_fd = p[1]; -} - -static void -cleaner_neuter(void) -{ - if (cleaner_pid > 0) - closefd(&cleaner_fd); -} - -static void -cleaner_finish(void) -{ - int st; - - if (cleaner_pid > 0) { - closefd(&cleaner_fd); - assert(waitpid(cleaner_pid, &st, 0) == cleaner_pid); - AZ(st); - } -} - -/********************************************************************** - * CallBack - */ - -static int -tst_cb(const struct vev *ve, int what) -{ - struct vtc_job *jp; - char buf[BUFSIZ]; - int ecode; - int i, stx; - pid_t px; - double t; - FILE *f; - char *p; - - CAST_OBJ_NOTNULL(jp, ve->priv, JOB_MAGIC); - CHECK_OBJ_NOTNULL(jp->tst, TST_MAGIC); - - // printf("CB %p %s %d\n", ve, jp->tst->filename, what); - if (what == 0) { - jp->killed = 1; - AZ(kill(-jp->child, SIGKILL)); /* XXX: Timeout */ - } else { - assert(what & (VEV__RD | VEV__HUP)); - } - - *buf = '\0'; - i = read(ve->fd, buf, sizeof buf); - if (i > 0) - VSB_bcat(jp->bp->diag, buf, i); - if (i == 0) { - - njob--; - px = wait4(jp->child, &stx, 0, NULL); - assert(px == jp->child); - t = VTIM_mono() - jp->t0; - AZ(close(ve->fd)); - - ecode = WTERMSIG(stx); - if (ecode == 0) - ecode = WEXITSTATUS(stx); - - AZ(VSB_finish(jp->bp->diag)); - - VSB_clear(cbvsb); - VSB_cat(cbvsb, jp->bp->buf); - p = strchr(jp->bp->buf, '\0'); - if (p > jp->bp->buf && p[-1] != '\n') - VSB_putc(cbvsb, '\n'); - VSB_quote_pfx(cbvsb, "* diag 0.0 ", - VSB_data(jp->bp->diag), -1, VSB_QUOTE_NONL); - AZ(VSB_finish(cbvsb)); - rel_buf(&jp->bp); - - if ((ecode > 1 && vtc_verbosity) || vtc_verbosity > 1) - printf("%s", VSB_data(cbvsb)); - - if (!ecode) - vtc_good++; - else if (ecode == 1) - vtc_skip++; - else - vtc_fail++; - - if (leave_temp == 0 || (leave_temp == 1 && ecode <= 1)) { - cleaner_do(jp->tmpdir); - } else { - bprintf(buf, "%s/LOG", jp->tmpdir); - f = fopen(buf, "w"); - AN(f); - (void)fprintf(f, "%s\n", VSB_data(cbvsb)); - AZ(fclose(f)); - } - free(jp->tmpdir); - - if (jp->killed) - printf("# top TEST %s TIMED OUT (kill -9)\n", - jp->tst->filename); - if (ecode > 1) { - printf("# top TEST %s FAILED (%.3f)", - jp->tst->filename, t); - if (WIFSIGNALED(stx)) - printf(" signal=%d\n", WTERMSIG(stx)); - else if (WIFEXITED(stx)) - printf(" exit=%d\n", WEXITSTATUS(stx)); - if (!vtc_continue) { - /* XXX kill -9 other jobs ? */ - exit(2); - } - } else if (vtc_verbosity) { - printf("# top TEST %s %s (%.3f)\n", - jp->tst->filename, - ecode ? "skipped" : "passed", t); - } - if (jp->evt != NULL) { - VEV_Stop(vb, jp->evt); - free(jp->evt); - } - jp->tst->nwait--; - if (jp->tst->nwait == 0) { - free(jp->tst->script); - FREE_OBJ(jp->tst); - } - FREE_OBJ(jp); - return (1); - } - return (0); -} - -/********************************************************************** - * Start Test - */ - -static void -start_test(void) -{ - struct vtc_tst *tp; - int p[2], retval; - struct vtc_job *jp; - char tmpdir[PATH_MAX]; - char default_n[PATH_MAX]; - - ALLOC_OBJ(jp, JOB_MAGIC); - AN(jp); - - jp->bp = get_buf(); - - bprintf(tmpdir, "%s/vtc.%d.%08x", tmppath, (int)getpid(), - (unsigned)random()); - AZ(mkdir(tmpdir, 0755)); - - bprintf(default_n, "%s/default_n", tmpdir); - - AZ(setenv("VARNISH_DEFAULT_N", default_n, 1)); - - tp = VTAILQ_FIRST(&tst_head); - CHECK_OBJ_NOTNULL(tp, TST_MAGIC); - AN(tp->ntodo); - tp->ntodo--; - VTAILQ_REMOVE(&tst_head, tp, list); - if (tp->ntodo > 0) - VTAILQ_INSERT_TAIL(&tst_head, tp, list); - - jp->tst = tp; - REPLACE(jp->tmpdir, tmpdir); - - AZ(pipe(p)); - assert(p[0] > STDERR_FILENO); - assert(p[1] > STDERR_FILENO); - jp->t0 = VTIM_mono(); - jp->child = fork(); - assert(jp->child >= 0); - if (jp->child == 0) { - cleaner_neuter(); // Too dangerous to have around - AZ(setpgid(getpid(), 0)); - VFIL_null_fd(STDIN_FILENO); - assert(dup2(p[1], STDOUT_FILENO) == STDOUT_FILENO); - assert(dup2(p[1], STDERR_FILENO) == STDERR_FILENO); - VSUB_closefrom(STDERR_FILENO + 1); - retval = exec_file(jp->tst->filename, jp->tst->script, - jp->tmpdir, jp->bp->buf, jp->bp->bufsiz); - exit(retval); - } - closefd(&p[1]); - - jp->ev = VEV_Alloc(); - AN(jp->ev); - jp->ev->fd_flags = VEV__RD | VEV__HUP | VEV__ERR; - jp->ev->fd = p[0]; - jp->ev->priv = jp; - jp->ev->callback = tst_cb; - AZ(VEV_Start(vb, jp->ev)); - - jp->evt = VEV_Alloc(); - AN(jp->evt); - jp->evt->fd = -1; - jp->evt->timeout = vtc_maxdur; - jp->evt->priv = jp; - jp->evt->callback = tst_cb; - AZ(VEV_Start(vb, jp->evt)); -} - -/********************************************************************** - * i-mode = "we're inside a src-tree" - * - * Find the abs path to top of source dir from Makefile, if that - * fails, fall back on "../../" - * - * Set PATH to all programs build directories - * Set vmod_path to all vmods build directories - * - */ - -static char * -top_dir(const char *makefile, const char *top_var) -{ - const char *b, *e; - char *var; - - AN(makefile); - AN(top_var); - assert(*top_var == '\n'); - - b = strstr(makefile, top_var); - top_var++; - - if (b == NULL) { - fprintf(stderr, "could not find '%s' in Makefile\n", top_var); - return (NULL); - } - - e = strchr(b + 1, '\n'); - if (e == NULL) { - fprintf(stderr, "No NL after '%s' in Makefile\n", top_var); - return (NULL); - } - - b = memchr(b, '/', e - b); - if (b == NULL) { - fprintf(stderr, "No '/' after '%s' in Makefile\n", top_var); - return (NULL); - } - var = strndup(b, e - b); - AN(var); - return (var); -} - -static void -build_path(const char *topdir, const char *subdir, - const char *pfx, const char *sfx, struct vsb *vsb) -{ - char buf[PATH_MAX]; - DIR *dir; - struct dirent *de; - struct stat st; - const char *topsep = "", *sep = ""; - - if (*subdir != '\0') - topsep = "/"; - bprintf(buf, "%s%s%s/", topdir, topsep, subdir); - dir = opendir(buf); - XXXAN(dir); - while (1) { - de = readdir(dir); - if (de == NULL) - break; - if (strncmp(de->d_name, pfx, strlen(pfx))) - continue; - bprintf(buf, "%s%s%s/%s", topdir, topsep, subdir, de->d_name); - if (!stat(buf, &st) && S_ISDIR(st.st_mode)) { - VSB_cat(vsb, sep); - VSB_cat(vsb, buf); - VSB_cat(vsb, sfx); - sep = ":"; - } - } - AZ(closedir(dir)); -} - -static void -i_mode(void) -{ - struct vsb *vsb; - char *p, *topbuild, *topsrc; - - /* - * This code has a rather intimate knowledge of auto* generated - * makefiles. - */ - - vsb = VSB_new_auto(); - AN(vsb); - - p = VFIL_readfile(NULL, "Makefile", NULL); - if (p == NULL) { - fprintf(stderr, "No Makefile to search for -i flag.\n"); - exit(2); - } - - topbuild = top_dir(p, "\nabs_top_builddir"); - topsrc = top_dir(p, "\nabs_top_srcdir"); - free(p); - if (topbuild == NULL || topsrc == NULL) { - free(topbuild); - free(topsrc); - exit(2); - } - extmacro_def("topbuild", NULL, "%s", topbuild); - extmacro_def("topsrc", NULL, "%s", topsrc); - - /* - * Build $PATH which can find all programs in the build tree - */ - VSB_clear(vsb); - VSB_cat(vsb, "PATH="); - build_path(topbuild, "bin", "varnish", "", vsb); -#ifdef WITH_CONTRIB - VSB_putc(vsb, ':'); - build_path(topsrc, "", "contrib", "", vsb); -#endif - VSB_printf(vsb, ":%s", getenv("PATH")); - AZ(VSB_finish(vsb)); - AZ(putenv(strdup(VSB_data(vsb)))); - - /* - * Build vmod_path which can find all VMODs in the build tree - */ - - VSB_clear(vsb); - build_path(topbuild, "vmod", ".libs", "", vsb); - AZ(VSB_finish(vsb)); - vmod_path = strdup(VSB_data(vsb)); - AN(vmod_path); - - free(topbuild); - free(topsrc); - VSB_destroy(&vsb); - - /* - * strict jemalloc checking - */ - AZ(putenv(strdup("MALLOC_CONF=abort:true,junk:true"))); -} - -/********************************************************************** - * Figure out what IP related magic - */ - -static void -ip_magic(void) -{ - const struct suckaddr *sa; - char abuf[VTCP_ADDRBUFSIZE]; - char pbuf[VTCP_PORTBUFSIZE]; - char *s; - - /* - * In FreeBSD jails localhost/127.0.0.1 becomes the jails IP# - * XXX: IPv6-only hosts would have similar issue, but it is not - * XXX: obvious how to cope. Ideally "127.0.0.1" would be - * XXX: "localhost", but that doesn't work out of the box. - * XXX: Things like "prefer_ipv6" parameter complicates things. - */ - sa = VSS_ResolveOne(NULL, "127.0.0.1", "0", 0, SOCK_STREAM, 0); - AN(sa); - bad_backend_fd = VTCP_bind(sa, NULL); - if (bad_backend_fd < 0) { - VSA_free(&sa); - sa = VSS_ResolveFirst(NULL, "localhost", "0", 0, SOCK_STREAM, 0); - AN(sa); - bad_backend_fd = VTCP_bind(sa, NULL); - } - assert(bad_backend_fd >= 0); - VTCP_myname(bad_backend_fd, abuf, sizeof abuf, pbuf, sizeof(pbuf)); - extmacro_def("localhost", NULL, "%s", abuf); - s = strdup(abuf); - AN(s); - -#if defined (__APPLE__) - /* - * In macOS a bound socket that is not listening will timeout - * instead of refusing the connection so close it and hope - * for the best. - */ - VTCP_close(&bad_backend_fd); -#endif - - /* Expose a backend that is forever down. */ - if (VSA_Get_Proto(sa) == AF_INET) - extmacro_def("bad_backend", NULL, "%s:%s", abuf, pbuf); - else - extmacro_def("bad_backend", NULL, "[%s]:%s", abuf, pbuf); - - /* our default bind/listen address */ - if (VSA_Get_Proto(sa) == AF_INET) - bprintf(abuf, "%s:0", s); - else - bprintf(abuf, "[%s]:0", s); - free(s); - - extmacro_def("listen_addr", NULL, "%s", abuf); - default_listen_addr = strdup(abuf); - AN(default_listen_addr); - VSA_free(&sa); - - /* - * We need an IP number which will not respond, ever, and that is a - * lot harder than it sounds. This IP# is from RFC5737 and a - * C-class broadcast at that. - * If tests involving ${bad_ip} fails and you run linux, you should - * check your /proc/sys/net/ipv4/ip_nonlocal_bind setting. - */ - - extmacro_def("bad_ip", NULL, "%s", "192.0.2.255"); -} - -/********************************************************************** - * Macros - */ - -static char * v_matchproto_(macro_f) -macro_func_date(int argc, char *const *argv, const char **err) -{ - double t; - char *s; - - assert(argc >= 2); - AN(argv); - AN(err); - - if (argc > 2) { - *err = "macro does not take arguments"; - return (NULL); - } - - t = VTIM_real(); - s = malloc(VTIM_FORMAT_SIZE); - AN(s); - VTIM_format(t, s); - return (s); -} - -static char * -macro_func_string_repeat(int argc, char *const *argv, const char **err) -{ - struct vsb vsb[1]; - const char *p; - char *res; - size_t l; - int i; - - if (argc != 4) { - *err = "repeat takes 2 arguments"; - return (NULL); - } - - p = argv[2]; - i = SF_Parse_Integer(&p, err); - - if (*err != NULL) - return (NULL); - - if (*p != '\0' || i < 0) { - *err = "invalid number of repetitions"; - return (NULL); - } - - l = (strlen(argv[3]) * i) + 1; - res = malloc(l); - AN(res); - AN(VSB_init(vsb, res, l)); - while (i > 0) { - AZ(VSB_cat(vsb, argv[3])); - i--; - } - AZ(VSB_finish(vsb)); - VSB_fini(vsb); - return (res); -} - -static char * -macro_func_string(int argc, char *const *argv, const char **err) -{ - - assert(argc >= 2); - AN(argv); - AN(err); - - if (argc == 2) { - *err = "missing action"; - return (NULL); - } - - if (!strcmp(argv[2], "repeat")) - return (macro_func_string_repeat(argc - 1, argv + 1, err)); - - *err = "unknown action"; - return (NULL); -} - -/********************************************************************** - * Main - */ - -static int -read_file(const char *fn, int ntest) -{ - struct vtc_tst *tp; - char *p, *q; - - p = VFIL_readfile(NULL, fn, NULL); - if (p == NULL) { - fprintf(stderr, "Cannot stat file \"%s\": %s\n", - fn, strerror(errno)); - return (2); - } - for (q = p ;q != NULL && *q != '\0'; q++) { - if (vct_islws(*q)) - continue; - if (*q != '#') - break; - q = strchr(q, '\n'); - if (q == NULL) - break; - } - - if (q == NULL || *q == '\0') { - fprintf(stderr, "File \"%s\" has no content.\n", fn); - free(p); - return (2); - } - - if ((strncmp(q, "varnishtest", 11) || !isspace(q[11])) && - (strncmp(q, "vtest", 5) || !isspace(q[5]))) { - fprintf(stderr, - "File \"%s\" doesn't start with" - " 'vtest' or 'varnishtest'\n", fn); - free(p); - vtc_skip++; - return (2); - } - ALLOC_OBJ(tp, TST_MAGIC); - AN(tp); - tp->filename = fn; - tp->script = p; - tp->ntodo = ntest; - tp->nwait = ntest; - VTAILQ_INSERT_TAIL(&tst_head, tp, list); - return (0); -} - -/********************************************************************** - * Main - */ - -int -main(int argc, char * const *argv) -{ - int ch, i; - int ntest = 1; /* Run tests this many times */ - int nstart = 0; - int use_cleaner = 0; - uintmax_t bufsiz; - const char *p; - char buf[PATH_MAX]; - - argv0 = strrchr(argv[0], '/'); - if (argv0 == NULL) - argv0 = argv[0]; - else - argv0++; - - if (getenv("TMPDIR") != NULL) - tmppath = strdup(getenv("TMPDIR")); - else - tmppath = strdup("/tmp"); - -#ifdef PACKAGE_VERSION - extmacro_def("pkg_version", NULL, PACKAGE_VERSION); -#endif -#ifdef PACKAGE_BRANCH - extmacro_def("pkg_branch", NULL, PACKAGE_BRANCH); -#endif - - cwd = getcwd(buf, sizeof buf); - extmacro_def("pwd", NULL, "%s", cwd); - - extmacro_def("date", macro_func_date, NULL); - extmacro_def("string", macro_func_string, NULL); - - vmod_path = NULL; - - params_vsb = VSB_new_auto(); - AN(params_vsb); - p = getenv("VTEST_DURATION"); - if (p == NULL) - p = getenv("VARNISHTEST_DURATION"); - if (p != NULL) - vtc_maxdur = atoi(p); - - VRND_SeedAll(); - cbvsb = VSB_new_auto(); - AN(cbvsb); - setbuf(stdout, NULL); - setbuf(stderr, NULL); - while ((ch = getopt(argc, argv, "b:CD:hij:kLln:p:qt:v")) != -1) { - switch (ch) { - case 'b': - if (VNUM_2bytes(optarg, &bufsiz, 0)) { - fprintf(stderr, "Cannot parse b opt '%s'\n", - optarg); - exit(2); - } - if (bufsiz > UINT_MAX) { - fprintf(stderr, "Invalid b opt '%s'\n", - optarg); - exit(2); - } - vtc_bufsiz = (unsigned)bufsiz; - break; - case 'C': - use_cleaner = !use_cleaner; - break; - case 'D': - if (!parse_D_opt(optarg)) { - fprintf(stderr, "Cannot parse D opt '%s'\n", - optarg); - exit(2); - } - break; - case 'i': - iflg = 1; - break; - case 'j': - npar = strtoul(optarg, NULL, 0); - break; - case 'L': - leave_temp = 2; - break; - case 'l': - leave_temp = 1; - break; - case 'k': - vtc_continue = !vtc_continue; - break; - case 'n': - ntest = strtoul(optarg, NULL, 0); - break; - case 'p': - VSB_cat(params_vsb, " -p "); - VSB_quote(params_vsb, optarg, -1, 0); - break; - case 'q': - if (vtc_verbosity > 0) - vtc_verbosity--; - break; - case 't': - vtc_maxdur = strtoul(optarg, NULL, 0); - break; - case 'v': - if (vtc_verbosity < 2) - vtc_verbosity++; - break; - default: - usage(); - } - } - argc -= optind; - argv += optind; - - if (argc < 1) - usage(); - - for (; argc > 0; argc--, argv++) { - if (!read_file(*argv, ntest)) - continue; - if (!vtc_continue) - exit(2); - } - - AZ(VSB_finish(params_vsb)); - - ip_magic(); - - if (iflg) - i_mode(); - - vb = VEV_New(); - - if (use_cleaner) - cleaner_setup(); - - i = 0; - while (!VTAILQ_EMPTY(&tst_head) || i) { - if (!VTAILQ_EMPTY(&tst_head) && njob < npar) { - start_test(); - njob++; - /* Stagger ramp-up */ - if (nstart++ < npar) - (void)usleep(random() % 100000L); - i = 1; - continue; - } - i = VEV_Once(vb); - } - cleaner_finish(); - (void)close(bad_backend_fd); - if (vtc_continue) - fprintf(stderr, - "%d tests failed, %d tests skipped, %d tests passed\n", - vtc_fail, vtc_skip, vtc_good); - if (vtc_fail) - return (1); - if (vtc_skip && !vtc_good) - return (77); - return (0); -} diff --git a/bin/varnishtest/vtc_misc.c b/bin/varnishtest/vtc_misc.c deleted file mode 100644 index 8167c23ce..000000000 --- a/bin/varnishtest/vtc_misc.c +++ /dev/null @@ -1,663 +0,0 @@ -/*- - * Copyright (c) 2008-2011 Varnish Software AS - * All rights reserved. - * - * Author: Poul-Henning Kamp - * - * SPDX-License-Identifier: BSD-2-Clause - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#include "config.h" - -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -#ifdef HAVE_SYS_PERSONALITY_H -# include -#endif - -#include "vtc.h" - -#include "vfil.h" -#include "vnum.h" -#include "vre.h" -#include "vtcp.h" -#include "vsa.h" -#include "vss.h" -#include "vtim.h" -#include "vus.h" - -/* SECTION: vtest vtest - * - * This should be the first command in your vtc as it will identify the test - * case with a short yet descriptive sentence. It takes exactly one argument, a - * string, eg:: - * - * vtest "Check that vtest is actually a valid command" - * - * It will also print that string in the log. - */ - -void v_matchproto_(cmd_f) -cmd_vtest(CMD_ARGS) -{ - - (void)priv; - (void)vl; - - if (av == NULL) - return; - AZ(strcmp(av[0], "vtest")); - - vtc_log(vl, 1, "VTEST %s", av[1]); - AZ(av[2]); -} - -/* SECTION: varnishtest varnishtest - * - * Alternate name for 'vtest', see above. - * - */ - -void v_matchproto_(cmd_f) -cmd_varnishtest(CMD_ARGS) -{ - - (void)priv; - (void)vl; - - if (av == NULL) - return; - AZ(strcmp(av[0], "varnishtest")); - - vtc_log(vl, 1, "VTEST %s", av[1]); - AZ(av[2]); -} - -/* SECTION: shell shell - * - * NOTE: This command is available everywhere commands are given. - * - * Pass the string given as argument to a shell. If you have multiple - * commands to run, you can use curly brackets to describe a multi-lines - * script, eg:: - * - * shell { - * echo begin - * cat /etc/fstab - * echo end - * } - * - * By default a zero exit code is expected, otherwise the vtc will fail. - * - * Notice that the commandstring is prefixed with "exec 2>&1;" to combine - * stderr and stdout back to the test process. - * - * Optional arguments: - * - * \-err - * Expect non-zero exit code. - * - * \-exit N - * Expect exit code N instead of zero. - * - * \-expect STRING - * Expect string to be found in stdout+err. - * - * \-match REGEXP - * Expect regexp to match the stdout+err output. - */ -/* SECTION: client-server.spec.shell - * - * shell - * Same as for the top-level shell. - */ - -static void -cmd_shell_engine(struct vtclog *vl, int ok, const char *cmd, - const char *expect, const char *re) -{ - struct vsb *vsb, re_vsb[1]; - FILE *fp; - vre_t *vre = NULL; - int r, c; - int err, erroff; - char errbuf[VRE_ERROR_LEN]; - - AN(vl); - AN(cmd); - vsb = VSB_new_auto(); - AN(vsb); - if (re != NULL) { - vre = VRE_compile(re, 0, &err, &erroff, 1); - if (vre == NULL) { - AN(VSB_init(re_vsb, errbuf, sizeof errbuf)); - AZ(VRE_error(re_vsb, err)); - AZ(VSB_finish(re_vsb)); - VSB_fini(re_vsb); - vtc_fatal(vl, - "shell_match invalid regexp (\"%s\" at %d)", - errbuf, erroff); - } - } - VSB_printf(vsb, "set -e ;"); - VSB_printf(vsb, "exec 2>&1 ; %s", cmd); - AZ(VSB_finish(vsb)); - vtc_dump(vl, 4, "shell_cmd", VSB_data(vsb), -1); - fp = popen(VSB_data(vsb), "r"); - if (fp == NULL) - vtc_fatal(vl, "popen fails: %s", strerror(errno)); - VSB_clear(vsb); - do { - c = getc(fp); - if (c != EOF) - VSB_putc(vsb, c); - } while (c != EOF); - r = pclose(fp); - AZ(VSB_finish(vsb)); - vtc_dump(vl, 4, "shell_out", VSB_data(vsb), VSB_len(vsb)); - vtc_log(vl, 4, "shell_status = 0x%04x", WEXITSTATUS(r)); - if (WIFSIGNALED(r)) - vtc_log(vl, 4, "shell_signal = %d", WTERMSIG(r)); - - if (ok < 0 && !WEXITSTATUS(r) && !WIFSIGNALED(r)) - vtc_fatal(vl, "shell did not fail as expected"); - else if (ok >= 0 && WEXITSTATUS(r) != ok) - vtc_fatal(vl, "shell_exit not as expected: " - "got 0x%04x wanted 0x%04x", WEXITSTATUS(r), ok); - - if (expect != NULL) { - if (strstr(VSB_data(vsb), expect) == NULL) - vtc_fatal(vl, - "shell_expect not found: (\"%s\")", expect); - else - vtc_log(vl, 4, "shell_expect found"); - } else if (vre != NULL) { - if (VRE_match(vre, VSB_data(vsb), VSB_len(vsb), 0, NULL) < 1) - vtc_fatal(vl, "shell_match failed: (\"%s\")", re); - else - vtc_log(vl, 4, "shell_match succeeded"); - VRE_free(&vre); - } - VSB_destroy(&vsb); -} - - -void -cmd_shell(CMD_ARGS) -{ - const char *expect = NULL; - const char *re = NULL; - int n; - int ok = 0; - - (void)priv; - - if (av == NULL) - return; - for (n = 1; av[n] != NULL; n++) { - if (!strcmp(av[n], "-err")) { - ok = -1; - } else if (!strcmp(av[n], "-exit")) { - n += 1; - ok = atoi(av[n]); - } else if (!strcmp(av[n], "-expect")) { - if (re != NULL) - vtc_fatal(vl, - "Cannot use -expect with -match"); - n += 1; - expect = av[n]; - } else if (!strcmp(av[n], "-match")) { - if (expect != NULL) - vtc_fatal(vl, - "Cannot use -match with -expect"); - n += 1; - re = av[n]; - } else { - break; - } - } - AN(av[n]); - cmd_shell_engine(vl, ok, av[n], expect, re); -} - -/* SECTION: filewrite filewrite - * - * Write strings to file - * - * filewrite [-a] /somefile "Hello" " " "World\n" - * - * The -a flag opens the file in append mode. - * - */ - -void v_matchproto_(cmd_f) -cmd_filewrite(CMD_ARGS) -{ - FILE *fo; - int n; - const char *mode = "w"; - - (void)priv; - - if (av == NULL) - return; - if (av[1] != NULL && !strcmp(av[1], "-a")) { - av++; - mode = "a"; - } - if (av[1] == NULL) - vtc_fatal(vl, "Need filename"); - fo = fopen(av[1], mode); - if (fo == NULL) - vtc_fatal(vl, "Cannot open %s: %s", av[1], strerror(errno)); - for (n = 2; av[n] != NULL; n++) - (void)fputs(av[n], fo); - AZ(fclose(fo)); -} - -/* SECTION: setenv setenv - * - * Set or change an environment variable:: - * - * setenv FOO "bar baz" - * - * The above will set the environment variable $FOO to the value - * provided. There is also an ``-ifunset`` argument which will only - * set the value if the environment variable does not already - * exist:: - * - * setenv -ifunset FOO quux - */ - -void v_matchproto_(cmd_f) -cmd_setenv(CMD_ARGS) -{ - int r; - int force; - - (void)priv; - - if (av == NULL) - return; - AN(av[1]); - AN(av[2]); - - force = 1; - if (strcmp("-ifunset", av[1]) == 0) { - force = 0; - av++; - AN(av[2]); - } - if (av[3] != NULL) - vtc_fatal(vl, "CMD setenv: Unexpected argument '%s'", av[3]); - r = setenv(av[1], av[2], force); - if (r != 0) - vtc_log(vl, 0, "CMD setenv %s=\"%s\" failed: %s", - av[1], av[2], strerror(errno)); -} - -/* SECTION: delay delay - * - * NOTE: This command is available everywhere commands are given. - * - * Sleep for the number of seconds specified in the argument. The number - * can include a fractional part, e.g. 1.5. - */ -void -cmd_delay(CMD_ARGS) -{ - double f; - - (void)priv; - if (av == NULL) - return; - AN(av[1]); - AZ(av[2]); - f = VNUM(av[1]); - if (isnan(f)) - vtc_fatal(vl, "Syntax error in number (%s)", av[1]); - vtc_log(vl, 3, "delaying %g second(s)", f); - VTIM_sleep(f); -} - -/* SECTION: include include - * - * Executes a vtc fragment:: - * - * include FILE [...] - * - * Open a file and execute it as a VTC fragment. This command is available - * everywhere commands are given. - * - */ -void -cmd_include(CMD_ARGS) -{ - char *spec; - unsigned i; - - if (av == NULL) - return; - - if (av[1] == NULL) - vtc_fatal(vl, "CMD include: At least 1 argument required"); - - for (i = 1; av[i] != NULL; i++) { - spec = VFIL_readfile(NULL, av[i], NULL); - if (spec == NULL) - vtc_fatal(vl, "CMD include: Unable to read file '%s' " - "(%s)", av[i], strerror(errno)); - vtc_log(vl, 2, "Begin include '%s'", av[i]); - parse_string(vl, priv, spec); - vtc_log(vl, 2, "End include '%s'", av[i]); - free(spec); - } -} - -/********************************************************************** - * Most test-cases use only numeric IP#'s but a few requires non-demented - * DNS services. This is a basic sanity check for those. - */ - -static int -dns_works(void) -{ - const struct suckaddr *sa; - char abuf[VTCP_ADDRBUFSIZE]; - char pbuf[VTCP_PORTBUFSIZE]; - - sa = VSS_ResolveOne(NULL, "dns-canary.varnish-cache.org", NULL, - AF_INET, SOCK_STREAM, 0); - if (sa == NULL) - return (0); - VTCP_name(sa, abuf, sizeof abuf, pbuf, sizeof pbuf); - VSA_free(&sa); - if (strcmp(abuf, "192.0.2.255")) - return (0); - - sa = VSS_ResolveOne(NULL, "dns-canary.varnish-cache.org", NULL, - AF_INET6, SOCK_STREAM, 0); - if (sa == NULL) - return (1); /* the canary is ipv4 only */ - VSA_free(&sa); - return (0); -} - -/********************************************************************** - * Test if IPv4/IPv6 works - */ - -static int -ipvx_works(const char *target) -{ - const struct suckaddr *sa; - int fd; - - sa = VSS_ResolveOne(NULL, target, "0", 0, SOCK_STREAM, 0); - if (sa == NULL) - return (0); - fd = VTCP_bind(sa, NULL); - VSA_free(&sa); - if (fd >= 0) { - VTCP_close(&fd); - return (1); - } - return (0); -} - -/**********************************************************************/ - -static int -addr_no_randomize_works(void) -{ - int r = 0; - -#ifdef HAVE_SYS_PERSONALITY_H - r = personality(0xffffffff); - r = personality(r | ADDR_NO_RANDOMIZE); -#endif - return (r >= 0); -} - -/**********************************************************************/ - -static int -uds_socket(void *priv, const struct sockaddr_un *uds) -{ - - return (VUS_bind(uds, priv)); -} -static int -abstract_uds_works(void) -{ - const char *err; - int fd; - - fd = VUS_resolver("@vtc.feature.abstract_uds", uds_socket, NULL, &err); - if (fd < 0) - return (0); - AZ(close(fd)); - return (1); -} - -/* SECTION: feature feature - * - * Test that the required feature(s) for a test are available, and skip - * the test otherwise; or change the interpretation of the test, as - * documented below. feature takes any number of arguments from this list: - * - * 64bit - * The environment is 64 bits - * ipv4 - * 127.0.0.1 works - * ipv6 - * [::1] works - * dns - * DNS lookups are working - * topbuild - * The test has been started with '-i' - * root - * The test has been invoked by the root user - * user_varnish - * The varnish user is present - * user_vcache - * The vcache user is present - * group_varnish - * The varnish group is present - * cmd - * A command line that should execute with a zero exit status - * ignore_unknown_macro - * Do not fail the test if a string of the form ${...} is not - * recognized as a macro. - * persistent_storage - * Varnish was built with the deprecated persistent storage. - * coverage - * Varnish was built with code coverage enabled. - * asan - * Varnish was built with the address sanitizer. - * msan - * Varnish was built with the memory sanitizer. - * tsan - * Varnish was built with the thread sanitizer. - * ubsan - * Varnish was built with the undefined behavior sanitizer. - * sanitizer - * Varnish was built with a sanitizer. - * workspace_emulator - * Varnish was built with its workspace emulator. - * abstract_uds - * Creation of an abstract unix domain socket succeeded. - * disable_aslr - * ASLR can be disabled. - * - * A feature name can be prefixed with an exclamation mark (!) to skip a - * test if the feature is present. - * - * Be careful with ignore_unknown_macro, because it may cause a test with a - * misspelled macro to fail silently. You should only need it if you must - * run a test with strings of the form "${...}". - */ - -#if ENABLE_COVERAGE -static const unsigned coverage = 1; -#else -static const unsigned coverage = 0; -#endif - -#if ENABLE_ASAN -static const unsigned asan = 1; -#else -static const unsigned asan = 0; -#endif - -#if ENABLE_MSAN -static const unsigned msan = 1; -#else -static const unsigned msan = 0; -#endif - -#if ENABLE_TSAN -static const unsigned tsan = 1; -#else -static const unsigned tsan = 0; -#endif - -#if ENABLE_UBSAN -static const unsigned ubsan = 1; -#else -static const unsigned ubsan = 0; -#endif - -#if ENABLE_SANITIZER -static const unsigned sanitizer = 1; -#else -static const unsigned sanitizer = 0; -#endif - -#if ENABLE_WORKSPACE_EMULATOR -static const unsigned workspace_emulator = 1; -#else -static const unsigned workspace_emulator = 0; -#endif - -#if WITH_PERSISTENT_STORAGE -static const unsigned with_persistent_storage = 1; -#else -static const unsigned with_persistent_storage = 0; -#endif - -void v_matchproto_(cmd_f) -cmd_feature(CMD_ARGS) -{ - const char *feat; - int r, good, skip, neg; - - (void)priv; - - if (av == NULL) - return; - -#define FEATURE(nm, tst) \ - do { \ - if (!strcmp(feat, nm)) { \ - good = 1; \ - if (tst) { \ - skip = neg; \ - } else { \ - skip = !neg; \ - } \ - } \ - } while (0) - - skip = 0; - - for (av++; *av != NULL; av++) { - good = 0; - neg = 0; - feat = *av; - - if (feat[0] == '!') { - neg = 1; - feat++; - } - - FEATURE("ipv4", ipvx_works("127.0.0.1")); - FEATURE("ipv6", ipvx_works("[::1]")); - FEATURE("64bit", sizeof(void*) == 8); - FEATURE("disable_aslr", addr_no_randomize_works()); - FEATURE("dns", dns_works()); - FEATURE("topbuild", iflg); - FEATURE("root", !geteuid()); - FEATURE("user_varnish", getpwnam("varnish") != NULL); - FEATURE("user_vcache", getpwnam("vcache") != NULL); - FEATURE("group_varnish", getgrnam("varnish") != NULL); - FEATURE("persistent_storage", with_persistent_storage); - FEATURE("coverage", coverage); - FEATURE("asan", asan); - FEATURE("msan", msan); - FEATURE("tsan", tsan); - FEATURE("ubsan", ubsan); - FEATURE("sanitizer", sanitizer); - FEATURE("workspace_emulator", workspace_emulator); - FEATURE("abstract_uds", abstract_uds_works()); - - if (!strcmp(feat, "cmd")) { - good = 1; - skip = neg; - av++; - if (*av == NULL) - vtc_fatal(vl, "Missing the command-line"); - r = system(*av); - if (WEXITSTATUS(r) != 0) - skip = !neg; - } else if (!strcmp(feat, "ignore_unknown_macro")) { - ign_unknown_macro = 1; - good = 1; - } - if (!good) - vtc_fatal(vl, "FAIL test, unknown feature: %s", feat); - - if (!skip) - continue; - - vtc_stop = 2; - if (neg) - vtc_log(vl, 1, - "SKIPPING test, conflicting feature: %s", feat); - else - vtc_log(vl, 1, - "SKIPPING test, lacking feature: %s", feat); - return; - } -} diff --git a/bin/varnishtest/vtc_process.c b/bin/varnishtest/vtc_process.c deleted file mode 100644 index 50dc1fea7..000000000 --- a/bin/varnishtest/vtc_process.c +++ /dev/null @@ -1,1233 +0,0 @@ -/*- - * Copyright (c) 2015 Varnish Software AS - * All rights reserved. - * - * Author: Dridi Boukelmoune - * - * SPDX-License-Identifier: BSD-2-Clause - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - * XXX: - * -ignore-stderr (otherwise output to stderr is fail) - */ - -#include "config.h" - -#include // Linux: struct winsize - -#include -#include -#include -#include -#include -#include -#include -#ifdef __sun -# include -#endif -#include -#include - -#include "vtc.h" - -#include "vre.h" -#include "vev.h" -#include "vlu.h" -#include "vsb.h" -#include "vsub.h" -#include "vtim.h" - -#include "teken.h" - -struct process { - unsigned magic; -#define PROCESS_MAGIC 0x1617b43e - char *name; - struct vtclog *vl; - VTAILQ_ENTRY(process) list; - - char *spec; - char *dir; - char *out; - char *err; - int fd_term; - int fd_stderr; - int f_stdout; - int f_stderr; - struct vlu *vlu_stdout; - struct vlu *vlu_stderr; - int log; - pid_t pid; - int expect_exit; - int expect_signal; - int allow_core; - - uintmax_t stdout_bytes; - uintmax_t stderr_bytes; - - pthread_mutex_t mtx; - pthread_t tp; - unsigned hasthread; - - int nlin; - int ncol; - int ansi_response; - char **vram; - teken_t tek[1]; -}; - -static VTAILQ_HEAD(, process) processes = - VTAILQ_HEAD_INITIALIZER(processes); - -static void term_resize(struct process *pp, int lin, int col); - -/********************************************************************** - * Terminal emulation - */ - -static void -term_cursor(void *priv, const teken_pos_t *pos) -{ - (void)priv; - (void)pos; -} - -static void -term_putchar(void *priv, const teken_pos_t *pos, teken_char_t ch, - const teken_attr_t *at) -{ - struct process *pp; - - CAST_OBJ_NOTNULL(pp, priv, PROCESS_MAGIC); - (void)at; - if (ch > 126 || ch < 32) - ch = '?'; - assert(pos->tp_row < pp->nlin); - assert(pos->tp_col < pp->ncol); - pp->vram[pos->tp_row][pos->tp_col] = ch; -} - -static void -term_fill(void *priv, const teken_rect_t *r, teken_char_t c, - const teken_attr_t *a) -{ - teken_pos_t p; - - /* Braindead implementation of fill() - just call putchar(). */ - for (p.tp_row = r->tr_begin.tp_row; - p.tp_row < r->tr_end.tp_row; p.tp_row++) - for (p.tp_col = r->tr_begin.tp_col; - p.tp_col < r->tr_end.tp_col; p.tp_col++) - term_putchar(priv, &p, c, a); -} - -static void -term_copy(void *priv, const teken_rect_t *r, const teken_pos_t *p) -{ - struct process *pp; - int nrow, ncol, y; /* Has to be signed - >= 0 comparison */ - - /* - * Copying is a little tricky. We must make sure we do it in - * correct order, to make sure we don't overwrite our own data. - */ - CAST_OBJ_NOTNULL(pp, priv, PROCESS_MAGIC); - - nrow = r->tr_end.tp_row - r->tr_begin.tp_row; - ncol = r->tr_end.tp_col - r->tr_begin.tp_col; - - if (p->tp_row < r->tr_begin.tp_row) { - /* Copy from top to bottom. */ - for (y = 0; y < nrow; y++) - memmove(&pp->vram[p->tp_row + y][p->tp_col], - &pp->vram[r->tr_begin.tp_row + y][r->tr_begin.tp_col], ncol); - } else { - /* Copy from bottom to top. */ - for (y = nrow - 1; y >= 0; y--) - memmove(&pp->vram[p->tp_row + y][p->tp_col], - &pp->vram[r->tr_begin.tp_row + y][r->tr_begin.tp_col], ncol); - } -} - -static void -term_respond(void *priv, const void *p, size_t l) -{ - struct process *pp; - int r; - - CAST_OBJ_NOTNULL(pp, priv, PROCESS_MAGIC); - - vtc_dump(pp->vl, 4, "term_response", p, l); - if (pp->ansi_response) { - r = write(pp->fd_term, p, l); - if (r != l) - vtc_fatal(pp->vl, "Could not write to process: %s", - strerror(errno)); - } -} - -static void -term_param(void *priv, int p, unsigned int v) -{ - struct process *pp; - - CAST_OBJ_NOTNULL(pp, priv, PROCESS_MAGIC); - if (p == TP_132COLS && v) - term_resize(pp, pp->nlin, 132); - if (p == TP_132COLS && !v) - term_resize(pp, pp->nlin, 80); -} - -static const teken_funcs_t process_teken_func = { - .tf_cursor = term_cursor, - .tf_putchar = term_putchar, - .tf_fill = term_fill, - .tf_copy = term_copy, - .tf_respond = term_respond, - .tf_param = term_param, -}; - -static void -term_screen_dump(const struct process *pp) -{ - int i; - const teken_pos_t *pos; - - for (i = 0; i < pp->nlin; i++) - vtc_dump(pp->vl, 3, "screen", pp->vram[i], pp->ncol); - pos = teken_get_cursor(pp->tek); - vtc_log(pp->vl, 3, "Cursor at line %d column %d", - pos->tp_row + 1, pos->tp_col + 1); -} - -static void -term_resize(struct process *pp, int lin, int col) -{ - teken_pos_t pos; - char **vram; - int i, j; - - vram = calloc(lin, sizeof *pp->vram); - AN(vram); - for (i = 0; i < lin; i++) { - vram[i] = calloc(col + 1L, 1); - AN(vram[i]); - memset(vram[i], ' ', col); - vram[i][col] = '\0'; - } - if (pp->vram != NULL) { - for (i = 0; i < lin; i++) { - if (i >= pp->nlin) - break; - j = col; - if (j > pp->ncol) - j = pp->ncol; - memcpy(vram[i], pp->vram[i], j); - } - for (i = 0; i < pp->nlin; i++) - free(pp->vram[i]); - free(pp->vram); - } - pp->vram = vram; - pp->nlin = lin; - pp->ncol = col; - - pos.tp_row = lin; - pos.tp_col = col; - teken_set_winsize(pp->tek, &pos); -} - -static int -term_find_textline(const struct process *pp, int *x, int y, const char *pat) -{ - const char *t; - int l; - - if (*x == 0) { - t = strstr(pp->vram[y], pat); - if (t != NULL) { - *x = 1 + (t - pp->vram[y]); - return (1); - } - } else if (*x <= pp->ncol) { - t = pp->vram[y] + *x - 1; - l = strlen(pat); - assert((*x - 1) + (l - 1) < pp->ncol); - if (!memcmp(t, pat, l)) - return (1); - } - return (0); -} - -static int -term_find_text(const struct process *pp, int *x, int *y, const char *pat) -{ - int yy; - - if (*y == 0) { - for (yy = 0; yy < pp->nlin; yy++) { - if (term_find_textline(pp, x, yy, pat)) { - *y = yy + 1; - return (1); - } - } - } else if (*y <= pp->nlin) { - if (term_find_textline(pp, x, *y - 1, pat)) - return (1); - } - return (0); -} - -static void -term_expect_text(struct process *pp, - const char *lin, const char *col, const char *pat) -{ - int x, y, l, d = 10000; - char *t; - - y = strtoul(lin, NULL, 0); - if (y < 0 || y > pp->nlin) - vtc_fatal(pp->vl, "YYY %d nlin %d", y, pp->nlin); - x = strtoul(col, NULL, 0); - for(l = 0; l <= 10 && x > pp->ncol; l++) // wait for screen change - VTIM_sleep(0.1); - if (x < 0 || x > pp->ncol) - vtc_fatal(pp->vl, "XXX %d ncol %d", x, pp->ncol); - l = strlen(pat); - if (x + l - 1 > pp->ncol) - vtc_fatal(pp->vl, "XXX %d ncol %d", x + l - 1, pp->ncol); - PTOK(pthread_mutex_lock(&pp->mtx)); - while (!term_find_text(pp, &x, &y, pat)) { - if (x != 0 && y != 0) { - t = pp->vram[y - 1] + x - 1; - vtc_log(pp->vl, 4, - "text at %d,%d: '%.*s'", y, x, l, t); - } - PTOK(pthread_mutex_unlock(&pp->mtx)); - usleep(d); - PTOK(pthread_mutex_lock(&pp->mtx)); - if (d < 3000000) - d += d; - } - PTOK(pthread_mutex_unlock(&pp->mtx)); - vtc_log(pp->vl, 4, "found expected text at %d,%d: '%s'", y, x, pat); -} - -static void -term_expect_cursor(const struct process *pp, const char *lin, const char *col) -{ - int x, y, l; - const teken_pos_t *pos; - - pos = teken_get_cursor(pp->tek); - y = strtoul(lin, NULL, 0); - if (y < 0 || y > pp->nlin) - vtc_fatal(pp->vl, "YYY %d nlin %d", y, pp->nlin); - x = strtoul(col, NULL, 0); - for(l = 0; l < 10 && x > pp->ncol; l++) // wait for screen change - VTIM_sleep(0.1); - if (x < 0 || x > pp->ncol) - vtc_fatal(pp->vl, "XXX %d ncol %d", x, pp->ncol); - if (y != 0 && (y-1) != pos->tp_row) - vtc_fatal(pp->vl, "Cursor on line %d (expected %d)", - pos->tp_row + 1, y); - if (x != 0 && (x-1) != pos->tp_col) - vtc_fatal(pp->vl, "Cursor in column %d (expected %d)", - pos->tp_col + 1, y); -} - -static void -term_match_text(struct process *pp, - const char *lin, const char *col, const char *re) -{ - int i, l, err, erroff; - struct vsb *vsb, re_vsb[1]; - size_t len; - ssize_t x, y; - vre_t *vre; - char errbuf[VRE_ERROR_LEN]; - - vsb = VSB_new_auto(); - AN(vsb); - - y = strtoul(lin, NULL, 0); - if (y < 0 || y > pp->nlin) - vtc_fatal(pp->vl, "YYY %zd nlin %d", y, pp->nlin); - x = strtoul(col, NULL, 0); - for(l = 0; l < 10 && x > pp->ncol; l++) // wait for screen change - VTIM_sleep(0.1); - if (x < 0 || x > pp->ncol) - vtc_fatal(pp->vl, "XXX %zd ncol %d", x, pp->ncol); - - if (x) - x--; - - if (y) - y--; - - vre = VRE_compile(re, 0, &err, &erroff, 1); - if (vre == NULL) { - AN(VSB_init(re_vsb, errbuf, sizeof errbuf)); - AZ(VRE_error(re_vsb, err)); - AZ(VSB_finish(re_vsb)); - VSB_fini(re_vsb); - vtc_fatal(pp->vl, "invalid regexp \"%s\" at %d (%s)", - re, erroff, errbuf); - } - - PTOK(pthread_mutex_lock(&pp->mtx)); - - len = (pp->nlin - y) * (pp->ncol - x); - for (i = y; i < pp->nlin; i++) { - VSB_bcat(vsb, &pp->vram[i][x], pp->ncol - x); - VSB_putc(vsb, '\n'); - } - - AZ(VSB_finish(vsb)); - - if (VRE_match(vre, VSB_data(vsb), len, 0, NULL) < 1) - vtc_fatal(pp->vl, "match failed: (\"%s\")", re); - else - vtc_log(pp->vl, 4, "match succeeded"); - - PTOK(pthread_mutex_unlock(&pp->mtx)); - VSB_destroy(&vsb); - VRE_free(&vre); -} - -/********************************************************************** - * Allocate and initialize a process - */ - -#define PROCESS_EXPAND(field, format, ...) \ - do { \ - vsb = macro_expandf(p->vl, format, __VA_ARGS__); \ - AN(vsb); \ - p->field = strdup(VSB_data(vsb)); \ - AN(p->field); \ - VSB_destroy(&vsb); \ - } while (0) - -static void -process_coverage(struct process *p) -{ - const teken_attr_t *a; - teken_pos_t pos; - int fg, bg; - - // Code-Coverage of Teken - - (void)teken_get_sequence(p->tek, TKEY_UP); - (void)teken_get_sequence(p->tek, TKEY_F1); - (void)teken_256to8(0); - (void)teken_256to16(0); - a = teken_get_defattr(p->tek); - teken_set_defattr(p->tek, a); - a = teken_get_curattr(p->tek); - teken_set_curattr(p->tek, a); - (void)teken_get_winsize(p->tek); - pos.tp_row = 0; - pos.tp_col = 8; - teken_set_cursor(p->tek, &pos); - teken_get_defattr_cons25(p->tek, &fg, &bg); -} - -static struct process * -process_new(const char *name) -{ - struct process *p; - struct vsb *vsb; - char buf[1024]; - - ALLOC_OBJ(p, PROCESS_MAGIC); - AN(p); - REPLACE(p->name, name); - PTOK(pthread_mutex_init(&p->mtx, NULL)); - - p->vl = vtc_logopen("%s", name); - AN(p->vl); - - PROCESS_EXPAND(dir, "${tmpdir}/%s", name); - PROCESS_EXPAND(out, "${tmpdir}/%s/term", name); - PROCESS_EXPAND(err, "${tmpdir}/%s/stderr", name); - - bprintf(buf, "rm -rf %s ; mkdir -p %s ; touch %s %s", - p->dir, p->dir, p->out, p->err); - AZ(system(buf)); - - p->fd_term = -1; - - VTAILQ_INSERT_TAIL(&processes, p, list); - teken_init(p->tek, &process_teken_func, p); - term_resize(p, 24, 80); - process_coverage(p); - return (p); -} - -#undef PROCESS_EXPAND - -/********************************************************************** - * Clean up process - */ - -static void -process_delete(struct process *p) -{ - int i; - - CHECK_OBJ_NOTNULL(p, PROCESS_MAGIC); - PTOK(pthread_mutex_destroy(&p->mtx)); - vtc_logclose(p->vl); - free(p->name); - free(p->dir); - free(p->out); - free(p->err); - - for (i = 0; i < p->nlin; i++) - free(p->vram[i]); - free(p->vram); - - /* - * We do not delete the directory, it may contain useful stdout - * and stderr files. They will be deleted on account of belonging - * to the test's tmpdir. - */ - - /* XXX: MEMLEAK (?) */ - FREE_OBJ(p); -} - -static void -process_undef(const struct process *p) -{ - CHECK_OBJ_NOTNULL(p, PROCESS_MAGIC); - - macro_undef(p->vl, p->name, "dir"); - macro_undef(p->vl, p->name, "out"); - macro_undef(p->vl, p->name, "err"); -} - -/********************************************************************** - * Data stream handling - */ - -static int -process_vlu_func(void *priv, const char *l) -{ - struct process *p; - - CAST_OBJ_NOTNULL(p, priv, PROCESS_MAGIC); - vtc_dump(p->vl, 4, "output", l, -1); - return (0); -} - -static int v_matchproto_(vev_cb_f) -process_stdout(const struct vev *ev, int what) -{ - struct process *p; - char buf[BUFSIZ]; - int i; - - CAST_OBJ_NOTNULL(p, ev->priv, PROCESS_MAGIC); - (void)what; - i = read(p->fd_term, buf, sizeof buf); - if (i <= 0) { - vtc_log(p->vl, 4, "stdout read %d", i); - return (1); - } - PTOK(pthread_mutex_lock(&p->mtx)); - p->stdout_bytes += i; - PTOK(pthread_mutex_unlock(&p->mtx)); - if (p->log == 1) - (void)VLU_Feed(p->vlu_stdout, buf, i); - else if (p->log == 2) - vtc_dump(p->vl, 4, "stdout", buf, i); - else if (p->log == 3) - vtc_hexdump(p->vl, 4, "stdout", buf, i); - assert(write(p->f_stdout, buf, i) == i); - PTOK(pthread_mutex_lock(&p->mtx)); - teken_input(p->tek, buf, i); - PTOK(pthread_mutex_unlock(&p->mtx)); - return (0); -} - -static int v_matchproto_(vev_cb_f) -process_stderr(const struct vev *ev, int what) -{ - struct process *p; - char buf[BUFSIZ]; - int i; - - CAST_OBJ_NOTNULL(p, ev->priv, PROCESS_MAGIC); - (void)what; - i = read(p->fd_stderr, buf, sizeof buf); - if (i <= 0) { - vtc_log(p->vl, 4, "stderr read %d", i); - return (1); - } - PTOK(pthread_mutex_lock(&p->mtx)); - p->stderr_bytes += i; - PTOK(pthread_mutex_unlock(&p->mtx)); - vtc_dump(p->vl, 4, "stderr", buf, i); - assert(write(p->f_stderr, buf, i) == i); - return (0); -} - -static void -process_cleanup(void *priv) -{ - struct vev_root *evb = priv; - VEV_Destroy(&evb); -} - -static void * -process_thread(void *priv) -{ - struct process *p; - struct vev_root *evb; - struct vev *ev; - int r; - - CAST_OBJ_NOTNULL(p, priv, PROCESS_MAGIC); - - p->f_stdout = open(p->out, O_WRONLY|O_APPEND); - assert(p->f_stdout >= 0); - p->f_stderr = open(p->err, O_WRONLY|O_APPEND); - assert(p->f_stderr >= 0); - - evb = VEV_New(); - AN(evb); - pthread_cleanup_push(process_cleanup, evb); - - ev = VEV_Alloc(); - AN(ev); - ev->fd = p->fd_term; - ev->fd_flags = VEV__RD | VEV__HUP | VEV__ERR; - ev->callback = process_stdout; - ev->priv = p; - AZ(VEV_Start(evb, ev)); - - ev = VEV_Alloc(); - AN(ev); - ev->fd = p->fd_stderr; - ev->fd_flags = VEV__RD | VEV__HUP | VEV__ERR; - ev->callback = process_stderr; - ev->priv = p; - AZ(VEV_Start(evb, ev)); - - if (p->log == 1) { - p->vlu_stdout = VLU_New(process_vlu_func, p, 1024); - AN(p->vlu_stdout); - p->vlu_stderr = VLU_New(process_vlu_func, p, 1024); - AN(p->vlu_stderr); - } - - do { - r = VEV_Once(evb); - } while (r == 1); - - if (r < 0) - vtc_fatal(p->vl, "VEV_Once() = %d, error %s", r, - strerror(errno)); - - vtc_wait4(p->vl, p->pid, - p->expect_exit, p->expect_signal, p->allow_core); - closefd(&p->f_stdout); - closefd(&p->f_stderr); - - PTOK(pthread_mutex_lock(&p->mtx)); - - /* NB: We keep the other macros around */ - macro_undef(p->vl, p->name, "pid"); - p->pid = -1; - - PTOK(pthread_mutex_unlock(&p->mtx)); - - pthread_cleanup_pop(0); - VEV_Destroy(&evb); - if (p->log == 1) { - VLU_Destroy(&p->vlu_stdout); - VLU_Destroy(&p->vlu_stderr); - } - return (NULL); -} - -static void -process_winsz(struct process *p, int fd) -{ - struct winsize ws; - int i; - - memset(&ws, 0, sizeof ws); - ws.ws_row = (short)p->nlin; - ws.ws_col = (short)p->ncol; - i = ioctl(fd, TIOCSWINSZ, &ws); - if (i) - vtc_log(p->vl, 4, "TIOCWINSZ %d %s", i, strerror(errno)); -} - -static void -process_init_term(struct process *p, int fd) -{ - struct termios tt; - int i; - - process_winsz(p, fd); - - memset(&tt, 0, sizeof tt); - tt.c_cflag = CREAD | CS8 | HUPCL; - tt.c_iflag = BRKINT | ICRNL | IMAXBEL | IXON | IXANY; - tt.c_lflag = ICANON | ISIG | IEXTEN | ECHO | ECHOE | ECHOKE | ECHOCTL; - tt.c_oflag = OPOST | ONLCR; - i = cfsetispeed(&tt, B9600); - if (i) - vtc_log(p->vl, 4, "cfsetispeed %d %s", i, strerror(errno)); - i = cfsetospeed(&tt, B9600); - if (i) - vtc_log(p->vl, 4, "cfsetospeed %d %s", i, strerror(errno)); - tt.c_cc[VEOF] = '\x04'; // CTRL-D - tt.c_cc[VERASE] = '\x08'; // CTRL-H (Backspace) - tt.c_cc[VKILL] = '\x15'; // CTRL-U - tt.c_cc[VINTR] = '\x03'; // CTRL-C - tt.c_cc[VQUIT] = '\x1c'; // CTRL-backslash - - i = tcsetattr(fd, TCSAFLUSH, &tt); - if (i) - vtc_log(p->vl, 4, "TCSAFLUSH %d %s", i, strerror(errno)); -} - -/********************************************************************** - * Start the process thread - */ - -static void -process_start(struct process *p) -{ - struct vsb *cl; - int fd2[2]; - int master, slave; - const char *slavename; - char c; - - CHECK_OBJ_NOTNULL(p, PROCESS_MAGIC); - if (p->hasthread) - vtc_fatal(p->vl, "Already running, -wait first"); - - vtc_log(p->vl, 4, "CMD: %s", p->spec); - - cl = macro_expand(p->vl, p->spec); - AN(cl); - - master = posix_openpt(O_RDWR|O_NOCTTY); - assert(master >= 0); - AZ(grantpt(master)); - AZ(unlockpt(master)); - slavename = ptsname(master); - AN(slavename); - - AZ(pipe(fd2)); - - p->pid = fork(); - assert(p->pid >= 0); - if (p->pid == 0) { - assert(setsid() == getpid()); - assert(dup2(fd2[1], STDERR_FILENO) == STDERR_FILENO); - AZ(close(STDIN_FILENO)); - slave = open(slavename, O_RDWR); - assert(slave == STDIN_FILENO); -#ifdef __sun - if (ioctl(slave, I_PUSH, "ptem")) - vtc_log(p->vl, 4, "PUSH ptem: %s", strerror(errno)); - if (ioctl(slave, I_PUSH, "ldterm")) - vtc_log(p->vl, 4, "PUSH ldterm: %s", strerror(errno)); - (void)ioctl(STDIN_FILENO, TIOCSCTTY, NULL); -#else - AZ(ioctl(STDIN_FILENO, TIOCSCTTY, NULL)); -#endif - AZ(close(STDOUT_FILENO)); - assert(dup2(slave, STDOUT_FILENO) == STDOUT_FILENO); - VSUB_closefrom(STDERR_FILENO + 1); - process_init_term(p, slave); - - AZ(setenv("TERM", "xterm", 1)); - AZ(unsetenv("TERMCAP")); - // Not using NULL because GCC is now even more demented... - assert(write(STDERR_FILENO, "+", 1) == 1); - AZ(execl("/bin/sh", "/bin/sh", "-c", VSB_data(cl), (char*)0)); - exit(1); - } - vtc_log(p->vl, 3, "PID: %ld", (long)p->pid); - VSB_destroy(&cl); - - assert(read(fd2[0], &c, 1) == 1); - p->fd_term = master; - closefd(&fd2[1]); - p->fd_stderr = fd2[0]; - macro_def(p->vl, p->name, "pid", "%ld", (long)p->pid); - macro_def(p->vl, p->name, "dir", "%s", p->dir); - macro_def(p->vl, p->name, "out", "%s", p->out); - macro_def(p->vl, p->name, "err", "%s", p->err); - p->hasthread = 1; - PTOK(pthread_create(&p->tp, NULL, process_thread, p)); -} - -/********************************************************************** - * Wait for process thread to stop - */ - -static void -process_wait(struct process *p) -{ - void *v; - - if (p->hasthread) { - PTOK(pthread_join(p->tp, &v)); - p->hasthread = 0; - } - vtc_log(p->vl, 4, "stdout %ju bytes, stderr %ju bytes", - p->stdout_bytes, p->stderr_bytes); -} - -/********************************************************************** - * Send a signal to a process - */ - -static void -process_kill(struct process *p, const char *sig) -{ - int j = 0; - pid_t pid; - - CHECK_OBJ_NOTNULL(p, PROCESS_MAGIC); - AN(sig); - - PTOK(pthread_mutex_lock(&p->mtx)); - pid = p->pid; - PTOK(pthread_mutex_unlock(&p->mtx)); - - if (pid <= 0) - vtc_fatal(p->vl, "Cannot signal a non-running process"); - - if (!strcmp(sig, "TERM")) - j = SIGTERM; - else if (!strcmp(sig, "INT")) - j = SIGINT; - else if (!strcmp(sig, "KILL")) - j = SIGKILL; - else if (!strcmp(sig, "HUP")) - j = SIGHUP; - else if (*sig == '-') - j = strtoul(sig + 1, NULL, 10); - else - vtc_fatal(p->vl, "Could not grok signal (%s)", sig); - - if (p->expect_signal == 0) - p->expect_signal = -j; - if (kill(-pid, j) < 0) - vtc_fatal(p->vl, "Failed to send signal %d (%s)", - j, strerror(errno)); - else - vtc_log(p->vl, 4, "Sent signal %d", j); -} - -/********************************************************************** - * Write to a process' stdin - */ - -static void -process_write(const struct process *p, const char *text) -{ - int r, len; - - if (!p->hasthread) - vtc_fatal(p->vl, "Cannot write to a non-running process"); - - len = strlen(text); - vtc_log(p->vl, 4, "Writing %d bytes", len); - r = write(p->fd_term, text, len); - if (r != len) - vtc_fatal(p->vl, "Failed to write: len=%d %s (%d)", - len, strerror(errno), errno); -} - -static void -process_write_hex(const struct process *p, const char *text) -{ - struct vsb *vsb; - - if (!p->hasthread) - vtc_fatal(p->vl, "Cannot write to a non-running process"); - - vsb = vtc_hex_to_bin(p->vl, text); - assert(VSB_len(vsb) >= 0); - vtc_hexdump(p->vl, 4, "sendhex", VSB_data(vsb), VSB_len(vsb)); - AZ(VSB_tofile(vsb, p->fd_term)); - VSB_destroy(&vsb); -} - -static void -process_close(struct process *p) -{ - - if (!p->hasthread) - vtc_fatal(p->vl, "Cannot close a non-running process"); - - process_kill(p, "HUP"); -} - -/* SECTION: process process - * - * Run a process with stdin+stdout on a pseudo-terminal and stderr on a pipe. - * - * Output from the pseudo-terminal is copied verbatim to ${pNAME_out}, - * and the -log/-dump/-hexdump flags will also put it in the vtc-log. - * - * The pseudo-terminal is not in ECHO mode, but if the programs run set - * it to ECHO mode ("stty sane") any input sent to the process will also - * appear in this stream because of the ECHO. - * - * Output from the stderr-pipe is copied verbatim to ${pNAME_err}, and - * is always included in the vtc_log. - * - * process pNAME SPEC [-allow-core] [-expect-exit N] [-expect-signal N] - * [-dump] [-hexdump] [-log] - * [-run] [-close] [-kill SIGNAL] [-start] [-stop] [-wait] - * [-write STRING] [-writeln STRING] [-writehex HEXSTRING] - * [-need-bytes [+]NUMBER] - * [-screen-dump] [-winsz LINES COLUMNS] [-ansi-response] - * [-expect-cursor LINE COLUMN] [-expect-text LINE COLUMN TEXT] - * [-match-text LINE COLUMN REGEXP] - * - * pNAME - * Name of the process. It must start with 'p'. - * - * SPEC - * The command(s) to run in this process. - * - * \-hexdump - * Log output with vtc_hexdump(). Must be before -start/-run. - * - * \-dump - * Log output with vtc_dump(). Must be before -start/-run. - * - * \-log - * Log output with VLU/vtc_log(). Must be before -start/-run. - * - * \-start - * Start the process. - * - * \-expect-exit N - * Expect exit status N - * - * \-expect-signal N - * Expect signal in exit status N - * - * \-allow-core - * Core dump in exit status is OK - * - * \-wait - * Wait for the process to finish. - * - * \-run - * Shorthand for -start -wait. - * - * In most cases, if you just want to start a process and wait for it - * to finish, you can use the ``shell`` command instead. - * The following commands are equivalent:: - * - * shell "do --something" - * - * process p1 "do --something" -run - * - * However, you may use the ``process`` variant to conveniently - * collect the standard input and output without dealing with shell - * redirections yourself. The ``shell`` command can also expect an - * expression from either output, consider using it if you only need - * to match one. - * - * \-key KEYSYM - * Send emulated key-press. - * KEYSYM can be one of (NPAGE, PPAGE, HOME, END) - * - * - * \-kill SIGNAL - * Send a signal to the process. The argument can be either - * the string "TERM", "INT", or "KILL" for SIGTERM, SIGINT or SIGKILL - * signals, respectively, or a hyphen (-) followed by the signal - * number. - * - * If you need to use other signal names, you can use the ``kill``\(1) - * command directly:: - * - * shell "kill -USR1 ${pNAME_pid}" - * - * Note that SIGHUP usage is discouraged in test cases. - * - * \-stop - * Shorthand for -kill TERM. - * - * \-close - * Alias for "-kill HUP" - * - * \-winsz LINES COLUMNS - * Change the terminal window size to LIN lines and COL columns. - * - * \-write STRING - * Write a string to the process' stdin. - * - * \-writeln STRING - * Same as -write followed by a newline (\\n). - * - * \-writehex HEXSTRING - * Same as -write but interpreted as hexadecimal bytes. - * - * \-need-bytes [+]NUMBER - * Wait until at least NUMBER bytes have been received in total. - * If '+' is prefixed, NUMBER new bytes must be received. - * - * \-ansi-response - * Respond to terminal respond-back sequences - * - * \-expect-cursor LINE COLUMN - * Expect cursors location - * - * \-expect-text LINE COLUMNS TEXT - * Wait for TEXT to appear at LIN,COL on the virtual screen. - * Lines and columns are numbered 1...N - * LIN==0 means "on any line" - * COL==0 means "anywhere on the line" - * - * \-match-text LINE COLUMN REGEXP - * Wait for the PAT regular expression to match the text at LIN,COL on the virtual screen. - * Lines and columns are numbered 1...N - * LIN==0 means "on any line" - * COL==0 means "anywhere on the line" - * - * - * \-screen-dump - * Dump the virtual screen into vtc_log - * - */ - -void -cmd_process(CMD_ARGS) -{ - struct process *p, *p2; - uintmax_t u, v, bsnap; - unsigned lin,col; - int spec_set = 0; - - (void)priv; - - if (av == NULL) { - /* Reset and free */ - VTAILQ_FOREACH_SAFE(p, &processes, list, p2) { - if (p->pid > 0) { - process_kill(p, "TERM"); - sleep(1); - if (p->pid > 0) - process_kill(p, "KILL"); - } - if (p->hasthread) - process_wait(p); - VTAILQ_REMOVE(&processes, p, list); - process_undef(p); - process_delete(p); - } - return; - } - - AZ(strcmp(av[0], "process")); - av++; - - VTC_CHECK_NAME(vl, av[0], "Process", 'p'); - VTAILQ_FOREACH(p, &processes, list) - if (!strcmp(p->name, av[0])) - break; - if (p == NULL) - p = process_new(av[0]); - av++; - - PTOK(pthread_mutex_lock(&p->mtx)); - bsnap = p->stdout_bytes; - PTOK(pthread_mutex_unlock(&p->mtx)); - - for (; *av != NULL; av++) { - if (vtc_error) - break; - - if (!strcmp(*av, "-allow-core")) { - p->allow_core = 1; - continue; - } - if (!strcmp(*av, "-close")) { - process_close(p); - continue; - } - if (!strcmp(*av, "-dump")) { - if (p->hasthread) - vtc_fatal(p->vl, - "Cannot dump a running process"); - p->log = 2; - continue; - } - if (!strcmp(*av, "-expect-exit")) { - p->expect_exit = strtoul(av[1], NULL, 0); - av++; - continue; - } - if (!strcmp(*av, "-expect-signal")) { - p->expect_signal = strtoul(av[1], NULL, 0); - av++; - continue; - } - if (!strcmp(*av, "-hexdump")) { - if (p->hasthread) - vtc_fatal(p->vl, - "Cannot dump a running process"); - p->log = 3; - continue; - } - if (!strcmp(*av, "-key")) { - if (!strcmp(av[1], "NPAGE")) - process_write(p, "\x1b\x5b\x36\x7e"); - else if (!strcmp(av[1], "PPAGE")) - process_write(p, "\x1b\x5b\x35\x7e"); - else if (!strcmp(av[1], "HOME")) - process_write(p, "\x1b\x4f\x48"); - else if (!strcmp(av[1], "END")) - process_write(p, "\x1b\x4f\x46"); - else - vtc_fatal(p->vl, "Unknown key %s", av[1]); - continue; - } - if (!strcmp(*av, "-kill")) { - process_kill(p, av[1]); - av++; - continue; - } - if (!strcmp(*av, "-log")) { - if (p->hasthread) - vtc_fatal(p->vl, - "Cannot log a running process"); - p->log = 1; - continue; - } - if (!strcmp(*av, "-need-bytes")) { - u = strtoumax(av[1], NULL, 0); - if (av[1][0] == '+') - u += bsnap; - av++; - do { - PTOK(pthread_mutex_lock(&p->mtx)); - v = p->stdout_bytes; - PTOK(pthread_mutex_unlock(&p->mtx)); - vtc_log(p->vl, 4, "Have %ju bytes", v); - VTIM_sleep(0.5); - } while(v < u); - continue; - } - if (!strcmp(*av, "-run")) { - process_start(p); - process_wait(p); - continue; - } - if (!strcmp(*av, "-ansi-response")) { - p->ansi_response = 1; - continue; - } - if (!strcmp(*av, "-expect-text")) { - AN(av[1]); - AN(av[2]); - AN(av[3]); - term_expect_text(p, av[1], av[2], av[3]); - av += 3; - continue; - } - if (!strcmp(*av, "-expect-cursor")) { - AN(av[1]); - AN(av[2]); - term_expect_cursor(p, av[1], av[2]); - av += 2; - continue; - } - if (!strcmp(*av, "-match-text")) { - AN(av[1]); - AN(av[2]); - AN(av[3]); - term_match_text(p, av[1], av[2], av[3]); - av += 3; - continue; - } - if (!strcmp(*av, "-screen_dump") || - !strcmp(*av, "-screen-dump")) { - term_screen_dump(p); - continue; - } - if (!strcmp(*av, "-start")) { - process_start(p); - continue; - } - if (!strcmp(*av, "-stop")) { - process_kill(p, "TERM"); - sleep(1); - continue; - } - if (!strcmp(*av, "-wait")) { - process_wait(p); - continue; - } - if (!strcmp(*av, "-winsz")) { - lin = atoi(av[1]); - assert(lin > 1); - col = atoi(av[2]); - assert(col > 1); - av += 2; - PTOK(pthread_mutex_lock(&p->mtx)); - term_resize(p, lin, col); - PTOK(pthread_mutex_unlock(&p->mtx)); - process_winsz(p, p->fd_term); - continue; - } - if (!strcmp(*av, "-write")) { - process_write(p, av[1]); - av++; - continue; - } - if (!strcmp(*av, "-writehex")) { - process_write_hex(p, av[1]); - av++; - continue; - } - if (!strcmp(*av, "-writeln")) { - process_write(p, av[1]); - process_write(p, "\n"); - av++; - continue; - } - if (**av == '-' || spec_set) - vtc_fatal(p->vl, "Unknown process argument: %s", *av); - REPLACE(p->spec, *av); - spec_set = 1; - } -} diff --git a/bin/varnishtest/vtc_proxy.c b/bin/varnishtest/vtc_proxy.c deleted file mode 100644 index 583748c59..000000000 --- a/bin/varnishtest/vtc_proxy.c +++ /dev/null @@ -1,250 +0,0 @@ -/*- - * Copyright (c) 2015 Varnish Software AS - * All rights reserved. - * - * Author: Poul-Henning Kamp - * - * SPDX-License-Identifier: BSD-2-Clause - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#include "config.h" - -#include -#include -#include - -#include - -#include - -#include "vtc.h" - -#include "vend.h" -#include "vsa.h" -#include "vtcp.h" - -static const char vpx1_sig[] = {'P', 'R', 'O', 'X', 'Y'}; -static const char vpx2_sig[] = { - '\r', '\n', '\r', '\n', '\0', '\r', '\n', - 'Q', 'U', 'I', 'T', '\n', -}; - -//lint -esym(750, PP2_*) -#define PP2_TYPE_ALPN 0x01 -#define PP2_TYPE_AUTHORITY 0x02 -#define PP2_TYPE_CRC32C 0x03 -#define PP2_TYPE_NOOP 0x04 -#define PP2_TYPE_UNIQUE_ID 0x05 -#define PP2_TYPE_SSL 0x20 -#define PP2_SUBTYPE_SSL_VERSION 0x21 -#define PP2_SUBTYPE_SSL_CN 0x22 -#define PP2_SUBTYPE_SSL_CIPHER 0x23 -#define PP2_SUBTYPE_SSL_SIG_ALG 0x24 -#define PP2_SUBTYPE_SSL_KEY_ALG 0x25 -#define PP2_SUBTYPE_SSL_MAX 0x25 -#define PP2_TYPE_NETNS 0x30 - -struct pp2_type { - const char * name; - uint8_t type; -}; - -/* sorted ! */ -static const struct pp2_type pp2_types[] = { - {"alpn", PP2_TYPE_ALPN}, - {"authority", PP2_TYPE_AUTHORITY}, - {"crc32c", PP2_TYPE_CRC32C}, - {"netns", PP2_TYPE_NETNS}, - {"noop", PP2_TYPE_NOOP}, - {"unique_id", PP2_TYPE_UNIQUE_ID} -}; - -static int -pp2cmp(const void *va, const void *vb) -{ - const struct pp2_type *a = va; - const struct pp2_type *b = vb; - return (strcmp(a->name, b->name)); -} - -void -vtc_proxy_tlv(struct vtclog *vl, struct vsb *vsb, const char *kva) -{ - struct pp2_type *pp2, needle; - char *save = NULL, *kv; - struct vsb *vsb2; - const char *p; - uint16_t le; - ssize_t sz; - - kv = strdup(kva); - AN(kv); - - p = strtok_r(kv, "=", &save); - AN(p); - if (p[0] == '0' && p[1] == 'x') { - p += 2; - vsb2 = vtc_hex_to_bin(vl, p); - AN(vsb2); - if (VSB_len(vsb2) != 1) - vtc_fatal(vl, "tlv hex type has wrong length"); - VSB_bcat(vsb, VSB_data(vsb2), 1); - VSB_destroy(&vsb2); - } - else { - needle = (typeof(needle)){p, 0}; - pp2 = bsearch(&needle, pp2_types, sizeof pp2_types / sizeof pp2_types[0], - sizeof pp2_types[0], pp2cmp); - if (pp2 == NULL) - vtc_fatal(vl, "tlv type %s not found", p); - VSB_putc(vsb, pp2->type); - } - - p = strtok_r(NULL, "", &save); - if (p == NULL) - vtc_fatal(vl, "tlv value missing"); - if (p[0] == '0' && p[1] == 'x') - vsb2 = vtc_hex_to_bin(vl, p + 2); - else { - vsb2 = VSB_new_auto(); - AN(vsb2); - VSB_cat(vsb2, p); - AZ(VSB_finish(vsb2)); - } - AN(vsb2); - free(kv); - - sz = VSB_len(vsb2); - assert(sz >= 0); - assert(sz <= UINT16_MAX); - - vbe16enc(&le, (uint16_t)sz); - assert(sizeof(le) == 2); - VSB_bcat(vsb, &le, 2); - VSB_bcat(vsb, VSB_data(vsb2), sz); - VSB_destroy(&vsb2); -} - -static void -vpx_enc_addr(struct vsb *vsb, int proto, const struct suckaddr *s) -{ - const struct sockaddr_in *sin4; - const struct sockaddr_in6 *sin6; - socklen_t sl; - - if (proto == PF_INET6) { - sin6 = VSA_Get_Sockaddr(s, &sl); //lint !e826 - AN(sin6); - assert(sl >= sizeof(*sin6)); - VSB_bcat(vsb, &sin6->sin6_addr, sizeof(sin6->sin6_addr)); - } else { - sin4 = VSA_Get_Sockaddr(s, &sl); //lint !e826 - AN(sin4); - assert(sl >= sizeof(*sin4)); - VSB_bcat(vsb, &sin4->sin_addr, sizeof(sin4->sin_addr)); - } -} - -static void -vpx_enc_port(struct vsb *vsb, const struct suckaddr *s) -{ - uint8_t b[2]; - - vbe16enc(b, (uint16_t)VSA_Port(s)); - VSB_bcat(vsb, b, sizeof(b)); -} - -int -vtc_send_proxy(int fd, int version, const struct suckaddr *sac, - const struct suckaddr *sas, struct vsb *tlv) -{ - struct vsb *vsb; - char hc[VTCP_ADDRBUFSIZE]; - char pc[VTCP_PORTBUFSIZE]; - char hs[VTCP_ADDRBUFSIZE]; - char ps[VTCP_PORTBUFSIZE]; - uint16_t le, l; - int i; - int proto; - - AN(sac); - AN(sas); - - assert(version == 1 || version == 2); - vsb = VSB_new_auto(); - AN(vsb); - - proto = VSA_Get_Proto(sas); - assert(proto == PF_INET6 || proto == PF_INET); - - if (tlv == NULL) - l = 0; - else - l = VSB_len(tlv); - - assert(l <= UINT16_MAX - 0x24); - - if (version == 1) { - VSB_bcat(vsb, vpx1_sig, sizeof(vpx1_sig)); - if (proto == PF_INET6) - VSB_cat(vsb, " TCP6 "); - else if (proto == PF_INET) - VSB_cat(vsb, " TCP4 "); - VTCP_name(sac, hc, sizeof(hc), pc, sizeof(pc)); - VTCP_name(sas, hs, sizeof(hs), ps, sizeof(ps)); - VSB_printf(vsb, "%s %s %s %s\r\n", hc, hs, pc, ps); - } else if (version == 2) { - VSB_bcat(vsb, vpx2_sig, sizeof(vpx2_sig)); - VSB_putc(vsb, 0x21); - if (proto == PF_INET6) { - VSB_putc(vsb, 0x21); - l += 0x24; - } else if (proto == PF_INET) { - VSB_putc(vsb, 0x11); - l += 0x0c; - } else - WRONG("proto"); - - vbe16enc(&le, l); - assert(sizeof(le) == 2); - VSB_bcat(vsb, &le, 2); - vpx_enc_addr(vsb, proto, sac); - vpx_enc_addr(vsb, proto, sas); - vpx_enc_port(vsb, sac); - vpx_enc_port(vsb, sas); - } else - WRONG("Wrong proxy version"); - - AZ(VSB_finish(vsb)); - i = VSB_tofile(vsb, fd); - VSB_destroy(&vsb); - if (i != 0 && tlv != NULL) - VSB_destroy(&tlv); - if (i != 0 || tlv == NULL) - return (i); - - i = VSB_tofile(tlv, fd); - VSB_destroy(&tlv); - return (i); -} diff --git a/bin/varnishtest/vtc_server.c b/bin/varnishtest/vtc_server.c deleted file mode 100644 index 5de00896a..000000000 --- a/bin/varnishtest/vtc_server.c +++ /dev/null @@ -1,585 +0,0 @@ -/*- - * Copyright (c) 2008-2010 Varnish Software AS - * All rights reserved. - * - * Author: Poul-Henning Kamp - * - * SPDX-License-Identifier: BSD-2-Clause - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#include "config.h" - -#include -#include -#include - -#include -#include -#include -#include - -#include "vsa.h" -#include "vtc.h" - -#include "vtcp.h" -#include "vus.h" - -struct server { - unsigned magic; -#define SERVER_MAGIC 0x55286619 - char *name; - struct vtclog *vl; - VTAILQ_ENTRY(server) list; - struct vtc_sess *vsp; - char run; - - char *spec; - - int depth; - int sock; - int fd; - unsigned is_dispatch; - char listen[256]; - char aaddr[VTCP_ADDRBUFSIZE]; - char aport[VTCP_PORTBUFSIZE]; - - pthread_t tp; -}; - -static pthread_mutex_t server_mtx; - -static VTAILQ_HEAD(, server) servers = - VTAILQ_HEAD_INITIALIZER(servers); - -/********************************************************************** - * Allocate and initialize a server - */ - -static struct server * -server_new(const char *name, struct vtclog *vl) -{ - struct server *s; - - VTC_CHECK_NAME(vl, name, "Server", 's'); - ALLOC_OBJ(s, SERVER_MAGIC); - AN(s); - REPLACE(s->name, name); - s->vl = vtc_logopen("%s", s->name); - AN(s->vl); - s->vsp = Sess_New(s->vl, name); - AN(s->vsp); - - bprintf(s->listen, "%s", default_listen_addr); - s->depth = 10; - s->sock = -1; - s->fd = -1; - PTOK(pthread_mutex_lock(&server_mtx)); - VTAILQ_INSERT_TAIL(&servers, s, list); - PTOK(pthread_mutex_unlock(&server_mtx)); - return (s); -} - -/********************************************************************** - * Clean up a server - */ - -static void -server_delete(struct server *s) -{ - - CHECK_OBJ_NOTNULL(s, SERVER_MAGIC); - Sess_Destroy(&s->vsp); - macro_undef(s->vl, s->name, "addr"); - macro_undef(s->vl, s->name, "port"); - macro_undef(s->vl, s->name, "sock"); - vtc_logclose(s->vl); - free(s->name); - /* XXX: MEMLEAK (?) (VSS ??) */ - FREE_OBJ(s); -} - -/********************************************************************** - * Server listen - */ - -struct helper { - int depth; - const char **errp; -}; - -/* cf. VTCP_listen_on() */ -static int v_matchproto_(vus_resolved_f) -uds_listen(void *priv, const struct sockaddr_un *uds) -{ - int sock, e; - struct helper *hp = priv; - - sock = VUS_bind(uds, hp->errp); - if (sock >= 0) { - if (listen(sock, hp->depth) != 0) { - e = errno; - closefd(&sock); - errno = e; - if (hp->errp != NULL) - *hp->errp = "listen(2)"; - return (-1); - } - } - if (sock > 0) { - *hp->errp = NULL; - return (sock); - } - AN(*hp->errp); - return (0); -} - -static void -server_listen_uds(struct server *s, const char **errp) -{ - mode_t m; - struct helper h; - - h.depth = s->depth; - h.errp = errp; - - errno = 0; - if (unlink(s->listen) != 0 && errno != ENOENT) - vtc_fatal(s->vl, "Could not unlink %s before bind: %s", - s->listen, strerror(errno)); - /* - * Temporarily set the umask to 0 to avoid issues with - * permissions. - */ - m = umask(0); - s->sock = VUS_resolver(s->listen, uds_listen, &h, errp); - (void)umask(m); - if (*errp != NULL) - return; - assert(s->sock > 0); - macro_def(s->vl, s->name, "addr", "0.0.0.0"); - macro_def(s->vl, s->name, "port", "0"); - macro_def(s->vl, s->name, "sock", "%s", s->listen); -} - -static void -server_listen_tcp(struct server *s, const char **errp) -{ - char buf[vsa_suckaddr_len]; - const struct suckaddr *sua; - - s->sock = VTCP_listen_on(s->listen, "0", s->depth, errp); - if (*errp != NULL) - return; - assert(s->sock > 0); - sua = VSA_getsockname(s->sock, buf, sizeof buf); - AN(sua); - VTCP_name(sua, s->aaddr, sizeof s->aaddr, - s->aport, sizeof s->aport); - - /* Record the actual port, and reuse it on subsequent starts */ - if (VSA_Get_Proto(sua) == AF_INET) - bprintf(s->listen, "%s:%s", s->aaddr, s->aport); - else - bprintf(s->listen, "[%s]:%s", s->aaddr, s->aport); - - macro_def(s->vl, s->name, "addr", "%s", s->aaddr); - macro_def(s->vl, s->name, "port", "%s", s->aport); - macro_def(s->vl, s->name, "sock", "%s", s->listen); -} - -static void -server_listen(struct server *s) -{ - const char *err; - - CHECK_OBJ_NOTNULL(s, SERVER_MAGIC); - - if (s->sock >= 0) - VTCP_close(&s->sock); - if (VUS_is(s->listen)) - server_listen_uds(s, &err); - else - server_listen_tcp(s, &err); - if (err != NULL) - vtc_fatal(s->vl, - "Server listen address (%s) cannot be resolved: %s", - s->listen, err); -} - -/********************************************************************** - * Server thread - */ - -static int -server_conn(void *priv, struct vtclog *vl) -{ - struct server *s; - struct sockaddr_storage addr_s; - struct sockaddr *addr; - char abuf[VTCP_ADDRBUFSIZE]; - char pbuf[VTCP_PORTBUFSIZE]; - socklen_t l; - int fd; - - CAST_OBJ_NOTNULL(s, priv, SERVER_MAGIC); - - addr = (void*)&addr_s; - l = sizeof addr_s; - fd = accept(s->sock, addr, &l); - if (fd < 0) - vtc_fatal(vl, "Accept failed: %s", strerror(errno)); - if (VUS_is(s->listen)) - vtc_log(vl, 3, "accepted fd %d 0.0.0.0 0", fd); - else { - VTCP_hisname(fd, abuf, sizeof abuf, pbuf, sizeof pbuf); - vtc_log(vl, 3, "accepted fd %d %s %s", fd, abuf, pbuf); - } - return (fd); -} - -static void -server_disc(void *priv, struct vtclog *vl, int *fdp) -{ - int j; - struct server *s; - - CAST_OBJ_NOTNULL(s, priv, SERVER_MAGIC); - vtc_log(vl, 3, "shutting fd %d (server run)", *fdp); - j = shutdown(*fdp, SHUT_WR); - if (!vtc_stop && !VTCP_Check(j)) - vtc_fatal(vl, "Shutdown failed: %s", strerror(errno)); - VTCP_close(fdp); -} - -static void -server_start_thread(struct server *s) -{ - - s->run = 1; - s->tp = Sess_Start_Thread( - s, - s->vsp, - server_conn, - server_disc, - s->listen, - &s->sock, - s->spec - ); -} - -/********************************************************************** - * Start the server thread - */ - -static void -server_start(struct server *s) -{ - CHECK_OBJ_NOTNULL(s, SERVER_MAGIC); - vtc_log(s->vl, 2, "Starting server"); - server_listen(s); - vtc_log(s->vl, 1, "Listen on %s", s->listen); - server_start_thread(s); -} - -/********************************************************************** - */ - -static void * -server_dispatch_wrk(void *priv) -{ - struct server *s; - struct vtclog *vl; - int j, fd; - - CAST_OBJ_NOTNULL(s, priv, SERVER_MAGIC); - assert(s->sock < 0); - - vl = vtc_logopen("%s", s->name); - pthread_cleanup_push(vtc_logclose, vl); - - fd = s->fd; - - vtc_log(vl, 3, "start with fd %d", fd); - fd = sess_process(vl, s->vsp, s->spec, fd, &s->sock, s->listen); - vtc_log(vl, 3, "shutting fd %d (server dispatch)", fd); - j = shutdown(fd, SHUT_WR); - if (!VTCP_Check(j)) - vtc_fatal(vl, "Shutdown failed: %s", strerror(errno)); - VTCP_close(&s->fd); - vtc_log(vl, 2, "Ending"); - pthread_cleanup_pop(0); - vtc_logclose(vl); - return (NULL); -} - -static void * -server_dispatch_thread(void *priv) -{ - struct server *s, *s2; - static int sn = 1; - int fd; - char snbuf[8]; - struct vtclog *vl; - struct sockaddr_storage addr_s; - struct sockaddr *addr; - socklen_t l; - - CAST_OBJ_NOTNULL(s, priv, SERVER_MAGIC); - assert(s->sock >= 0); - - vl = vtc_logopen("%s", s->name); - pthread_cleanup_push(vtc_logclose, vl); - - vtc_log(vl, 2, "Dispatch started on %s", s->listen); - - while (!vtc_stop) { - addr = (void*)&addr_s; - l = sizeof addr_s; - fd = accept(s->sock, addr, &l); - if (fd < 0) - vtc_fatal(vl, "Accepted failed: %s", strerror(errno)); - bprintf(snbuf, "s%d", sn++); - vtc_log(vl, 3, "dispatch fd %d -> %s", fd, snbuf); - s2 = server_new(snbuf, vl); - s2->is_dispatch = 1; - s2->spec = s->spec; - bstrcpy(s2->listen, s->listen); - s2->fd = fd; - s2->run = 1; - PTOK(pthread_create(&s2->tp, NULL, server_dispatch_wrk, s2)); - } - pthread_cleanup_pop(0); - vtc_logclose(vl); - NEEDLESS(return (NULL)); -} - -static void -server_dispatch(struct server *s) -{ - CHECK_OBJ_NOTNULL(s, SERVER_MAGIC); - server_listen(s); - vtc_log(s->vl, 2, "Starting dispatch server"); - s->run = 1; - PTOK(pthread_create(&s->tp, NULL, server_dispatch_thread, s)); -} - -/********************************************************************** - * Force stop the server thread - */ - -static void -server_break(struct server *s) -{ - void *res; - - CHECK_OBJ_NOTNULL(s, SERVER_MAGIC); - vtc_log(s->vl, 2, "Breaking for server"); - (void)pthread_cancel(s->tp); - PTOK(pthread_join(s->tp, &res)); - VTCP_close(&s->sock); - s->tp = 0; - s->run = 0; -} - -/********************************************************************** - * Wait for server thread to stop - */ - -static void -server_wait(struct server *s) -{ - void *res; - - CHECK_OBJ_NOTNULL(s, SERVER_MAGIC); - vtc_log(s->vl, 2, "Waiting for server (%d/%d)", s->sock, s->fd); - PTOK(pthread_join(s->tp, &res)); - if (res != NULL && !vtc_stop) - vtc_fatal(s->vl, "Server returned \"%p\"", - (char *)res); - s->tp = 0; - s->run = 0; -} - -/********************************************************************** - * Generate VCL backend decls for our servers - */ - -void -cmd_server_gen_vcl(struct vsb *vsb) -{ - struct server *s; - - PTOK(pthread_mutex_lock(&server_mtx)); - VTAILQ_FOREACH(s, &servers, list) { - if (s->is_dispatch) - continue; - - if (VUS_is(s->listen)) - VSB_printf(vsb, - "backend %s { .path = \"%s\"; }\n", - s->name, s->listen); - else - VSB_printf(vsb, - "backend %s { .host = \"%s\"; .port = \"%s\"; }\n", - s->name, s->aaddr, s->aport); - } - PTOK(pthread_mutex_unlock(&server_mtx)); -} - - -/********************************************************************** - * Generate VCL backend decls for our servers - */ - -void -cmd_server_gen_haproxy_conf(struct vsb *vsb) -{ - struct server *s; - - PTOK(pthread_mutex_lock(&server_mtx)); - VTAILQ_FOREACH(s, &servers, list) { - if (! VUS_is(s->listen)) - VSB_printf(vsb, - "\n backend be%s\n" - "\tserver srv%s %s:%s\n", - s->name + 1, s->name + 1, s->aaddr, s->aport); - else - INCOMPL(); - } - VTAILQ_FOREACH(s, &servers, list) { - if (! VUS_is(s->listen)) - VSB_printf(vsb, - "\n frontend http%s\n" - "\tuse_backend be%s\n" - "\tbind \"fd@${fe%s}\"\n", - s->name + 1, s->name + 1, s->name + 1); - else - INCOMPL(); - } - PTOK(pthread_mutex_unlock(&server_mtx)); -} - - -/********************************************************************** - * Server command dispatch - */ - -void -cmd_server(CMD_ARGS) -{ - struct server *s; - - (void)priv; - - if (av == NULL) { - /* Reset and free */ - while (1) { - PTOK(pthread_mutex_lock(&server_mtx)); - s = VTAILQ_FIRST(&servers); - CHECK_OBJ_ORNULL(s, SERVER_MAGIC); - if (s != NULL) - VTAILQ_REMOVE(&servers, s, list); - PTOK(pthread_mutex_unlock(&server_mtx)); - if (s == NULL) - break; - if (s->run) { - (void)pthread_cancel(s->tp); - server_wait(s); - } - if (s->sock >= 0) - VTCP_close(&s->sock); - server_delete(s); - } - return; - } - - AZ(strcmp(av[0], "server")); - av++; - - PTOK(pthread_mutex_lock(&server_mtx)); - VTAILQ_FOREACH(s, &servers, list) - if (!strcmp(s->name, av[0])) - break; - PTOK(pthread_mutex_unlock(&server_mtx)); - if (s == NULL) - s = server_new(av[0], vl); - CHECK_OBJ_NOTNULL(s, SERVER_MAGIC); - av++; - - for (; *av != NULL; av++) { - if (vtc_error) - break; - if (!strcmp(*av, "-wait")) { - if (!s->run) - vtc_fatal(s->vl, "Server not -started"); - server_wait(s); - continue; - } - - if (!strcmp(*av, "-break")) { - server_break(s); - continue; - } - - /* - * We do an implicit -wait if people muck about with a - * running server. - */ - if (s->run) - server_wait(s); - - AZ(s->run); - - if (Sess_GetOpt(s->vsp, &av)) - continue; - - if (!strcmp(*av, "-listen")) { - if (s->sock >= 0) - VTCP_close(&s->sock); - bprintf(s->listen, "%s", av[1]); - av++; - continue; - } - if (!strcmp(*av, "-start")) { - server_start(s); - continue; - } - if (!strcmp(*av, "-dispatch")) { - if (strcmp(s->name, "s0")) - vtc_fatal(s->vl, - "server -dispatch only works on s0"); - server_dispatch(s); - continue; - } - if (**av == '-') - vtc_fatal(s->vl, "Unknown server argument: %s", *av); - s->spec = *av; - } -} - -void -init_server(void) -{ - PTOK(pthread_mutex_init(&server_mtx, NULL)); -} diff --git a/bin/varnishtest/vtc_sess.c b/bin/varnishtest/vtc_sess.c deleted file mode 100644 index d138b2483..000000000 --- a/bin/varnishtest/vtc_sess.c +++ /dev/null @@ -1,184 +0,0 @@ -/*- - * Copyright (c) 2020 Varnish Software AS - * All rights reserved. - * - * Author: Poul-Henning Kamp - * - * SPDX-License-Identifier: BSD-2-Clause - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - * - */ - -#include "config.h" - -#include - -#include -#include - -#include "vtc.h" -#include "vtc_http.h" - -struct thread_arg { - unsigned magic; -#define THREAD_ARG_MAGIC 0xd5dc5f1c - void *priv; - sess_conn_f *conn_f; - sess_disc_f *disc_f; - const char *listen_addr; - struct vtc_sess *vsp; - int *asocket; - const char *spec; -}; - -struct vtc_sess * -Sess_New(struct vtclog *vl, const char *name) -{ - struct vtc_sess *vsp; - - ALLOC_OBJ(vsp, VTC_SESS_MAGIC); - AN(vsp); - vsp->vl = vl; - REPLACE(vsp->name, name); - vsp->repeat = 1; - return (vsp); -} - -void -Sess_Destroy(struct vtc_sess **vspp) -{ - struct vtc_sess *vsp; - - TAKE_OBJ_NOTNULL(vsp, vspp, VTC_SESS_MAGIC); - REPLACE(vsp->name, NULL); - FREE_OBJ(vsp); -} - -int -Sess_GetOpt(struct vtc_sess *vsp, char * const **avp) -{ - char * const *av; - int rv = 0; - - CHECK_OBJ_NOTNULL(vsp, VTC_SESS_MAGIC); - AN(avp); - av = *avp; - AN(*av); - if (!strcmp(*av, "-rcvbuf")) { - AN(av[1]); - vsp->rcvbuf = atoi(av[1]); - av += 1; - rv = 1; - } else if (!strcmp(*av, "-repeat")) { - AN(av[1]); - vsp->repeat = atoi(av[1]); - av += 1; - rv = 1; - } else if (!strcmp(*av, "-keepalive")) { - vsp->keepalive = 1; - rv = 1; - } - *avp = av; - return (rv); -} - -int -sess_process(struct vtclog *vl, struct vtc_sess *vsp, - const char *spec, int sock, int *sfd, const char *addr) -{ - int rv; - - CHECK_OBJ_NOTNULL(vsp, VTC_SESS_MAGIC); - - rv = http_process(vl, vsp, spec, sock, sfd, addr, vsp->rcvbuf); - return (rv); -} - -static void * -sess_thread(void *priv) -{ - struct vtclog *vl; - struct vtc_sess *vsp; - struct thread_arg ta, *tap; - int i, fd = -1; - - CAST_OBJ_NOTNULL(tap, priv, THREAD_ARG_MAGIC); - ta = *tap; - FREE_OBJ(tap); - - vsp = ta.vsp; - CHECK_OBJ_NOTNULL(vsp, VTC_SESS_MAGIC); - vl = vtc_logopen("%s", vsp->name); - pthread_cleanup_push(vtc_logclose, vl); - - assert(vsp->repeat > 0); - vtc_log(vl, 2, "Started on %s (%u iterations%s)", ta.listen_addr, - vsp->repeat, vsp->keepalive ? " using keepalive" : ""); - for (i = 0; i < vsp->repeat; i++) { - if (fd < 0) - fd = ta.conn_f(ta.priv, vl); - fd = sess_process(vl, ta.vsp, ta.spec, fd, - ta.asocket, ta.listen_addr); - if (! vsp->keepalive) - ta.disc_f(ta.priv, vl, &fd); - } - if (vsp->keepalive) - ta.disc_f(ta.priv, vl, &fd); - vtc_log(vl, 2, "Ending"); - pthread_cleanup_pop(0); - vtc_logclose(vl); - return (NULL); -} - -pthread_t -Sess_Start_Thread( - void *priv, - struct vtc_sess *vsp, - sess_conn_f *conn, - sess_disc_f *disc, - const char *listen_addr, - int *asocket, - const char *spec -) -{ - struct thread_arg *ta; - pthread_t pt; - - AN(priv); - CHECK_OBJ_NOTNULL(vsp, VTC_SESS_MAGIC); - AN(conn); - AN(disc); - AN(listen_addr); - ALLOC_OBJ(ta, THREAD_ARG_MAGIC); - AN(ta); - ta->priv = priv; - ta->vsp = vsp; - - ta->conn_f = conn; - ta->disc_f = disc; - ta->listen_addr = listen_addr; - ta->asocket = asocket; - ta->spec = spec; - PTOK(pthread_create(&pt, NULL, sess_thread, ta)); - return (pt); -} diff --git a/bin/varnishtest/vtc_subr.c b/bin/varnishtest/vtc_subr.c deleted file mode 100644 index 4a9c7ec0e..000000000 --- a/bin/varnishtest/vtc_subr.c +++ /dev/null @@ -1,231 +0,0 @@ -/*- - * Copyright (c) 2008-2017 Varnish Software AS - * All rights reserved. - * - * Author: Poul-Henning Kamp - * - * SPDX-License-Identifier: BSD-2-Clause - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#include "config.h" - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "vtc.h" - -#include "vct.h" -#include "vnum.h" -#include "vre.h" - -#include "vapi/vsig.h" - -struct vsb * -vtc_hex_to_bin(struct vtclog *vl, const char *arg) -{ - struct vsb *vsb; - unsigned sh = 4; - unsigned c, b = 0; - - vsb = VSB_new_auto(); - AN(vsb); - for (; *arg != '\0'; arg++) { - if (vct_issp(*arg) || *arg == '\n') - continue; - c = (uint8_t)*arg; - if (c >= '0' && c <= '9') - b |= (c - 48U) << sh; - else if (c >= 'A' && c <= 'F') - b |= (c - 55U) << sh; - else if (c >= 'a' && c <= 'f') - b |= (c - 87U) << sh; - else - vtc_fatal(vl,"Illegal hex string"); - sh = 4 - sh; - if (sh == 4) { - VSB_putc(vsb, b); - b = 0; - } - } - if (sh != 4) - VSB_putc(vsb, b); - AZ(VSB_finish(vsb)); - return (vsb); -} - -void -vtc_expect(struct vtclog *vl, - const char *olhs, const char *lhs, - const char *cmp, - const char *orhs, const char *rhs) -{ - vre_t *vre; - struct vsb vsb[1]; - int error, erroroffset; - int i, j, retval = -1; - double fl, fr; - char errbuf[VRE_ERROR_LEN]; - - j = lhs == NULL || rhs == NULL; - if (lhs == NULL) - lhs = ""; - if (rhs == NULL) - rhs = ""; - - if (!strcmp(cmp, "~") || !strcmp(cmp, "!~")) { - vre = VRE_compile(rhs, 0, &error, &erroroffset, 1); - if (vre == NULL) { - AN(VSB_init(vsb, errbuf, sizeof errbuf)); - AZ(VRE_error(vsb, error)); - AZ(VSB_finish(vsb)); - VSB_fini(vsb); - vtc_fatal(vl, "REGEXP error: %s (@%d) (%s)", - errbuf, erroroffset, rhs); - } - i = VRE_match(vre, lhs, 0, 0, NULL); - retval = (i >= 0 && *cmp == '~') || (i < 0 && *cmp == '!'); - VRE_free(&vre); - } else if (!strcmp(cmp, "==")) { - retval = strcmp(lhs, rhs) == 0; - } else if (!strcmp(cmp, "!=")) { - retval = strcmp(lhs, rhs) != 0; - } else if (!strcmp(cmp, "-lt")) { - retval = strtoul(lhs, NULL, 0) < strtoul(rhs, NULL, 0); - } else if (!strcmp(cmp, "-le")) { - retval = strtoul(lhs, NULL, 0) <= strtoul(rhs, NULL, 0); - } else if (!strcmp(cmp, "-eq")) { - retval = strtoul(lhs, NULL, 0) == strtoul(rhs, NULL, 0); - } else if (!strcmp(cmp, "-ne")) { - retval = strtoul(lhs, NULL, 0) != strtoul(rhs, NULL, 0); - } else if (!strcmp(cmp, "-ge")) { - retval = strtoul(lhs, NULL, 0) >= strtoul(rhs, NULL, 0); - } else if (!strcmp(cmp, "-gt")) { - retval = strtoul(lhs, NULL, 0) > strtoul(rhs, NULL, 0); - } else if (j) { - // fail inequality comparisons if either side is undef'ed - retval = 0; - } else { - fl = VNUM(lhs); - fr = VNUM(rhs); - if (!strcmp(cmp, "<")) - retval = isless(fl, fr); - else if (!strcmp(cmp, ">")) - retval = isgreater(fl, fr); - else if (!strcmp(cmp, "<=")) - retval = islessequal(fl, fr); - else if (!strcmp(cmp, ">=")) - retval = isgreaterequal(fl, fr); - } - - if (retval == -1) - vtc_fatal(vl, - "EXPECT %s (%s) %s %s (%s) test not implemented", - olhs, lhs, cmp, orhs, rhs); - else if (retval == 0) - vtc_fatal(vl, "EXPECT %s (%s) %s \"%s\" failed", - olhs, lhs, cmp, rhs); - else - vtc_log(vl, 4, "EXPECT %s (%s) %s \"%s\" match", - olhs, lhs, cmp, rhs); -} - -/********************************************************************** - * Wait for a subprocess. - * - * if expect_signal > 0, the process must die on that signal. - * if expect_signal < 0, dying on that signal is allowed, but not required. - * if allow_core > 0, a coredump is allowed, but not required. - * otherwise, the process must die on exit(expect_status) - */ - -void -vtc_wait4(struct vtclog *vl, long pid, - int expect_status, int expect_signal, int allow_core) -{ - int status, r; - struct rusage ru; - - r = wait4(pid, &status, 0, &ru); - if (r < 0) - vtc_fatal(vl, "wait4 failed on pid %ld: %s", - pid, strerror(errno)); - assert(r == pid); - vtc_log(vl, 2, "WAIT4 pid=%ld status=0x%04x (user %.6f sys %.6f)", - pid, status, - ru.ru_utime.tv_sec + 1e-6 * ru.ru_utime.tv_usec, - ru.ru_stime.tv_sec + 1e-6 * ru.ru_stime.tv_usec - ); - - if (WIFEXITED(status) && expect_signal <= 0 && - WEXITSTATUS(status) == expect_status) - return; - - if (expect_signal < 0) - expect_signal = -expect_signal; - - if (WIFSIGNALED(status) && WCOREDUMP(status) <= allow_core && - WTERMSIG(status) == expect_signal) - return; - vtc_log(vl, 1, "Expected exit: 0x%x signal: %d core: %d", - expect_status, expect_signal, allow_core); - vtc_fatal(vl, "Bad exit status: 0x%04x exit 0x%x signal %d core %d", - status, - WEXITSTATUS(status), - WIFSIGNALED(status) ? WTERMSIG(status) : 0, - WCOREDUMP(status)); -} - -void * -vtc_record(struct vtclog *vl, int fd, struct vsb *vsb) -{ - char buf[BUFSIZ]; - int i; - - while (1) { - errno = 0; - i = read(fd, buf, sizeof buf - 1); - if (i > 0) { - if (vsb != NULL) - VSB_bcat(vsb, buf, i); - buf[i] = '\0'; - vtc_dump(vl, 3, "debug", buf, -2); - } else if (i == 0 && errno == 0) { - vtc_log(vl, 4, "STDOUT EOF"); - break; - } else { - vtc_log(vl, 4, - "STDOUT read failed with %d - %s.", - errno, strerror(errno)); - break; - } - } - return (NULL); -} - diff --git a/bin/varnishtest/vtc_syslog.c b/bin/varnishtest/vtc_syslog.c deleted file mode 100644 index 02ffce658..000000000 --- a/bin/varnishtest/vtc_syslog.c +++ /dev/null @@ -1,661 +0,0 @@ -/*- - * Copyright (c) 2008-2010 Varnish Software AS - * All rights reserved. - * - * Author: Fr?d?ric L?caille - * - * SPDX-License-Identifier: BSD-2-Clause - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#include "config.h" - -#include -#include - -#include -#include -#include -#include - -#include "vtc.h" - -#include "vsa.h" -#include "vss.h" -#include "vtcp.h" -#include "vre.h" - -struct syslog_srv { - unsigned magic; -#define SYSLOG_SRV_MAGIC 0xbf28a692 - char *name; - struct vtclog *vl; - VTAILQ_ENTRY(syslog_srv) list; - char run; - - int repeat; - char *spec; - - int sock; - char bind[256]; - int lvl; - - pthread_t tp; - ssize_t rxbuf_left; - size_t rxbuf_sz; - char *rxbuf; - vtim_dur timeout; -}; - -static pthread_mutex_t syslog_mtx; - -static VTAILQ_HEAD(, syslog_srv) syslogs = - VTAILQ_HEAD_INITIALIZER(syslogs); - -#define SYSLOGCMDS \ - CMD_SYSLOG(expect) \ - CMD_SYSLOG(recv) - -#define CMD_SYSLOG(nm) static cmd_f cmd_syslog_##nm; -SYSLOGCMDS -#undef CMD_SYSLOG - -static const struct cmds syslog_cmds[] = { -#define CMD_SYSLOG(n) { #n, cmd_syslog_##n }, -SYSLOGCMDS -#undef CMD_SYSLOG - { NULL, NULL } -}; - -static const char * const syslog_levels[] = { - "emerg", - "alert", - "crit", - "err", - "warning", - "notice", - "info", - "debug", - NULL, -}; - -static int -get_syslog_level(struct vtclog *vl, const char *lvl) -{ - int i; - - for (i = 0; syslog_levels[i]; i++) - if (!strcmp(lvl, syslog_levels[i])) - return (i); - vtc_fatal(vl, "wrong syslog level '%s'\n", lvl); -} - -/*-------------------------------------------------------------------- - * Check if a UDP syscall return value is fatal - * XXX: Largely copied from VTCP, not sure if really applicable - */ - -static int -VUDP_Check(int a) -{ - if (a == 0) - return (1); - if (errno == ECONNRESET) - return (1); -#if (defined (__SVR4) && defined (__sun)) || defined (__NetBSD__) - /* - * Solaris returns EINVAL if the other end unexpectedly reset the - * connection. - * This is a bug in Solaris and documented behaviour on NetBSD. - */ - if (errno == EINVAL || errno == ETIMEDOUT || errno == EPIPE) - return (1); -#elif defined (__APPLE__) - /* - * macOS returns EINVAL if the other end unexpectedly reset - * the connection. - */ - if (errno == EINVAL) - return (1); -#endif - return (0); -} - -/*-------------------------------------------------------------------- - * When closing a UDP connection, a couple of errno's are legit, we - * can't be held responsible for the other end wanting to talk to us. - */ - -static void -VUDP_close(int *s) -{ - int i; - - i = close(*s); - - assert(VUDP_Check(i)); - *s = -1; -} - -/*-------------------------------------------------------------------- - * Given a struct suckaddr, open a socket of the appropriate type, and bind - * it to the requested address. - * - * If the address is an IPv6 address, the IPV6_V6ONLY option is set to - * avoid conflicts between INADDR_ANY and IN6ADDR_ANY. - */ - -static int -VUDP_bind(const struct suckaddr *sa, const char **errp) -{ -#ifdef IPV6_V6ONLY - int val; -#endif - int sd, e; - socklen_t sl; - const struct sockaddr *so; - int proto; - - if (errp != NULL) - *errp = NULL; - - proto = VSA_Get_Proto(sa); - sd = socket(proto, SOCK_DGRAM, 0); - if (sd < 0) { - if (errp != NULL) - *errp = "socket(2)"; - return (-1); - } - -#ifdef IPV6_V6ONLY - /* forcibly use separate sockets for IPv4 and IPv6 */ - val = 1; - if (proto == AF_INET6 && - setsockopt(sd, IPPROTO_IPV6, IPV6_V6ONLY, &val, sizeof val) != 0) { - if (errp != NULL) - *errp = "setsockopt(IPV6_V6ONLY, 1)"; - e = errno; - closefd(&sd); - errno = e; - return (-1); - } -#endif - so = VSA_Get_Sockaddr(sa, &sl); - if (bind(sd, so, sl) != 0) { - if (errp != NULL) - *errp = "bind(2)"; - e = errno; - closefd(&sd); - errno = e; - return (-1); - } - return (sd); -} - -/*--------------------------------------------------------------------*/ - -struct udp_helper { - const char **errp; -}; - -static int v_matchproto_(vss_resolved_f) -vudp_lo_cb(void *priv, const struct suckaddr *sa) -{ - int sock; - struct udp_helper *hp = priv; - - sock = VUDP_bind(sa, hp->errp); - if (sock > 0) { - *hp->errp = NULL; - return (sock); - } - AN(*hp->errp); - return (0); -} - -static int -VUDP_bind_on(const char *addr, const char *def_port, const char **errp) -{ - struct udp_helper h; - int sock; - - h.errp = errp; - - sock = VSS_resolver_socktype( - addr, def_port, vudp_lo_cb, &h, errp, SOCK_DGRAM); - if (*errp != NULL) - return (-1); - return (sock); -} - -/********************************************************************** - * Allocate and initialize a syslog - */ - -static struct syslog_srv * -syslog_new(const char *name, struct vtclog *vl) -{ - struct syslog_srv *s; - - VTC_CHECK_NAME(vl, name, "Syslog", 'S'); - ALLOC_OBJ(s, SYSLOG_SRV_MAGIC); - AN(s); - REPLACE(s->name, name); - s->vl = vtc_logopen("%s", s->name); - AN(s->vl); - vtc_log_set_cmd(s->vl, syslog_cmds); - - bprintf(s->bind, "%s", default_listen_addr); - s->repeat = 1; - s->sock = -1; - s->lvl = -1; - s->timeout = vtc_maxdur * .5; // XXX - - vl = vtc_logopen("%s", s->name); - AN(vl); - - s->rxbuf_sz = s->rxbuf_left = 2048*1024; - s->rxbuf = malloc(s->rxbuf_sz); /* XXX */ - AN(s->rxbuf); - - PTOK(pthread_mutex_lock(&syslog_mtx)); - VTAILQ_INSERT_TAIL(&syslogs, s, list); - PTOK(pthread_mutex_unlock(&syslog_mtx)); - return (s); -} - -/********************************************************************** - * Clean up a syslog - */ - -static void -syslog_delete(struct syslog_srv *s) -{ - - CHECK_OBJ_NOTNULL(s, SYSLOG_SRV_MAGIC); - macro_undef(s->vl, s->name, "addr"); - macro_undef(s->vl, s->name, "port"); - macro_undef(s->vl, s->name, "sock"); - vtc_logclose(s->vl); - free(s->name); - free(s->rxbuf); - /* XXX: MEMLEAK (?) (VSS ??) */ - FREE_OBJ(s); -} - -static void -syslog_rx(const struct syslog_srv *s, int lvl) -{ - ssize_t ret; - - while (!vtc_error) { - /* Pointers to syslog priority value (see , rfc5424). */ - char *prib, *prie, *end; - unsigned int prival; - - VTCP_set_read_timeout(s->sock, s->timeout); - - ret = recv(s->sock, s->rxbuf, s->rxbuf_sz - 1, 0); - if (ret < 0) { - if (errno == EINTR || errno == EAGAIN) - continue; - - vtc_fatal(s->vl, - "%s: recv failed (fd: %d read: %s", __func__, - s->sock, strerror(errno)); - } - if (ret == 0) - vtc_fatal(s->vl, - "syslog rx timeout (fd: %d %.3fs ret: %zd)", - s->sock, s->timeout, ret); - - s->rxbuf[ret] = '\0'; - vtc_dump(s->vl, 4, "syslog", s->rxbuf, ret); - - prib = s->rxbuf; - if (*prib++ != '<') - vtc_fatal(s->vl, "syslog PRI, no '<'"); - prie = strchr(prib, '>'); - if (prie == NULL) - vtc_fatal(s->vl, "syslog PRI, no '>'"); - - prival = strtoul(prib, &end, 10); - if (end != prie) - vtc_fatal(s->vl, "syslog PRI, bad number"); - - if (lvl >= 0 && lvl == (prival & 0x7)) - return; - } -} - -/********************************************************************** - * Syslog server bind - */ - -static void -syslog_bind(struct syslog_srv *s) -{ - const char *err; - char aaddr[VTCP_ADDRBUFSIZE]; - char aport[VTCP_PORTBUFSIZE]; - char buf[vsa_suckaddr_len]; - const struct suckaddr *sua; - - CHECK_OBJ_NOTNULL(s, SYSLOG_SRV_MAGIC); - - if (s->sock >= 0) - VUDP_close(&s->sock); - s->sock = VUDP_bind_on(s->bind, "0", &err); - if (err != NULL) - vtc_fatal(s->vl, - "Syslog server bind address (%s) cannot be resolved: %s", - s->bind, err); - assert(s->sock > 0); - sua = VSA_getsockname(s->sock, buf, sizeof buf); - AN(sua); - VTCP_name(sua, aaddr, sizeof aaddr, aport, sizeof aport); - macro_def(s->vl, s->name, "addr", "%s", aaddr); - macro_def(s->vl, s->name, "port", "%s", aport); - if (VSA_Get_Proto(sua) == AF_INET) - macro_def(s->vl, s->name, "sock", "%s:%s", aaddr, aport); - else - macro_def(s->vl, s->name, "sock", "[%s]:%s", aaddr, aport); - /* Record the actual port, and reuse it on subsequent starts */ - bprintf(s->bind, "%s %s", aaddr, aport); -} - -static void v_matchproto_(cmd_f) -cmd_syslog_expect(CMD_ARGS) -{ - struct syslog_srv *s; - struct vsb vsb[1]; - vre_t *vre; - int error, erroroffset, i, ret; - char *cmp, *spec, errbuf[VRE_ERROR_LEN]; - - (void)vl; - CAST_OBJ_NOTNULL(s, priv, SYSLOG_SRV_MAGIC); - AZ(strcmp(av[0], "expect")); - av++; - - cmp = av[0]; - spec = av[1]; - AN(cmp); - AN(spec); - AZ(av[2]); - - assert(!strcmp(cmp, "~") || !strcmp(cmp, "!~")); - - vre = VRE_compile(spec, 0, &error, &erroroffset, 1); - if (vre == NULL) { - AN(VSB_init(vsb, errbuf, sizeof errbuf)); - AZ(VRE_error(vsb, error)); - AZ(VSB_finish(vsb)); - VSB_fini(vsb); - vtc_fatal(s->vl, "REGEXP error: '%s' (@%d) (%s)", - errbuf, erroroffset, spec); - } - - i = VRE_match(vre, s->rxbuf, 0, 0, NULL); - - VRE_free(&vre); - - ret = (i >= 0 && *cmp == '~') || (i < 0 && *cmp == '!'); - if (!ret) - vtc_fatal(s->vl, "EXPECT FAILED %s \"%s\"", cmp, spec); - else - vtc_log(s->vl, 4, "EXPECT MATCH %s \"%s\"", cmp, spec); -} - -static void v_matchproto_(cmd_f) -cmd_syslog_recv(CMD_ARGS) -{ - int lvl; - struct syslog_srv *s; - - CAST_OBJ_NOTNULL(s, priv, SYSLOG_SRV_MAGIC); - (void)vl; - AZ(strcmp(av[0], "recv")); - av++; - if (av[0] == NULL) - lvl = s->lvl; - else - lvl = get_syslog_level(vl, av[0]); - - syslog_rx(s, lvl); -} - -/********************************************************************** - * Syslog server thread - */ - -static void * -syslog_thread(void *priv) -{ - struct syslog_srv *s; - int i; - - CAST_OBJ_NOTNULL(s, priv, SYSLOG_SRV_MAGIC); - assert(s->sock >= 0); - - vtc_log(s->vl, 2, "Started on %s (level: %d)", s->bind, s->lvl); - for (i = 0; i < s->repeat; i++) { - if (s->repeat > 1) - vtc_log(s->vl, 3, "Iteration %d", i); - parse_string(s->vl, s, s->spec); - vtc_log(s->vl, 3, "shutting fd %d", s->sock); - } - VUDP_close(&s->sock); - vtc_log(s->vl, 2, "Ending"); - return (NULL); -} - -/********************************************************************** - * Start the syslog thread - */ - -static void -syslog_start(struct syslog_srv *s) -{ - CHECK_OBJ_NOTNULL(s, SYSLOG_SRV_MAGIC); - vtc_log(s->vl, 2, "Starting syslog server"); - if (s->sock == -1) - syslog_bind(s); - vtc_log(s->vl, 1, "Bound on %s", s->bind); - s->run = 1; - PTOK(pthread_create(&s->tp, NULL, syslog_thread, s)); -} - -/********************************************************************** - * Force stop the syslog thread - */ - -static void -syslog_stop(struct syslog_srv *s) -{ - void *res; - - CHECK_OBJ_NOTNULL(s, SYSLOG_SRV_MAGIC); - vtc_log(s->vl, 2, "Stopping for syslog server"); - (void)pthread_cancel(s->tp); - PTOK(pthread_join(s->tp, &res)); - s->tp = 0; - s->run = 0; -} - -/********************************************************************** - * Wait for syslog thread to stop - */ - -static void -syslog_wait(struct syslog_srv *s) -{ - void *res; - - CHECK_OBJ_NOTNULL(s, SYSLOG_SRV_MAGIC); - vtc_log(s->vl, 2, "Waiting for syslog server (%d)", s->sock); - PTOK(pthread_join(s->tp, &res)); - if (res != NULL && !vtc_stop) - vtc_fatal(s->vl, "Syslog server returned \"%p\"", - (char *)res); - s->tp = 0; - s->run = 0; -} - -/* SECTION: syslog syslog - * - * Define and interact with syslog instances (for use with haproxy) - * - * To define a syslog server, you'll use this syntax:: - * - * syslog SNAME - * - * Arguments: - * - * SNAME - * Identify the syslog server with a string which must start with 'S'. - * - * \-level STRING - * Set the default syslog priority level used by any subsequent "recv" - * command. - * Any syslog dgram with a different level will be skipped by - * "recv" command. This default level value may be superseded - * by "recv" command if supplied as first argument: "recv ". - * - * \-start - * Start the syslog server thread in the background. - * - * \-repeat - * Instead of processing the specification only once, do it - * NUMBER times. - * - * \-bind - * Bind the syslog socket to a local address. - * - * \-wait - * Wait for that thread to terminate. - * - * \-stop - * Stop the syslog server thread. - */ - -void v_matchproto_(cmd_f) -cmd_syslog(CMD_ARGS) -{ - struct syslog_srv *s; - - (void)priv; - - if (av == NULL) { - /* Reset and free */ - do { - PTOK(pthread_mutex_lock(&syslog_mtx)); - s = VTAILQ_FIRST(&syslogs); - CHECK_OBJ_ORNULL(s, SYSLOG_SRV_MAGIC); - if (s != NULL) - VTAILQ_REMOVE(&syslogs, s, list); - PTOK(pthread_mutex_unlock(&syslog_mtx)); - if (s != NULL) { - if (s->run) { - (void)pthread_cancel(s->tp); - syslog_wait(s); - } - if (s->sock >= 0) - VUDP_close(&s->sock); - syslog_delete(s); - } - } while (s != NULL); - return; - } - - AZ(strcmp(av[0], "syslog")); - av++; - - PTOK(pthread_mutex_lock(&syslog_mtx)); - VTAILQ_FOREACH(s, &syslogs, list) - if (!strcmp(s->name, av[0])) - break; - PTOK(pthread_mutex_unlock(&syslog_mtx)); - if (s == NULL) - s = syslog_new(av[0], vl); - CHECK_OBJ_NOTNULL(s, SYSLOG_SRV_MAGIC); - av++; - - for (; *av != NULL; av++) { - if (vtc_error) - break; - if (!strcmp(*av, "-wait")) { - if (!s->run) - vtc_fatal(s->vl, "Syslog server not -started"); - syslog_wait(s); - continue; - } - - if (!strcmp(*av, "-stop")) { - syslog_stop(s); - continue; - } - - /* - * We do an implicit -wait if people muck about with a - * running syslog. - * This only works if the previous ->spec has completed - */ - if (s->run) - syslog_wait(s); - - AZ(s->run); - if (!strcmp(*av, "-repeat")) { - AN(av[1]); - s->repeat = atoi(av[1]); - av++; - continue; - } - if (!strcmp(*av, "-bind")) { - AN(av[1]); - bprintf(s->bind, "%s", av[1]); - av++; - syslog_bind(s); - continue; - } - if (!strcmp(*av, "-level")) { - AN(av[1]); - s->lvl = get_syslog_level(vl, av[1]); - av++; - continue; - } - if (!strcmp(*av, "-start")) { - syslog_start(s); - continue; - } - if (**av == '-') - vtc_fatal(s->vl, "Unknown syslog argument: %s", *av); - s->spec = *av; - } -} - -void -init_syslog(void) -{ - PTOK(pthread_mutex_init(&syslog_mtx, NULL)); -} diff --git a/bin/varnishtest/vtc_tunnel.c b/bin/varnishtest/vtc_tunnel.c deleted file mode 100644 index 8b9cc1863..000000000 --- a/bin/varnishtest/vtc_tunnel.c +++ /dev/null @@ -1,775 +0,0 @@ -/*- - * Copyright (c) 2020 Varnish Software - * All rights reserved. - * - * Author: Dridi Boukelmoune - * - * SPDX-License-Identifier: BSD-2-Clause - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#include "config.h" - -#include -#include -#include - -#include -#include -#include -#include -#include -#include - -#include "vtc.h" - -#include "vsa.h" -#include "vtcp.h" - -/* SECTION: tunnel tunnel - * - * The goal of a tunnel is to help control the data transfer between two - * parties, for example to trigger socket timeouts in the middle of protocol - * frames, without the need to change how both parties are implemented. - * - * A tunnel accepts a connection and then connects on behalf of the source to - * the desired destination. Once both connections are established the tunnel - * will transfer bytes unchanged between the source and destination. Transfer - * can be interrupted, usually with the help of synchronization methods like - * barriers. Once the transfer is paused, it is possible to let a specific - * amount of bytes move in either direction. - * - * SECTION: tunnel.args Arguments - * - * \-start - * Start the tunnel in background, processing the last given - * specification. - * - * \-start+pause - * Start the tunnel, but already paused. - * - * \-wait - * Block until the thread finishes. - * - * \-listen STRING - * Dictate the listening socket for the server. STRING is of the form - * "IP PORT", or "HOST PORT". - * - * Listens by defaults to a local random port. - * - * \-connect STRING - * Indicate the server to connect to. STRING is also of the form - * "IP PORT", or "HOST PORT". - * - * Connects by default to a varnish instance called ``v1``. - * - * SECTION: tunnel.spec Specification - * - * The specification contains a list of tunnel commands that can be combined - * with barriers and delays. For example:: - * - * tunnel t1 { - * barrier b1 sync - * pause - * delay 1 - * send 42 - * barrier b2 sync - * resume - * } -start - * - * If one end of the tunnel is closed before the end of the specification - * the test case will fail. A specification that ends in a paused state will - * implicitly resume the tunnel. - */ - -enum tunnel_state_e { - TUNNEL_ACCEPT, - TUNNEL_RUNNING, - TUNNEL_PAUSED, - TUNNEL_SPEC_DONE, - TUNNEL_POLL_DONE, - TUNNEL_STOPPED, -}; - -struct tunnel_lane { - char buf[1024]; - ssize_t buf_len; - size_t wrk_len; - int *rfd; - int *wfd; -}; - -struct tunnel { - unsigned magic; -#define TUNNEL_MAGIC 0x7f59913d - char *name; - struct vtclog *vl; - VTAILQ_ENTRY(tunnel) list; - enum tunnel_state_e state; - unsigned start_paused; - - char *spec; - - char connect[256]; - int csock; - - char listen[256]; - int lsock; - char laddr[VTCP_ADDRBUFSIZE]; - char lport[VTCP_PORTBUFSIZE]; - - int asock; - - struct tunnel_lane send_lane[1]; - struct tunnel_lane recv_lane[1]; - - pthread_mutex_t mtx; /* state and lanes->*_len */ - pthread_cond_t cond; - pthread_t tspec; - pthread_t tpoll; -}; - -static pthread_mutex_t tunnel_mtx; - -static VTAILQ_HEAD(, tunnel) tunnels = VTAILQ_HEAD_INITIALIZER(tunnels); - -/********************************************************************** - * Is the tunnel still operating? - */ - -static unsigned -tunnel_is_open(struct tunnel *t) -{ - unsigned is_open; - - PTOK(pthread_mutex_lock(&t->mtx)); - is_open = (t->send_lane->buf_len >= 0 && t->recv_lane->buf_len >= 0); - PTOK(pthread_mutex_unlock(&t->mtx)); - return (is_open); -} - -/********************************************************************** - * SECTION: tunnel.spec.pause - * - * pause - * Wait for in-flight bytes to be transferred and pause the tunnel. - * - * The tunnel must be running. - */ - -static void -cmd_tunnel_pause(CMD_ARGS) -{ - struct tunnel *t; - - CAST_OBJ_NOTNULL(t, priv, TUNNEL_MAGIC); - AZ(av[1]); - - if (!tunnel_is_open(t)) - vtc_fatal(vl, "Tunnel already closed"); - - PTOK(pthread_mutex_lock(&t->mtx)); - if (t->state == TUNNEL_PAUSED) { - PTOK(pthread_mutex_unlock(&t->mtx)); - vtc_fatal(vl, "Tunnel already paused"); - } - assert(t->state == TUNNEL_RUNNING); - t->state = TUNNEL_PAUSED; - PTOK(pthread_cond_signal(&t->cond)); - PTOK(pthread_cond_wait(&t->cond, &t->mtx)); - PTOK(pthread_mutex_unlock(&t->mtx)); -} - -/********************************************************************** - * SECTION: tunnel.spec.send - * - * send NUMBER - * Wait until NUMBER bytes are transferred from source to - * destination. - * - * The tunnel must be paused, it remains paused afterwards. - */ - -static void -cmd_tunnel_send(CMD_ARGS) -{ - struct tunnel *t; - unsigned len; - - CAST_OBJ_NOTNULL(t, priv, TUNNEL_MAGIC); - AN(av[1]); - AZ(av[2]); - - len = atoi(av[1]); - - if (!tunnel_is_open(t)) - vtc_fatal(vl, "Tunnel already closed"); - - PTOK(pthread_mutex_lock(&t->mtx)); - if (t->state == TUNNEL_RUNNING) { - PTOK(pthread_mutex_unlock(&t->mtx)); - vtc_fatal(vl, "Tunnel still running"); - } - assert(t->state == TUNNEL_PAUSED); - AZ(t->send_lane->wrk_len); - AZ(t->recv_lane->wrk_len); - if (!strcmp(av[0], "send")) - t->send_lane->wrk_len = len; - else - t->recv_lane->wrk_len = len; - PTOK(pthread_cond_signal(&t->cond)); - PTOK(pthread_cond_wait(&t->cond, &t->mtx)); - PTOK(pthread_mutex_unlock(&t->mtx)); -} - -/********************************************************************** - * SECTION: tunnel.spec.recv - * - * recv NUMBER - * Wait until NUMBER bytes are transferred from destination to - * source. - * - * The tunnel must be paused, it remains paused afterwards. - */ - -static void -cmd_tunnel_recv(CMD_ARGS) -{ - - cmd_tunnel_send(av, priv, vl); -} - -/********************************************************************** - * SECTION: tunnel.spec.resume - * - * resume - * Resume the transfer of bytes in both directions. - * - * The tunnel must be paused. - */ - -static void -cmd_tunnel_resume(CMD_ARGS) -{ - struct tunnel *t; - - CAST_OBJ_NOTNULL(t, priv, TUNNEL_MAGIC); - AZ(av[1]); - - if (!tunnel_is_open(t)) - vtc_fatal(vl, "Tunnel already closed"); - - PTOK(pthread_mutex_lock(&t->mtx)); - if (t->state == TUNNEL_RUNNING) { - PTOK(pthread_mutex_unlock(&t->mtx)); - vtc_fatal(vl, "Tunnel already running"); - } - assert(t->state == TUNNEL_PAUSED); - t->state = TUNNEL_RUNNING; - PTOK(pthread_cond_signal(&t->cond)); - PTOK(pthread_mutex_unlock(&t->mtx)); -} - -static const struct cmds tunnel_cmds[] = { -#define CMD_TUNNEL(n) { #n, cmd_tunnel_##n }, - CMD_TUNNEL(pause) - CMD_TUNNEL(send) - CMD_TUNNEL(recv) - CMD_TUNNEL(resume) -#undef CMD_TUNNEL - { NULL, NULL } -}; - -/********************************************************************** - * Tunnel poll thread - */ - -static void -tunnel_read(struct tunnel *t, struct vtclog *vl, const struct pollfd *pfd, - struct tunnel_lane *lane) -{ - size_t len; - ssize_t res; - enum tunnel_state_e state; - - assert(pfd->fd == *lane->rfd); - if (!(pfd->revents & POLLIN)) - return; - - PTOK(pthread_mutex_lock(&t->mtx)); - AZ(lane->buf_len); - len = lane->wrk_len; - state = t->state; - PTOK(pthread_mutex_unlock(&t->mtx)); - - if (len == 0 && state == TUNNEL_PAUSED) - return; - - if (len == 0 || len > sizeof lane->buf) - len = sizeof lane->buf; - - res = read(pfd->fd, lane->buf, len); - if (res < 0) - vtc_fatal(vl, "Read failed: %s", strerror(errno)); - - PTOK(pthread_mutex_lock(&t->mtx)); - lane->buf_len = (res == 0) ? -1 : res; - PTOK(pthread_mutex_unlock(&t->mtx)); -} - -static void -tunnel_write(struct tunnel *t, struct vtclog *vl, struct tunnel_lane *lane, - const char *action) -{ - const char *p; - ssize_t res, l; - - p = lane->buf; - l = lane->buf_len; - - if (l > 0) - vtc_log(vl, 3, "%s %zd bytes", action, l); - while (l > 0) { - res = write(*lane->wfd, p, l); - if (res <= 0) - vtc_fatal(vl, "Write failed: %s", strerror(errno)); - l -= res; - p += res; - } - - PTOK(pthread_mutex_lock(&t->mtx)); - if (lane->wrk_len > 0 && lane->buf_len != -1) { - assert(lane->buf_len >= 0); - assert(lane->wrk_len >= (size_t)lane->buf_len); - lane->wrk_len -= lane->buf_len; - } - lane->buf_len = l; - PTOK(pthread_mutex_unlock(&t->mtx)); -} - -static void * -tunnel_poll_thread(void *priv) -{ - struct tunnel *t; - struct vtclog *vl; - struct pollfd pfd[2]; - enum tunnel_state_e state; - int res; - - CAST_OBJ_NOTNULL(t, priv, TUNNEL_MAGIC); - - vl = vtc_logopen("%s", t->name); - pthread_cleanup_push(vtc_logclose, vl); - - while (tunnel_is_open(t) && !vtc_stop) { - PTOK(pthread_mutex_lock(&t->mtx)); - /* NB: can be woken up by `tunnel tX -wait` */ - while (t->state == TUNNEL_ACCEPT && !vtc_stop) - PTOK(pthread_cond_wait(&t->cond, &t->mtx)); - state = t->state; - PTOK(pthread_mutex_unlock(&t->mtx)); - - if (vtc_stop) - break; - - assert(state < TUNNEL_POLL_DONE); - - memset(pfd, 0, sizeof pfd); - pfd[0].fd = *t->send_lane->rfd; - pfd[1].fd = *t->recv_lane->rfd; - pfd[0].events = POLLIN; - pfd[1].events = POLLIN; - res = poll(pfd, 2, 100); - if (res == -1) - vtc_fatal(vl, "Poll failed: %s", strerror(errno)); - - tunnel_read(t, vl, &pfd[0], t->send_lane); - tunnel_read(t, vl, &pfd[1], t->recv_lane); - - PTOK(pthread_mutex_lock(&t->mtx)); - if (t->state == TUNNEL_PAUSED && t->send_lane->wrk_len == 0 && - t->recv_lane->wrk_len == 0) { - AZ(t->send_lane->buf_len); - AZ(t->recv_lane->buf_len); - PTOK(pthread_cond_signal(&t->cond)); - PTOK(pthread_cond_wait(&t->cond, &t->mtx)); - } - PTOK(pthread_mutex_unlock(&t->mtx)); - - if (vtc_stop) - break; - - tunnel_write(t, vl, t->send_lane, "Sending"); - tunnel_write(t, vl, t->recv_lane, "Receiving"); - } - - PTOK(pthread_mutex_lock(&t->mtx)); - if (t->state != TUNNEL_SPEC_DONE && !vtc_stop) { - PTOK(pthread_cond_signal(&t->cond)); - PTOK(pthread_cond_wait(&t->cond, &t->mtx)); - } - PTOK(pthread_mutex_unlock(&t->mtx)); - - pthread_cleanup_pop(0); - vtc_logclose(vl); - t->state = TUNNEL_POLL_DONE; - return (NULL); -} - -/********************************************************************** - * Tunnel spec thread - */ - -static void -tunnel_accept(struct tunnel *t, struct vtclog *vl) -{ - struct vsb *vsb; - const char *addr, *err; - int afd, cfd; - - CHECK_OBJ_NOTNULL(t, TUNNEL_MAGIC); - assert(t->lsock >= 0); - assert(t->asock < 0); - assert(t->csock < 0); - assert(t->state == TUNNEL_ACCEPT); - - vtc_log(vl, 4, "Accepting"); - afd = accept(t->lsock, NULL, NULL); - if (afd < 0) - vtc_fatal(vl, "Accept failed: %s", strerror(errno)); - vtc_log(vl, 3, "Accepted socket fd is %d", afd); - - vsb = macro_expand(vl, t->connect); - AN(vsb); - addr = VSB_data(vsb); - - cfd = VTCP_open(addr, NULL, 10., &err); - if (cfd < 0) - vtc_fatal(vl, "Failed to open %s: %s", addr, err); - vtc_log(vl, 3, "Connected socket fd is %d", cfd); - VSB_destroy(&vsb); - - VTCP_blocking(afd); - VTCP_blocking(cfd); - - PTOK(pthread_mutex_lock(&t->mtx)); - t->asock = afd; - t->csock = cfd; - t->send_lane->buf_len = 0; - t->send_lane->wrk_len = 0; - t->recv_lane->buf_len = 0; - t->recv_lane->wrk_len = 0; - if (t->start_paused) { - t->state = TUNNEL_PAUSED; - t->start_paused = 0; - } else - t->state = TUNNEL_RUNNING; - PTOK(pthread_cond_signal(&t->cond)); - PTOK(pthread_mutex_unlock(&t->mtx)); -} - -static void * -tunnel_spec_thread(void *priv) -{ - struct tunnel *t; - struct vtclog *vl; - enum tunnel_state_e state; - - CAST_OBJ_NOTNULL(t, priv, TUNNEL_MAGIC); - AN(*t->connect); - - vl = vtc_logopen("%s", t->name); - vtc_log_set_cmd(vl, tunnel_cmds); - pthread_cleanup_push(vtc_logclose, vl); - - tunnel_accept(t, vl); - parse_string(vl, t, t->spec); - - PTOK(pthread_mutex_lock(&t->mtx)); - state = t->state; - PTOK(pthread_mutex_unlock(&t->mtx)); - - if (state == TUNNEL_PAUSED && !vtc_stop) - parse_string(vl, t, "resume"); - - PTOK(pthread_mutex_lock(&t->mtx)); - t->state = TUNNEL_SPEC_DONE; - PTOK(pthread_cond_signal(&t->cond)); - PTOK(pthread_mutex_unlock(&t->mtx)); - - vtc_log(vl, 2, "Ending"); - pthread_cleanup_pop(0); - vtc_logclose(vl); - return (NULL); -} - -/********************************************************************** - * Tunnel management - */ - -static struct tunnel * -tunnel_new(const char *name) -{ - struct tunnel *t; - - ALLOC_OBJ(t, TUNNEL_MAGIC); - AN(t); - REPLACE(t->name, name); - t->vl = vtc_logopen("%s", name); - AN(t->vl); - - t->state = TUNNEL_STOPPED; - bprintf(t->connect, "%s", "${v1_sock}"); - bprintf(t->listen, "%s", default_listen_addr); - t->csock = -1; - t->lsock = -1; - t->asock = -1; - t->send_lane->rfd = &t->asock; - t->send_lane->wfd = &t->csock; - t->recv_lane->rfd = &t->csock; - t->recv_lane->wfd = &t->asock; - PTOK(pthread_mutex_init(&t->mtx, NULL)); - PTOK(pthread_cond_init(&t->cond, NULL)); - PTOK(pthread_mutex_lock(&tunnel_mtx)); - VTAILQ_INSERT_TAIL(&tunnels, t, list); - PTOK(pthread_mutex_unlock(&tunnel_mtx)); - return (t); -} - -static void -tunnel_delete(struct tunnel *t) -{ - - CHECK_OBJ_NOTNULL(t, TUNNEL_MAGIC); - assert(t->asock < 0); - assert(t->csock < 0); - if (t->lsock >= 0) - VTCP_close(&t->lsock); - macro_undef(t->vl, t->name, "addr"); - macro_undef(t->vl, t->name, "port"); - macro_undef(t->vl, t->name, "sock"); - vtc_logclose(t->vl); - (void)pthread_mutex_destroy(&t->mtx); - (void)pthread_cond_destroy(&t->cond); - free(t->name); - FREE_OBJ(t); -} - -/********************************************************************** - * Tunnel listen - */ - -static void -tunnel_listen(struct tunnel *t) -{ - char buf[vsa_suckaddr_len]; - const struct suckaddr *sua; - const char *err; - - if (t->lsock >= 0) - VTCP_close(&t->lsock); - t->lsock = VTCP_listen_on(t->listen, "0", 1, &err); - if (err != NULL) - vtc_fatal(t->vl, - "Tunnel listen address (%s) cannot be resolved: %s", - t->listen, err); - assert(t->lsock > 0); - sua = VSA_getsockname(t->lsock, buf, sizeof buf); - AN(sua); - VTCP_name(sua, t->laddr, sizeof t->laddr, t->lport, sizeof t->lport); - - /* Record the actual port, and reuse it on subsequent starts */ - if (VSA_Get_Proto(sua) == AF_INET) - bprintf(t->listen, "%s:%s", t->laddr, t->lport); - else - bprintf(t->listen, "[%s]:%s", t->laddr, t->lport); - - macro_def(t->vl, t->name, "addr", "%s", t->laddr); - macro_def(t->vl, t->name, "port", "%s", t->lport); - macro_def(t->vl, t->name, "sock", "%s %s", t->laddr, t->lport); -} - -/********************************************************************** - * Start the tunnel thread - */ - -static void -tunnel_start(struct tunnel *t) -{ - - CHECK_OBJ_NOTNULL(t, TUNNEL_MAGIC); - vtc_log(t->vl, 2, "Starting tunnel"); - tunnel_listen(t); - vtc_log(t->vl, 1, "Listen on %s", t->listen); - assert(t->state == TUNNEL_STOPPED); - t->state = TUNNEL_ACCEPT; - t->send_lane->buf_len = 0; - t->send_lane->wrk_len = 0; - t->recv_lane->buf_len = 0; - t->recv_lane->wrk_len = 0; - PTOK(pthread_create(&t->tpoll, NULL, tunnel_poll_thread, t)); - PTOK(pthread_create(&t->tspec, NULL, tunnel_spec_thread, t)); -} - -static void -tunnel_start_pause(struct tunnel *t) -{ - - CHECK_OBJ_NOTNULL(t, TUNNEL_MAGIC); - t->start_paused = 1; - tunnel_start(t); -} - -/********************************************************************** - * Wait for tunnel thread to stop - */ - -static void -tunnel_wait(struct tunnel *t) -{ - void *res; - - CHECK_OBJ_NOTNULL(t, TUNNEL_MAGIC); - vtc_log(t->vl, 2, "Waiting for tunnel"); - - PTOK(pthread_cond_signal(&t->cond)); - - PTOK(pthread_join(t->tspec, &res)); - if (res != NULL && !vtc_stop) - vtc_fatal(t->vl, "Tunnel spec returned \"%p\"", res); - - PTOK(pthread_join(t->tpoll, &res)); - if (res != NULL && !vtc_stop) - vtc_fatal(t->vl, "Tunnel poll returned \"%p\"", res); - - if (t->csock >= 0) - VTCP_close(&t->csock); - if (t->asock >= 0) - VTCP_close(&t->asock); - t->tspec = 0; - t->tpoll = 0; - t->state = TUNNEL_STOPPED; -} - -/********************************************************************** - * Reap tunnel - */ - -static void -tunnel_reset(void) -{ - struct tunnel *t; - - while (1) { - PTOK(pthread_mutex_lock(&tunnel_mtx)); - t = VTAILQ_FIRST(&tunnels); - CHECK_OBJ_ORNULL(t, TUNNEL_MAGIC); - if (t != NULL) - VTAILQ_REMOVE(&tunnels, t, list); - PTOK(pthread_mutex_unlock(&tunnel_mtx)); - if (t == NULL) - break; - - if (t->state != TUNNEL_STOPPED) - tunnel_wait(t); - tunnel_delete(t); - } -} - -/********************************************************************** - * Tunnel command dispatch - */ - -void -cmd_tunnel(CMD_ARGS) -{ - struct tunnel *t; - - (void)priv; - - if (av == NULL) { - /* Reset and free */ - tunnel_reset(); - return; - } - - AZ(strcmp(av[0], "tunnel")); - av++; - - VTC_CHECK_NAME(vl, av[0], "Tunnel", 't'); - - PTOK(pthread_mutex_lock(&tunnel_mtx)); - VTAILQ_FOREACH(t, &tunnels, list) - if (!strcmp(t->name, av[0])) - break; - PTOK(pthread_mutex_unlock(&tunnel_mtx)); - if (t == NULL) - t = tunnel_new(av[0]); - CHECK_OBJ_NOTNULL(t, TUNNEL_MAGIC); - av++; - - for (; *av != NULL; av++) { - if (vtc_error) - break; - if (!strcmp(*av, "-wait")) { - if (t->state == TUNNEL_STOPPED) - vtc_fatal(t->vl, "Tunnel not -started"); - tunnel_wait(t); - continue; - } - - /* Don't mess with a running tunnel */ - if (t->state != TUNNEL_STOPPED) - tunnel_wait(t); - - assert(t->state == TUNNEL_STOPPED); - if (!strcmp(*av, "-connect")) { - bprintf(t->connect, "%s", av[1]); - av++; - continue; - } - if (!strcmp(*av, "-listen")) { - bprintf(t->listen, "%s", av[1]); - av++; - continue; - } - if (!strcmp(*av, "-start")) { - tunnel_start(t); - continue; - } - if (!strcmp(*av, "-start+pause")) { - tunnel_start_pause(t); - continue; - } - if (**av == '-') - vtc_fatal(t->vl, "Unknown tunnel argument: %s", *av); - t->spec = *av; - } -} - -void -init_tunnel(void) -{ - - PTOK(pthread_mutex_init(&tunnel_mtx, NULL)); -} diff --git a/bin/varnishtest/vtc_varnish.c b/bin/varnishtest/vtc_varnish.c deleted file mode 100644 index 12a67b4ff..000000000 --- a/bin/varnishtest/vtc_varnish.c +++ /dev/null @@ -1,1402 +0,0 @@ -/*- - * Copyright (c) 2008-2015 Varnish Software AS - * All rights reserved. - * - * Author: Poul-Henning Kamp - * - * SPDX-License-Identifier: BSD-2-Clause - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#ifdef VTEST_WITH_VTC_VARNISH - -#include "config.h" - -#include -#include - -#include -#include -#include -#include -#include -#include -#include -#include - -#include "vtc.h" - -#include "vapi/vsc.h" -#include "vapi/vsl.h" -#include "vapi/vsm.h" -#include "vcli.h" -#include "vjsn.h" -#include "vre.h" -#include "vsub.h" -#include "vtcp.h" -#include "vtim.h" - -struct varnish { - unsigned magic; -#define VARNISH_MAGIC 0x208cd8e3 - char *name; - struct vtclog *vl; - VTAILQ_ENTRY(varnish) list; - - struct vsb *args; - int fds[4]; - pid_t pid; - - double syntax; - - pthread_t tp; - pthread_t tp_vsl; - int tp_started; - - int expect_exit; - - int cli_fd; - int vcl_nbr; - char *workdir; - char *jail; - char *proto; - - struct vsm *vsm_vsl; - struct vsm *vsm_vsc; - struct vsc *vsc; - int has_a_arg; - - unsigned vsl_tag_count[256]; - - volatile int vsl_rec; - volatile int vsl_idle; -}; - -#define NONSENSE "%XJEIFLH|)Xspa8P" - -static VTAILQ_HEAD(, varnish) varnishes = - VTAILQ_HEAD_INITIALIZER(varnishes); - -/********************************************************************** - * Fatal condition cleanup - * Invalid to call in any code path not followed by vtc_fatal(). - */ - -static void -varnish_fatal_cleanup(const struct varnish *v) -{ - struct pollfd fd[1]; - int n; - - if (!pthread_equal(pthread_self(), vtc_thread)) - return; - - if (!v->tp_started) - return; - - memset(fd, 0, sizeof(fd)); - fd[0].fd = v->fds[0]; - fd[0].events = POLLIN; - - do { - n = poll(fd, sizeof(fd)/sizeof(fd[0]), 10); - if (n == 1 && (fd[0].revents & (POLLHUP|POLLERR)) != 0) { - PTOK(pthread_join(v->tp, NULL)); - break; - } - - if (n == 1) - VTIM_sleep(0.01); - } while (n > 0); -} - -#define varnish_fatal(v, ...) \ - do { \ - varnish_fatal_cleanup((v)); \ - vtc_fatal((v)->vl, __VA_ARGS__); \ - } while (0) - -/********************************************************************** - * Ask a question over CLI - */ - -static enum VCLI_status_e -varnish_ask_cli(const struct varnish *v, const char *cmd, char **repl) -{ - int i; - unsigned retval; - char *r; - - if (cmd != NULL) { - vtc_dump(v->vl, 4, "CLI TX", cmd, -1); - i = write(v->cli_fd, cmd, strlen(cmd)); - if (i != strlen(cmd) && !vtc_stop) - varnish_fatal(v, "CLI write failed (%s) = %u %s", - cmd, errno, strerror(errno)); - i = write(v->cli_fd, "\n", 1); - if (i != 1 && !vtc_stop) - varnish_fatal(v, "CLI write failed (%s) = %u %s", - cmd, errno, strerror(errno)); - } - i = VCLI_ReadResult(v->cli_fd, &retval, &r, vtc_maxdur); - if (i != 0 && !vtc_stop) - varnish_fatal(v, "CLI failed (%s) = %d %u %s", - cmd != NULL ? cmd : "NULL", i, retval, r); - vtc_log(v->vl, 3, "CLI RX %u", retval); - vtc_dump(v->vl, 4, "CLI RX", r, -1); - if (repl != NULL) - *repl = r; - else - free(r); - return ((enum VCLI_status_e)retval); -} - -/********************************************************************** - * - */ - -static void -wait_stopped(const struct varnish *v) -{ - char *r = NULL; - enum VCLI_status_e st; - - vtc_log(v->vl, 3, "wait-stopped"); - while (1) { - st = varnish_ask_cli(v, "status", &r); - if (st != CLIS_OK) - varnish_fatal(v, - "CLI status command failed: %u %s", st, r); - if (!strcmp(r, "Child in state stopped")) { - free(r); - break; - } - free(r); - r = NULL; - VTIM_sleep(0.2); - } -} -/********************************************************************** - * - */ - -static void -wait_running(const struct varnish *v) -{ - char *r = NULL; - enum VCLI_status_e st; - - while (1) { - vtc_log(v->vl, 3, "wait-running"); - st = varnish_ask_cli(v, "status", &r); - if (st != CLIS_OK) - varnish_fatal(v, - "CLI status command failed: %u %s", st, r); - if (!strcmp(r, "Child in state stopped")) - varnish_fatal(v, - "Child stopped before running: %u %s", st, r); - if (!strcmp(r, "Child in state running")) { - free(r); - r = NULL; - st = varnish_ask_cli(v, "debug.listen_address", &r); - if (st != CLIS_OK) - varnish_fatal(v, - "CLI status command failed: %u %s", st, r); - free(r); - break; - } - free(r); - r = NULL; - VTIM_sleep(0.2); - } -} - -/********************************************************************** - * Varnishlog gatherer thread - */ - -static void * -varnishlog_thread(void *priv) -{ - struct varnish *v; - struct VSL_data *vsl; - struct vsm *vsm; - struct VSL_cursor *c; - enum VSL_tag_e tag; - uint64_t vxid; - unsigned len, vs; - const char *tagname, *data; - int type, i, opt; - struct vsb *vsb = NULL; - - CAST_OBJ_NOTNULL(v, priv, VARNISH_MAGIC); - - vsl = VSL_New(); - AN(vsl); - vsm = v->vsm_vsl; - - c = NULL; - opt = 0; - while (v->fds[1] > 0 || c != NULL) { //lint !e845 bug in flint - if (c == NULL) { - if (vtc_error) - break; - VTIM_sleep(0.1); - (void)VSM_Status(vsm); - c = VSL_CursorVSM(vsl, vsm, opt); - if (c == NULL) { - vtc_log(v->vl, 3, "vsl|%s", VSL_Error(vsl)); - VSL_ResetError(vsl); - continue; - } - } - AN(c); - - opt = VSL_COPT_TAIL; - - while (1) { - i = VSL_Next(c); - if (i != 1) - break; - - v->vsl_rec = 1; - - tag = VSL_TAG(c->rec.ptr); - vxid = VSL_ID(c->rec.ptr); - if (tag == SLT__Batch) - continue; - tagname = VSL_tags[tag]; - len = VSL_LEN(c->rec.ptr); - type = VSL_CLIENT(c->rec.ptr) ? - 'c' : VSL_BACKEND(c->rec.ptr) ? - 'b' : '-'; - data = VSL_CDATA(c->rec.ptr); - v->vsl_tag_count[tag]++; - if (VSL_tagflags[tag] & SLT_F_BINARY) { - if (vsb == NULL) - vsb = VSB_new_auto(); - VSB_clear(vsb); - VSB_quote(vsb, data, len, VSB_QUOTE_HEX); - AZ(VSB_finish(vsb)); - /* +2 to skip "0x" */ - vtc_log(v->vl, 4, "vsl| %10ju %-15s %c [%s]", - (uintmax_t)vxid, tagname, type, - VSB_data(vsb) + 2); - } else { - vtc_log(v->vl, 4, "vsl| %10ju %-15s %c %.*s", - (uintmax_t)vxid, tagname, type, (int)len, - data); - } - } - if (i == 0) { - /* Nothing to do but wait */ - v->vsl_idle++; - vs = VSM_Status(vsm) & VSM_WRK_MASK; - if ((vs & ~VSM_WRK_CHANGED) != VSM_WRK_RUNNING) { - /* Abandoned - try reconnect */ - VSL_DeleteCursor(c); - c = NULL; - } else { - VTIM_sleep(0.1); - } - } else if (i == -2) { - /* Abandoned - try reconnect */ - VSL_DeleteCursor(c); - c = NULL; - } else - break; - } - - if (c) - VSL_DeleteCursor(c); - VSL_Delete(vsl); - if (vsb != NULL) - VSB_destroy(&vsb); - - return (NULL); -} - -/********************************************************************** - * Allocate and initialize a varnish - */ - -static struct varnish * -varnish_new(const char *name) -{ - struct varnish *v; - struct vsb *vsb; - char buf[1024]; - - ALLOC_OBJ(v, VARNISH_MAGIC); - AN(v); - REPLACE(v->name, name); - - REPLACE(v->jail, ""); - - v->vl = vtc_logopen("%s", name); - AN(v->vl); - - vsb = macro_expandf(v->vl, "${tmpdir}/%s", name); - AN(vsb); - v->workdir = strdup(VSB_data(vsb)); - AN(v->workdir); - VSB_destroy(&vsb); - - bprintf(buf, "rm -rf %s ; mkdir -p %s", v->workdir, v->workdir); - AZ(system(buf)); - - v->args = VSB_new_auto(); - - v->cli_fd = -1; - VTAILQ_INSERT_TAIL(&varnishes, v, list); - - return (v); -} - -/********************************************************************** - * Delete a varnish instance - */ - -static void -varnish_delete(struct varnish *v) -{ - - CHECK_OBJ_NOTNULL(v, VARNISH_MAGIC); - vtc_logclose(v->vl); - free(v->name); - free(v->jail); - free(v->workdir); - VSB_destroy(&v->args); - if (v->vsc != NULL) - VSC_Destroy(&v->vsc, v->vsm_vsc); - if (v->vsm_vsc != NULL) - VSM_Destroy(&v->vsm_vsc); - if (v->vsm_vsl != NULL) - VSM_Destroy(&v->vsm_vsl); - - /* - * We do not delete the workdir, it may contain stuff people - * want (coredumps, shmlog/stats etc), and trying to divine - * "may want" is just too much trouble. Leave it around and - * nuke it at the start of the next test-run. - */ - - /* XXX: MEMLEAK (?) */ - FREE_OBJ(v); -} - -/********************************************************************** - * Varnish listener - */ - -static void * -varnish_thread(void *priv) -{ - struct varnish *v; - - CAST_OBJ_NOTNULL(v, priv, VARNISH_MAGIC); - return (vtc_record(v->vl, v->fds[0], NULL)); -} - -/********************************************************************** - * Launch a Varnish - */ - -static void -varnish_launch(struct varnish *v) -{ - struct vsb *vsb, *vsb1; - int i, nfd, asock; - char abuf[VTCP_ADDRBUFSIZE]; - char pbuf[VTCP_PORTBUFSIZE]; - char lbuf[PATH_MAX]; - struct pollfd fd[3]; - enum VCLI_status_e u; - const char *err; - char *r = NULL; - - /* Create listener socket */ - asock = VTCP_listen_on(default_listen_addr, NULL, 1, &err); - if (err != NULL) - varnish_fatal(v, "Create CLI listen socket failed: %s", err); - assert(asock > 0); - VTCP_myname(asock, abuf, sizeof abuf, pbuf, sizeof pbuf); - - AZ(VSB_finish(v->args)); - vtc_log(v->vl, 2, "Launch"); - vsb = VSB_new_auto(); - AN(vsb); - VSB_cat(vsb, "cd ${pwd} &&"); - VSB_printf(vsb, " exec varnishd %s -d -n %s -i %s", - v->jail, v->workdir, v->name); - if (macro_isdef(NULL, "varnishd_args_prepend")) { - VSB_putc(vsb, ' '); - macro_cat(v->vl, vsb, "varnishd_args_prepend", NULL); - } - VSB_cat(vsb, VSB_data(params_vsb)); - if (leave_temp) { - VSB_cat(vsb, " -p debug=+vcl_keep"); - VSB_cat(vsb, " -p debug=+vmod_so_keep"); - VSB_cat(vsb, " -p debug=+vsm_keep"); - } - VSB_cat(vsb, " -l 2m"); - VSB_cat(vsb, " -p auto_restart=off"); - VSB_cat(vsb, " -p syslog_cli_traffic=off"); - VSB_cat(vsb, " -p thread_pool_min=10"); - VSB_cat(vsb, " -p debug=+vtc_mode"); - VSB_cat(vsb, " -p vsl_mask=+Debug,+H2RxHdr,+H2RxBody"); - VSB_cat(vsb, " -p h2_initial_window_size=1m"); - VSB_cat(vsb, " -p h2_rx_window_low_water=64k"); - if (!v->has_a_arg) { - VSB_printf(vsb, " -a '%s'", default_listen_addr); - if (v->proto != NULL) - VSB_printf(vsb, ",%s", v->proto); - } - VSB_printf(vsb, " -M '%s %s'", abuf, pbuf); - VSB_printf(vsb, " -P %s/varnishd.pid", v->workdir); - if (vmod_path != NULL) - VSB_printf(vsb, " -p vmod_path=%s", vmod_path); - VSB_printf(vsb, " %s", VSB_data(v->args)); - if (macro_isdef(NULL, "varnishd_args_append")) { - VSB_putc(vsb, ' '); - macro_cat(v->vl, vsb, "varnishd_args_append", NULL); - } - AZ(VSB_finish(vsb)); - vtc_log(v->vl, 3, "CMD: %s", VSB_data(vsb)); - vsb1 = macro_expand(v->vl, VSB_data(vsb)); - AN(vsb1); - VSB_destroy(&vsb); - vsb = vsb1; - vtc_log(v->vl, 3, "CMD: %s", VSB_data(vsb)); - AZ(pipe(&v->fds[0])); - AZ(pipe(&v->fds[2])); - v->pid = fork(); - assert(v->pid >= 0); - if (v->pid == 0) { - AZ(dup2(v->fds[0], 0)); - assert(dup2(v->fds[3], 1) == 1); - assert(dup2(1, 2) == 2); - closefd(&v->fds[0]); - closefd(&v->fds[1]); - closefd(&v->fds[2]); - closefd(&v->fds[3]); - VSUB_closefrom(STDERR_FILENO + 1); - AZ(execl("/bin/sh", "/bin/sh", "-c", VSB_data(vsb), (char*)0)); - exit(1); - } else { - vtc_log(v->vl, 3, "PID: %ld", (long)v->pid); - macro_def(v->vl, v->name, "pid", "%ld", (long)v->pid); - macro_def(v->vl, v->name, "name", "%s", v->workdir); - } - closefd(&v->fds[0]); - closefd(&v->fds[3]); - v->fds[0] = v->fds[2]; - v->fds[2] = v->fds[3] = -1; - VSB_destroy(&vsb); - AZ(v->tp_started); - v->tp_started = 1; - PTOK(pthread_create(&v->tp, NULL, varnish_thread, v)); - - /* Wait for the varnish to call home */ - memset(fd, 0, sizeof fd); - fd[0].fd = asock; - fd[0].events = POLLIN; - fd[1].fd = v->fds[1]; - fd[1].events = POLLIN; - fd[2].fd = v->fds[2]; - fd[2].events = POLLIN; - i = poll(fd, 2, (int)(vtc_maxdur * 1000 / 3)); - vtc_log(v->vl, 4, "CLIPOLL %d 0x%x 0x%x 0x%x", - i, fd[0].revents, fd[1].revents, fd[2].revents); - if (i == 0) - varnish_fatal(v, "FAIL timeout waiting for CLI connection"); - if (fd[1].revents & POLLHUP) - varnish_fatal(v, "FAIL debug pipe closed"); - if (!(fd[0].revents & POLLIN)) - varnish_fatal(v, "FAIL CLI connection wait failure"); - nfd = accept(asock, NULL, NULL); - closefd(&asock); - if (nfd < 0) - varnish_fatal(v, "FAIL no CLI connection accepted"); - - v->cli_fd = nfd; - - vtc_log(v->vl, 3, "CLI connection fd = %d", v->cli_fd); - assert(v->cli_fd >= 0); - - /* Receive the banner or auth response */ - u = varnish_ask_cli(v, NULL, &r); - if (vtc_error) - return; - if (u != CLIS_AUTH) - varnish_fatal(v, "CLI auth demand expected: %u %s", u, r); - - bprintf(lbuf, "%s/_.secret", v->workdir); - nfd = open(lbuf, O_RDONLY); - assert(nfd >= 0); - - assert(sizeof lbuf >= CLI_AUTH_RESPONSE_LEN + 7); - bstrcpy(lbuf, "auth "); - VCLI_AuthResponse(nfd, r, lbuf + 5); - closefd(&nfd); - free(r); - r = NULL; - strcat(lbuf, "\n"); - - u = varnish_ask_cli(v, lbuf, &r); - if (vtc_error) - return; - if (u != CLIS_OK) - varnish_fatal(v, "CLI auth command failed: %u %s", u, r); - free(r); - - v->vsm_vsc = VSM_New(); - AN(v->vsm_vsc); - v->vsc = VSC_New(); - AN(v->vsc); - assert(VSM_Arg(v->vsm_vsc, 'n', v->workdir) > 0); - AZ(VSM_Attach(v->vsm_vsc, -1)); - - v->vsm_vsl = VSM_New(); - assert(VSM_Arg(v->vsm_vsl, 'n', v->workdir) > 0); - AZ(VSM_Attach(v->vsm_vsl, -1)); - - PTOK(pthread_create(&v->tp_vsl, NULL, varnishlog_thread, v)); -} - -#define VARNISH_LAUNCH(v) \ - do { \ - CHECK_OBJ_NOTNULL(v, VARNISH_MAGIC); \ - if (v->cli_fd < 0) \ - varnish_launch(v); \ - if (vtc_error) \ - return; \ - } while (0) - -/********************************************************************** - * Start a Varnish - */ - -static void -varnish_listen(const struct varnish *v, char *la) -{ - const char *a, *p, *n, *n2; - char m[64], s[256]; - unsigned first; - - n2 = ""; - first = 1; - - while (*la != '\0') { - n = la; - la = strchr(la, ' '); - AN(la); - *la = '\0'; - a = ++la; - la = strchr(la, ' '); - AN(la); - *la = '\0'; - p = ++la; - la = strchr(la, '\n'); - AN(la); - *la = '\0'; - la++; - - AN(*n); - AN(*a); - AN(*p); - - if (*p == '-') { - bprintf(s, "%s", a); - a = "0.0.0.0"; - p = "0"; - } else if (strchr(a, ':')) { - bprintf(s, "[%s]:%s", a, p); - } else { - bprintf(s, "%s:%s", a, p); - } - - if (first) { - vtc_log(v->vl, 2, "Listen on %s %s", a, p); - macro_def(v->vl, v->name, "addr", "%s", a); - macro_def(v->vl, v->name, "port", "%s", p); - macro_def(v->vl, v->name, "sock", "%s", s); - first = 0; - } - - if (!strcmp(n, n2)) - continue; - - bprintf(m, "%s_addr", n); - macro_def(v->vl, v->name, m, "%s", a); - bprintf(m, "%s_port", n); - macro_def(v->vl, v->name, m, "%s", p); - bprintf(m, "%s_sock", n); - macro_def(v->vl, v->name, m, "%s", s); - n2 = n; - } -} - -static void -varnish_start(struct varnish *v) -{ - enum VCLI_status_e u; - char *resp = NULL; - - VARNISH_LAUNCH(v); - vtc_log(v->vl, 2, "Start"); - u = varnish_ask_cli(v, "start", &resp); - if (vtc_error) - return; - if (u != CLIS_OK) - varnish_fatal(v, "CLI start command failed: %u %s", u, resp); - wait_running(v); - free(resp); - resp = NULL; - u = varnish_ask_cli(v, "debug.xid 1000", &resp); - if (vtc_error) - return; - if (u != CLIS_OK) - varnish_fatal(v, "CLI debug.xid command failed: %u %s", - u, resp); - free(resp); - resp = NULL; - u = varnish_ask_cli(v, "debug.listen_address", &resp); - if (vtc_error) - return; - if (u != CLIS_OK) - varnish_fatal(v, - "CLI debug.listen_address command failed: %u %s", u, resp); - varnish_listen(v, resp); - free(resp); - /* Wait for vsl logging to get underway */ - while (v->vsl_rec == 0) - VTIM_sleep(.1); -} - -/********************************************************************** - * Stop a Varnish - */ - -static void -varnish_stop(struct varnish *v) -{ - - VARNISH_LAUNCH(v); - vtc_log(v->vl, 2, "Stop"); - (void)varnish_ask_cli(v, "stop", NULL); - wait_stopped(v); -} - -/********************************************************************** - * Cleanup - */ - -static void -varnish_cleanup(struct varnish *v) -{ - void *p; - - /* Close the CLI connection */ - closefd(&v->cli_fd); - - /* Close the STDIN connection. */ - closefd(&v->fds[1]); - - /* Wait until STDOUT+STDERR closes */ - AN(v->tp_started); - PTOK(pthread_join(v->tp, &p)); - closefd(&v->fds[0]); - v->tp_started = 0; - - /* Pick up the VSL thread */ - PTOK(pthread_join(v->tp_vsl, &p)); - - vtc_wait4(v->vl, v->pid, v->expect_exit, 0, 0); - v->pid = 0; -} - -/********************************************************************** - * Wait for a Varnish - */ - -static void -varnish_wait(struct varnish *v) -{ - if (v->cli_fd < 0) - return; - - vtc_log(v->vl, 2, "Wait"); - - if (!vtc_error) { - /* Do a backend.list to log if child is still running */ - (void)varnish_ask_cli(v, "backend.list", NULL); - } - - /* Then stop it */ - varnish_stop(v); - - if (varnish_ask_cli(v, "panic.show", NULL) != CLIS_CANT) - varnish_fatal(v, "Unexpected panic"); - - varnish_cleanup(v); -} - - -/********************************************************************** - * Ask a CLI JSON question - */ - -static void -varnish_cli_json(struct varnish *v, const char *cli) -{ - enum VCLI_status_e u; - char *resp = NULL; - const char *errptr; - struct vjsn *vj; - - VARNISH_LAUNCH(v); - u = varnish_ask_cli(v, cli, &resp); - vtc_log(v->vl, 2, "CLI %03u <%s>", u, cli); - if (u != CLIS_OK) - varnish_fatal(v, - "FAIL CLI response %u expected %u", u, CLIS_OK); - vj = vjsn_parse(resp, &errptr); - if (vj == NULL) - varnish_fatal(v, "FAIL CLI, not good JSON: %s", errptr); - vjsn_delete(&vj); - free(resp); -} - -/********************************************************************** - * Ask a CLI question - */ - -static void -varnish_cli(struct varnish *v, const char *cli, unsigned exp, const char *re) -{ - enum VCLI_status_e u; - struct vsb vsb[1]; - vre_t *vre = NULL; - char *resp = NULL, errbuf[VRE_ERROR_LEN]; - int err, erroff; - - VARNISH_LAUNCH(v); - if (re != NULL) { - vre = VRE_compile(re, 0, &err, &erroff, 1); - if (vre == NULL) { - AN(VSB_init(vsb, errbuf, sizeof errbuf)); - AZ(VRE_error(vsb, err)); - AZ(VSB_finish(vsb)); - VSB_fini(vsb); - varnish_fatal(v, "Illegal regexp: %s (@%d)", - errbuf, erroff); - } - } - u = varnish_ask_cli(v, cli, &resp); - vtc_log(v->vl, 2, "CLI %03u <%s>", u, cli); - if (exp != 0 && exp != (unsigned)u) - varnish_fatal(v, "FAIL CLI response %u expected %u", u, exp); - if (vre != NULL) { - err = VRE_match(vre, resp, 0, 0, NULL); - if (err < 1) { - AN(VSB_init(vsb, errbuf, sizeof errbuf)); - AZ(VRE_error(vsb, err)); - AZ(VSB_finish(vsb)); - VSB_fini(vsb); - varnish_fatal(v, "Expect failed (%s)", errbuf); - } - VRE_free(&vre); - } - free(resp); -} - -/********************************************************************** - * Load a VCL program - */ - -static void -varnish_vcl(struct varnish *v, const char *vcl, int fail, char **resp) -{ - struct vsb *vsb; - enum VCLI_status_e u; - - VARNISH_LAUNCH(v); - vsb = VSB_new_auto(); - AN(vsb); - - VSB_printf(vsb, "vcl.inline vcl%d << %s\nvcl %.1f;\n%s\n%s\n", - ++v->vcl_nbr, NONSENSE, v->syntax, vcl, NONSENSE); - AZ(VSB_finish(vsb)); - - u = varnish_ask_cli(v, VSB_data(vsb), resp); - if (u == CLIS_OK) { - VSB_clear(vsb); - VSB_printf(vsb, "vcl.use vcl%d", v->vcl_nbr); - AZ(VSB_finish(vsb)); - u = varnish_ask_cli(v, VSB_data(vsb), NULL); - } - if (u == CLIS_OK && fail) { - VSB_destroy(&vsb); - varnish_fatal(v, "VCL compilation succeeded expected failure"); - } else if (u != CLIS_OK && !fail) { - VSB_destroy(&vsb); - varnish_fatal(v, "VCL compilation failed expected success"); - } else if (fail) - vtc_log(v->vl, 2, "VCL compilation failed (as expected)"); - VSB_destroy(&vsb); -} - -/********************************************************************** - * Load a VCL program prefixed by backend decls for our servers - */ - -static void -varnish_vclbackend(struct varnish *v, const char *vcl) -{ - struct vsb *vsb, *vsb2; - enum VCLI_status_e u; - - VARNISH_LAUNCH(v); - vsb = VSB_new_auto(); - AN(vsb); - - vsb2 = VSB_new_auto(); - AN(vsb2); - - VSB_printf(vsb2, "vcl %.1f;\n", v->syntax); - - cmd_server_gen_vcl(vsb2); - - AZ(VSB_finish(vsb2)); - - VSB_printf(vsb, "vcl.inline vcl%d << %s\n%s\n%s\n%s\n", - ++v->vcl_nbr, NONSENSE, VSB_data(vsb2), vcl, NONSENSE); - AZ(VSB_finish(vsb)); - - u = varnish_ask_cli(v, VSB_data(vsb), NULL); - if (u != CLIS_OK) { - VSB_destroy(&vsb); - VSB_destroy(&vsb2); - varnish_fatal(v, "FAIL VCL does not compile"); - } - VSB_clear(vsb); - VSB_printf(vsb, "vcl.use vcl%d", v->vcl_nbr); - AZ(VSB_finish(vsb)); - u = varnish_ask_cli(v, VSB_data(vsb), NULL); - assert(u == CLIS_OK); - VSB_destroy(&vsb); - VSB_destroy(&vsb2); -} - -/********************************************************************** - */ - -struct dump_priv { - const char *arg; - const struct varnish *v; -}; - -static int -do_stat_dump_cb(void *priv, const struct VSC_point * const pt) -{ - const struct varnish *v; - struct dump_priv *dp; - uint64_t u; - - if (pt == NULL) - return (0); - dp = priv; - v = dp->v; - - if (strcmp(pt->ctype, "uint64_t")) - return (0); - u = VSC_Value(pt); - - if (strcmp(dp->arg, "*")) { - if (fnmatch(dp->arg, pt->name, 0)) - return (0); - } - - vtc_log(v->vl, 4, "VSC %s %ju", pt->name, (uintmax_t)u); - return (0); -} - -static void -varnish_vsc(struct varnish *v, const char *arg) -{ - struct dump_priv dp; - - VARNISH_LAUNCH(v); - memset(&dp, 0, sizeof dp); - dp.v = v; - dp.arg = arg; - (void)VSM_Status(v->vsm_vsc); - (void)VSC_Iter(v->vsc, v->vsm_vsc, do_stat_dump_cb, &dp); -} - -/********************************************************************** - * Check statistics - */ - -struct stat_arg { - const char *pattern; - uintmax_t val; - unsigned good; -}; - -struct stat_priv { - struct stat_arg lhs; - struct stat_arg rhs; -}; - -static int -stat_match(const char *pattern, const char *name) -{ - - if (strchr(pattern, '.') == NULL) { - if (fnmatch("MAIN.*", name, 0)) - return (FNM_NOMATCH); - name += 5; - } - return (fnmatch(pattern, name, 0)); -} - -static int -do_expect_cb(void *priv, const struct VSC_point * const pt) -{ - struct stat_priv *sp = priv; - - if (pt == NULL) - return (0); - - if (!sp->lhs.good && stat_match(sp->lhs.pattern, pt->name) == 0) { - AZ(strcmp(pt->ctype, "uint64_t")); - AN(pt->ptr); - sp->lhs.val = VSC_Value(pt); - sp->lhs.good = 1; - } - - if (sp->rhs.pattern == NULL) { - sp->rhs.good = 1; - } else if (!sp->rhs.good && - stat_match(sp->rhs.pattern, pt->name) == 0) { - AZ(strcmp(pt->ctype, "uint64_t")); - AN(pt->ptr); - sp->rhs.val = VSC_Value(pt); - sp->rhs.good = 1; - } - - return (sp->lhs.good && sp->rhs.good); -} - -/********************************************************************** - */ - -static void -varnish_expect(struct varnish *v, char * const *av) -{ - struct stat_priv sp; - int good, i, not; - uintmax_t u; - char *l, *p; - - VARNISH_LAUNCH(v); - ZERO_OBJ(&sp, sizeof sp); - l = av[0]; - not = (*l == '!'); - if (not) { - l++; - AZ(av[1]); - } else { - AN(av[1]); - AN(av[2]); - u = strtoumax(av[2], &p, 0); - if (u != UINTMAX_MAX && *p == '\0') - sp.rhs.val = u; - else - sp.rhs.pattern = av[2]; - } - - sp.lhs.pattern = l; - - for (i = 0; i < 50; i++, (void)usleep(100000)) { - (void)VSM_Status(v->vsm_vsc); - sp.lhs.good = sp.rhs.good = 0; - good = VSC_Iter(v->vsc, v->vsm_vsc, do_expect_cb, &sp); - if (!good) - good = -2; - if (good < 0) - continue; - - if (not) - varnish_fatal(v, "Found (not expected): %s", l); - - good = -1; - if (!strcmp(av[1], "==")) good = (sp.lhs.val == sp.rhs.val); - if (!strcmp(av[1], "!=")) good = (sp.lhs.val != sp.rhs.val); - if (!strcmp(av[1], ">" )) good = (sp.lhs.val > sp.rhs.val); - if (!strcmp(av[1], "<" )) good = (sp.lhs.val < sp.rhs.val); - if (!strcmp(av[1], ">=")) good = (sp.lhs.val >= sp.rhs.val); - if (!strcmp(av[1], "<=")) good = (sp.lhs.val <= sp.rhs.val); - if (good == -1) - varnish_fatal(v, "comparison %s unknown", av[1]); - if (good) - break; - } - if (good == -1) { - varnish_fatal(v, "VSM error: %s", VSM_Error(v->vsm_vsc)); - } - if (good == -2) { - if (not) { - vtc_log(v->vl, 2, "not found (as expected): %s", l); - return; - } - varnish_fatal(v, "stats field %s unknown", - sp.lhs.good ? sp.rhs.pattern : sp.lhs.pattern); - } - - if (good == 1) { - vtc_log(v->vl, 2, "as expected: %s (%ju) %s %s (%ju)", - av[0], sp.lhs.val, av[1], av[2], sp.rhs.val); - } else { - varnish_fatal(v, "Not true: %s (%ju) %s %s (%ju)", - av[0], sp.lhs.val, av[1], av[2], sp.rhs.val); - } -} - -static void -vsl_catchup(struct varnish *v) -{ - int vsl_idle; - - VARNISH_LAUNCH(v); - vsl_idle = v->vsl_idle; - while (!vtc_error && vsl_idle == v->vsl_idle) - VTIM_sleep(0.1); -} - -/* SECTION: varnish varnish - * - * Define and interact with varnish instances. - * - * To define a Varnish server, you'll use this syntax:: - * - * varnish vNAME [-arg STRING] [-vcl STRING] [-vcl+backend STRING] - * [-errvcl STRING STRING] [-jail STRING] [-proto PROXY] - * - * The first ``varnish vNAME`` invocation will start the varnishd master - * process in the background, waiting for the ``-start`` switch to actually - * start the child. - * - * Types used in the description below: - * - * PATTERN - * is a 'glob' style pattern (ie: fnmatch(3)) as used in shell filename - * expansion. - * - * Arguments: - * - * vNAME - * Identify the Varnish server with a string, it must starts with 'v'. - * - * \-arg STRING - * Pass an argument to varnishd, for example "-h simple_list". - * - * If the ${varnishd_args_prepend} or ${varnishd_args_append} macros are - * defined, they are expanded and inserted before / appended to the - * varnishd command line as constructed by varnishtest, before the - * command line itself is expanded. This enables tweaks to the varnishd - * command line without editing test cases. This macros can be defined - * using the ``-D`` option for varnishtest. - * - * \-vcl STRING - * Specify the VCL to load on this Varnish instance. You'll probably - * want to use multi-lines strings for this ({...}). - * - * \-vcl+backend STRING - * Do the exact same thing as -vcl, but adds the definition block of - * known backends (ie. already defined). - * - * \-errvcl STRING1 STRING2 - * Load STRING2 as VCL, expecting it to fail, and Varnish to send an - * error string matching STRING1 - * - * \-jail STRING - * Look at ``man varnishd`` (-j) for more information. - * - * \-proto PROXY - * Have Varnish use the proxy protocol. Note that PROXY here is the - * actual string. - * - * You can decide to start the Varnish instance and/or wait for several events:: - * - * varnish vNAME [-start] [-wait] [-wait-running] [-wait-stopped] - * - * \-start - * Start the child process. - * - * Once successfully started, the following macros are available for - * the default listen address: ``${vNAME_addr}``, ``${vNAME_port}`` - * and ``${vNAME_sock}``. Additional macros are available, including - * the listen address name for each address vNAME listens to, like for - * example: ``${vNAME_a0_addr}``. - * - * \-stop - * Stop the child process. - * - * \-syntax - * Set the VCL syntax level for this command (default: 4.1) - * - * \-wait - * Wait for that instance to terminate. - * - * \-wait-running - * Wait for the Varnish child process to be started. - * - * \-wait-stopped - * Wait for the Varnish child process to stop. - * - * \-cleanup - * Once Varnish is stopped, clean everything after it. This is only used - * in very few tests and you should never need it. - * - * \-expectexit NUMBER - * Expect varnishd to exit(3) with this value - * - * Once Varnish is started, you can talk to it (as you would through - * ``varnishadm``) with these additional switches:: - * - * varnish vNAME [-cli STRING] [-cliok STRING] [-clierr STRING] - * [-clijson STRING] - * - * \-cli STRING|-cliok STRING|-clierr STATUS STRING|-cliexpect REGEXP STRING - * All four of these will send STRING to the CLI, the only difference - * is what they expect the result to be. -cli doesn't expect - * anything, -cliok expects 200, -clierr expects STATUS, and - * -cliexpect expects the REGEXP to match the returned response. - * - * \-clijson STRING - * Send STRING to the CLI, expect success (CLIS_OK/200) and check - * that the response is parsable JSON. - * - * It is also possible to interact with its shared memory (as you would - * through tools like ``varnishstat``) with additional switches: - * - * \-expect \!PATTERN|PATTERN OP NUMBER|PATTERN OP PATTERN - * Look into the VSM and make sure the first VSC counter identified by - * PATTERN has a correct value. OP can be ==, >, >=, <, <=. For - * example:: - * - * varnish v1 -expect SM?.s1.g_space > 1000000 - * varnish v1 -expect cache_hit >= cache_hit_grace - * - * In the \! form the test fails if a counter matches PATTERN. - * - * The ``MAIN.`` namespace can be omitted from PATTERN. - * - * The test takes up to 5 seconds before timing out. - * - * \-vsc PATTERN - * Dump VSC counters matching PATTERN. - * - * \-vsl_catchup - * Wait until the logging thread has idled to make sure that all - * the generated log is flushed - */ - -void -cmd_varnish(CMD_ARGS) -{ - struct varnish *v, *v2; - - (void)priv; - - if (av == NULL) { - /* Reset and free */ - VTAILQ_FOREACH_SAFE(v, &varnishes, list, v2) { - if (v->cli_fd >= 0) - varnish_wait(v); - VTAILQ_REMOVE(&varnishes, v, list); - varnish_delete(v); - } - return; - } - - AZ(strcmp(av[0], "varnish")); - av++; - - VTC_CHECK_NAME(vl, av[0], "Varnish", 'v'); - VTAILQ_FOREACH(v, &varnishes, list) - if (!strcmp(v->name, av[0])) - break; - if (v == NULL) - v = varnish_new(av[0]); - av++; - v->syntax = 4.1; - - for (; *av != NULL; av++) { - if (vtc_error) - break; - if (!strcmp(*av, "-arg")) { - AN(av[1]); - AZ(v->pid); - VSB_cat(v->args, " "); - VSB_cat(v->args, av[1]); - if (av[1][0] == '-' && av[1][1] == 'a') - v->has_a_arg = 1; - av++; - continue; - } - if (!strcmp(*av, "-cleanup")) { - AZ(av[1]); - varnish_cleanup(v); - continue; - } - if (!strcmp(*av, "-cli")) { - AN(av[1]); - varnish_cli(v, av[1], 0, NULL); - av++; - continue; - } - if (!strcmp(*av, "-clierr")) { - AN(av[1]); - AN(av[2]); - varnish_cli(v, av[2], atoi(av[1]), NULL); - av += 2; - continue; - } - if (!strcmp(*av, "-cliexpect")) { - AN(av[1]); - AN(av[2]); - varnish_cli(v, av[2], 0, av[1]); - av += 2; - continue; - } - if (!strcmp(*av, "-clijson")) { - AN(av[1]); - varnish_cli_json(v, av[1]); - av++; - continue; - } - if (!strcmp(*av, "-cliok")) { - AN(av[1]); - varnish_cli(v, av[1], (unsigned)CLIS_OK, NULL); - av++; - continue; - } - if (!strcmp(*av, "-errvcl")) { - char *r = NULL; - AN(av[1]); - AN(av[2]); - varnish_vcl(v, av[2], 1, &r); - if (strstr(r, av[1]) == NULL) - varnish_fatal(v, - "Did not find expected string: (\"%s\")", - av[1]); - else - vtc_log(v->vl, 3, - "Found expected string: (\"%s\")", - av[1]); - free(r); - av += 2; - continue; - } - if (!strcmp(*av, "-expect")) { - av++; - varnish_expect(v, av); - av += 2; - continue; - } - if (!strcmp(*av, "-expectexit")) { - v->expect_exit = strtoul(av[1], NULL, 0); - av++; - continue; - } - if (!strcmp(*av, "-jail")) { - AN(av[1]); - AZ(v->pid); - REPLACE(v->jail, av[1]); - av++; - continue; - } - if (!strcmp(*av, "-proto")) { - AN(av[1]); - AZ(v->pid); - REPLACE(v->proto, av[1]); - av++; - continue; - } - if (!strcmp(*av, "-start")) { - varnish_start(v); - continue; - } - if (!strcmp(*av, "-stop")) { - varnish_stop(v); - continue; - } - if (!strcmp(*av, "-syntax")) { - AN(av[1]); - v->syntax = strtod(av[1], NULL); - av++; - continue; - } - if (!strcmp(*av, "-vcl")) { - AN(av[1]); - varnish_vcl(v, av[1], 0, NULL); - av++; - continue; - } - if (!strcmp(*av, "-vcl+backend")) { - AN(av[1]); - varnish_vclbackend(v, av[1]); - av++; - continue; - } - if (!strcmp(*av, "-vsc")) { - AN(av[1]); - varnish_vsc(v, av[1]); - av++; - continue; - } - if (!strcmp(*av, "-wait-stopped")) { - wait_stopped(v); - continue; - } - if (!strcmp(*av, "-wait-running")) { - wait_running(v); - continue; - } - if (!strcmp(*av, "-wait")) { - varnish_wait(v); - continue; - } - if (!strcmp(*av, "-vsl_catchup")) { - vsl_catchup(v); - continue; - } - varnish_fatal(v, "Unknown varnish argument: %s", *av); - } -} - -#endif /* VTEST_WITH_VTC_VARNISH */ diff --git a/bin/varnishtest/vtc_vsm.c b/bin/varnishtest/vtc_vsm.c deleted file mode 100644 index 3a9c2cfac..000000000 --- a/bin/varnishtest/vtc_vsm.c +++ /dev/null @@ -1,296 +0,0 @@ -/*- - * Copyright (c) 2023 Varnish Software AS - * All rights reserved. - * - * Author: Dridi Boukelmoune - * - * SPDX-License-Identifier: BSD-2-Clause - * - * Redistribution and use in source and binary forms, with or without - * modification, are permitted provided that the following conditions - * are met: - * 1. Redistributions of source code must retain the above copyright - * notice, this list of conditions and the following disclaimer. - * 2. Redistributions in binary form must reproduce the above copyright - * notice, this list of conditions and the following disclaimer in the - * documentation and/or other materials provided with the distribution. - * - * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND - * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE - * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE - * ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE - * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL - * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS - * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) - * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY - * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF - * SUCH DAMAGE. - */ - -#ifdef VTEST_WITH_VTC_VSM - -#include "config.h" - -#include - -#include -#include -#include - -#include "vapi/vsm.h" - -#include "vtc.h" -#include "vav.h" - -struct vtc_vsm { - unsigned magic; -#define VTC_VSM_MAGIC 0x5ca77a36 - VTAILQ_ENTRY(vtc_vsm) list; - - char *name; - char *n_arg; - struct vtclog *vl; - struct vsm *vsm; -}; - -static VTAILQ_HEAD(, vtc_vsm) vsms = VTAILQ_HEAD_INITIALIZER(vsms); - -static struct vtc_vsm * -vsm_new(const char *name) -{ - struct vtc_vsm *m; - - ALLOC_OBJ(m, VTC_VSM_MAGIC); - AN(m); - REPLACE(m->name, name); - REPLACE(m->n_arg, "${v1_name}"); - m->vl = vtc_logopen("%s", name); - - VTAILQ_INSERT_TAIL(&vsms, m, list); - return (m); -} - -static void -vsm_detach(struct vtc_vsm *m) -{ - - CHECK_OBJ_NOTNULL(m, VTC_VSM_MAGIC); - if (m->vsm == NULL) - vtc_fatal(m->vl, "Cannot detach unattached VSM"); - vtc_log(m->vl, 3, "Detaching from VSM"); - VSM_Destroy(&m->vsm); - AZ(m->vsm); -} - -static void -vsm_attach(struct vtc_vsm *m) -{ - struct vsb *n_arg; - - CHECK_OBJ_NOTNULL(m, VTC_VSM_MAGIC); - if (m->vsm != NULL) - vsm_detach(m); - - n_arg = macro_expandf(m->vl, "%s", m->n_arg); - if (n_arg == NULL) - vtc_fatal(m->vl, "Could not expand -n argument"); - vtc_log(m->vl, 3, "Attaching to VSM: %s", VSB_data(n_arg)); - - m->vsm = VSM_New(); - AN(m->vsm); - - if (VSM_Arg(m->vsm, 'n', VSB_data(n_arg)) <= 0) - vtc_fatal(m->vl, "-n argument error: %s", VSM_Error(m->vsm)); - if (VSM_Attach(m->vsm, -1)) - vtc_fatal(m->vl, "VSM_Attach: %s", VSM_Error(m->vsm)); - - VSB_destroy(&n_arg); -} - -static void -vsm_delete(struct vtc_vsm *m) -{ - - CHECK_OBJ_NOTNULL(m, VTC_VSM_MAGIC); - if (m->vsm != NULL) - vsm_detach(m); - REPLACE(m->name, NULL); - REPLACE(m->n_arg, NULL); - vtc_logclose(m->vl); - FREE_OBJ(m); -} - -#define STATUS_BITS() \ - STATUS_BIT(VSM_MGT_RUNNING, mgt-running); \ - STATUS_BIT(VSM_MGT_CHANGED, mgt-changed); \ - STATUS_BIT(VSM_MGT_RESTARTED, mgt-restarted); \ - STATUS_BIT(VSM_WRK_RUNNING, wrk-running); \ - STATUS_BIT(VSM_WRK_CHANGED, wrk-changed); \ - STATUS_BIT(VSM_WRK_RESTARTED, wrk-restarted); - -static void -vsm_expect_status(struct vtc_vsm *m, const char *exp) -{ - struct vsb *stat; - const char *sep; - char **av; - unsigned bstat, bexp, bfound; - int argc, i; - - CHECK_OBJ_NOTNULL(m, VTC_VSM_MAGIC); - if (exp == NULL) - vtc_fatal(m->vl, "Missing expected status"); - - if (m->vsm == NULL) - vsm_attach(m); - - av = VAV_Parse(exp, &argc, ARGV_COMMA|ARGV_NOESC); - AN(av); - - bexp = 0; - for (i = 1; i < argc; i++) { -#define STATUS_BIT(b, s) \ - if (!strcasecmp(#s, av[i])) { \ - bexp |= b; \ - continue; \ - } - STATUS_BITS() -#undef STATUS_BIT - vtc_fatal(m->vl, "Unknown status bit: %s", av[i]); - } - VAV_Free(av); - - bfound = 0; - bstat = VSM_Status(m->vsm); - stat = VSB_new_auto(); - AN(stat); - sep = ""; -#define STATUS_BIT(b, s) \ - if (bstat & b) { \ - VSB_cat(stat, sep); \ - VSB_cat(stat, #s); \ - bfound |= b; \ - sep = ","; \ - } - STATUS_BITS(); -#undef STATUS_BIT - - if (bstat != bfound) { - vtc_fatal(m->vl, "VSM status bits not handled: %x", - bstat & ~bfound); - } - - if (bstat != bexp) { - AZ(VSB_finish(stat)); - vtc_fatal(m->vl, "Expected VSM status '%s' got '%s'", - exp, VSB_data(stat)); - } - - VSB_destroy(&stat); - vtc_log(m->vl, 4, "Found expected VSM status"); -} - -/* SECTION: vsm vsm - * - * Interact with the shared memory of a varnish instance. - * - * To define a VSM consumer, use this syntax:: - * - * vsm mNAME [-n STRING] - * - * Arguments: - * - * mNAME - * Identify the VSM consumer, it must starts with 'm'. - * - * \-n STRING - * Choose the working directory of the varnish instance. By default - * a VSM consumer connects to ``${v1_name}``. - * - * \-attach - * Attach to a new varnish instance. Implicitly detach from the - * current varnish instance if applicable. - * - * \-detach - * Detach from the current varnish instance. - * - * \-expect-status STRING - * Check that the status of VSM matches the list of status flags from - * STRING. The expected status is represented as a comma-separated - * list of flags. The list of flags in STRING is not sensitive to the - * order of flags. - * - * The available flags are: - * - * - ``mgt-running`` - * - ``mgt-changed`` - * - ``mgt-restarted`` - * - ``wrk-running`` - * - ``wrk-changed`` - * - ``wrk-restarted`` - * - * Expecting a status automatically attaches to the varnish instance - * if that was not already the case. - */ - -void -cmd_vsm(CMD_ARGS) -{ - struct vtc_vsm *m, *m2; - - (void)priv; - - if (av == NULL) { - /* Reset and free */ - VTAILQ_FOREACH_SAFE(m, &vsms, list, m2) { - CHECK_OBJ_NOTNULL(m, VTC_VSM_MAGIC); - VTAILQ_REMOVE(&vsms, m, list); - vsm_delete(m); - } - return; - } - - AZ(strcmp(av[0], "vsm")); - av++; - - VTC_CHECK_NAME(vl, av[0], "VSM", 'm'); - VTAILQ_FOREACH(m, &vsms, list) { - if (!strcmp(m->name, av[0])) - break; - } - - if (m == NULL) { - m = vsm_new(*av); - AN(m); - } - av++; - - for (; *av != NULL; av++) { - if (vtc_error) - break; - if (!strcmp(*av, "-attach")) { - vsm_attach(m); - continue; - } - if (!strcmp(*av, "-detach")) { - vsm_detach(m); - continue; - } - if (!strcmp(*av, "-expect-status")) { - vsm_expect_status(m, av[1]); - av++; - continue; - } - if (!strcmp(*av, "-n")) { - if (av[1] == NULL) - vtc_fatal(m->vl, "Missing -n argument"); - REPLACE(m->n_arg, av[1]); - av++; - continue; - } - vtc_fatal(vl, "Unknown VSM argument: %s", *av); - } -} - -#endif /* VTEST_WITH_VTC_VSM */ diff --git a/bin/varnishtest/vtest2 b/bin/varnishtest/vtest2 new file mode 160000 index 000000000..a5e1d0a58 --- /dev/null +++ b/bin/varnishtest/vtest2 @@ -0,0 +1 @@ +Subproject commit a5e1d0a58d6ae87b9c2f5fa8d10bb0880a409c4d diff --git a/configure.ac b/configure.ac index a0701f4d3..eeb0747bc 100644 --- a/configure.ac +++ b/configure.ac @@ -5,6 +5,9 @@ Copyright 2010-2025 UPLEX - Nils Goroll Systemoptimierung]) AC_REVISION([$Id$]) AC_INIT([Varnish], [trunk], [varnish-dev at varnish-cache.org]) AC_CONFIG_SRCDIR(include/miniobj.h) +if ! test -f "${srcdir}/bin/varnishtest/vtest2/src/vtc_main.c" ; then + AC_MSG_ERROR([vtest2 seems to be missing, use "git clone -r" or "git submodule update --init"]) +fi AC_CONFIG_HEADERS([config.h]) AC_CONFIG_MACRO_DIR([m4]) AC_CONFIG_AUX_DIR([build-aux]) @@ -883,7 +886,7 @@ AC_DEFINE_UNQUOTED([VCC_CC],"$VCC_CC",[C compiler command line for VCL code]) AC_DEFINE_UNQUOTED([VCC_WARN],"$VCC_WARN",[C compiler warnings for VCL code]) # Stupid automake needs this -VTC_TESTS="$(cd $srcdir/bin/varnishtest && echo tests/*.vtc)" +VTC_TESTS="$(cd $srcdir/bin/varnishtest && echo vtest2/tests/*.vtc tests/*.vtc)" AC_SUBST(VTC_TESTS) AM_SUBST_NOTMAKE(VTC_TESTS) diff --git a/doc/changes.rst b/doc/changes.rst index 33609d263..6ee0ed3ff 100644 --- a/doc/changes.rst +++ b/doc/changes.rst @@ -41,6 +41,15 @@ Varnish Cache NEXT (8.0, 2025-09-15) .. PLEASE keep this roughly in commit order as shown by git-log / tig (new to old) +* The bundled varnishtest sources have now been replaced with the seperate + VTest2 repository. + + When building from git, either clone with the ``-r`` option, or use ``git + submodule update --init`` on an existing repository. + + Developers should use ``make update`` to update the referenced vtest2 commit + after changes to vtest2. + ============================== Varnish-Cache 7.7 (2025-03-17) ============================== diff --git a/doc/sphinx/Makefile.am b/doc/sphinx/Makefile.am index b120649fe..fac1fc7aa 100644 --- a/doc/sphinx/Makefile.am +++ b/doc/sphinx/Makefile.am @@ -148,19 +148,21 @@ include/vsl-tags.rst: $(top_builddir)/lib/libvarnishapi/vsl2rst mv -f ${@}_ ${@} BUILT_SOURCES += include/vsl-tags.rst +vtest2_src = $(top_srcdir)/bin/varnishtest/vtest2/src + VTCSYN_SRC = \ - $(top_srcdir)/bin/varnishtest/vtc.c \ - $(top_srcdir)/bin/varnishtest/vtc_barrier.c \ - $(top_srcdir)/bin/varnishtest/vtc_haproxy.c \ - $(top_srcdir)/bin/varnishtest/vtc_http.c \ - $(top_srcdir)/bin/varnishtest/vtc_http2.c \ - $(top_srcdir)/bin/varnishtest/vtc_logexp.c \ - $(top_srcdir)/bin/varnishtest/vtc_misc.c \ - $(top_srcdir)/bin/varnishtest/vtc_process.c \ - $(top_srcdir)/bin/varnishtest/vtc_syslog.c \ - $(top_srcdir)/bin/varnishtest/vtc_tunnel.c \ - $(top_srcdir)/bin/varnishtest/vtc_varnish.c \ - $(top_srcdir)/bin/varnishtest/vtc_vsm.c + $(vtest2_src)/vtc.c \ + $(vtest2_src)/vtc_barrier.c \ + $(vtest2_src)/vtc_haproxy.c \ + $(vtest2_src)/vtc_http.c \ + $(vtest2_src)/vtc_http2.c \ + $(vtest2_src)/vtc_logexp.c \ + $(vtest2_src)/vtc_misc.c \ + $(vtest2_src)/vtc_process.c \ + $(vtest2_src)/vtc_syslog.c \ + $(vtest2_src)/vtc_tunnel.c \ + $(vtest2_src)/vtc_varnish.c \ + $(vtest2_src)/vtc_vsm.c include/vtc-syntax.rst: $(srcdir)/vtc-syntax.py $(VTCSYN_SRC) $(AM_V_GEN) $(PYTHON) $(srcdir)/vtc-syntax.py $(VTCSYN_SRC) > ${@}_ @mv -f ${@}_ ${@} From nils.goroll at uplex.de Tue May 27 07:06:05 2025 From: nils.goroll at uplex.de (Nils Goroll) Date: Tue, 27 May 2025 07:06:05 +0000 (UTC) Subject: [master] 68fb50186 vtest/cci: Adjust to vtest submodule Message-ID: <20250527070605.9F3821140D2@lists.varnish-cache.org> commit 68fb5018622f675ede5853473f57b042935e15e4 Author: Nils Goroll Date: Tue May 27 09:03:59 2025 +0200 vtest/cci: Adjust to vtest submodule diff --git a/.circleci/config.yml b/.circleci/config.yml index ce11e78cc..3262f515a 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -110,7 +110,7 @@ jobs: mkdir -p ~/.ssh ssh-keyscan -H github.com >> ~/.ssh/known_hosts echo ${CIRCLE_REPOSITORY_URL} - git clone https://github.com/varnishcache/pkg-varnish-cache.git . + git clone -r https://github.com/varnishcache/pkg-varnish-cache.git . git checkout << pipeline.parameters.pkg-commit >> tar cvzf debian.tar.gz debian --dereference tar cvzf redhat.tar.gz redhat --dereference diff --git a/tools/vtest.sh b/tools/vtest.sh index cc617b592..a1837ac0d 100755 --- a/tools/vtest.sh +++ b/tools/vtest.sh @@ -76,9 +76,11 @@ chown varnish "${TMPDIR}" > /dev/null 2>&1 || true # Establish the SRCDIR we build/run/test if ! (cd varnish-cache 2>/dev/null) ; then - git clone \ + git clone -r \ https://github.com/varnishcache/varnish-cache.git \ varnish-cache +else + git submodule update --init || true fi export SRCDIR=`pwd`/varnish-cache From nils.goroll at uplex.de Tue May 27 07:56:05 2025 From: nils.goroll at uplex.de (Nils Goroll) Date: Tue, 27 May 2025 07:56:05 +0000 (UTC) Subject: [master] 6daaa5948 give advise on how to override the submodule url Message-ID: <20250527075605.9208C115E09@lists.varnish-cache.org> commit 6daaa5948fa6ef5913345ed9ae923ec7514c7a1a Author: Nils Goroll Date: Tue May 27 09:54:53 2025 +0200 give advise on how to override the submodule url diff --git a/doc/sphinx/installation/install_source.rst b/doc/sphinx/installation/install_source.rst index 071600d0a..20cf3ba85 100644 --- a/doc/sphinx/installation/install_source.rst +++ b/doc/sphinx/installation/install_source.rst @@ -21,7 +21,13 @@ https://varnish-cache.org/releases/ . Alternatively, if you want to hack on Varnish, you should clone our git repository by doing. - ``git clone https://github.com/varnishcache/varnish-cache`` + ``git clone -r https://github.com/varnishcache/varnish-cache`` + +This will recursively check out the vtest2 sources from the default repository +URL. If you prefer to use vtest2 from a non-default (local) repository, you can +tell git to replace the url: + + ``git config url.$YOUR_VTEST2_URL.insteadOf https://github.com/vtest/VTest2.git`` Build dependencies on FreeBSD ----------------------------- From nils.goroll at uplex.de Tue May 27 08:04:05 2025 From: nils.goroll at uplex.de (Nils Goroll) Date: Tue, 27 May 2025 08:04:05 +0000 (UTC) Subject: [master] 080ab9c89 sorry, I actually was convinced git clone -r existed... Message-ID: <20250527080405.B683B1163F0@lists.varnish-cache.org> commit 080ab9c895d52b2495de85c91527c120c6b6130f Author: Nils Goroll Date: Tue May 27 10:02:37 2025 +0200 sorry, I actually was convinced git clone -r existed... diff --git a/.circleci/config.yml b/.circleci/config.yml index 3262f515a..19e24a857 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -110,7 +110,7 @@ jobs: mkdir -p ~/.ssh ssh-keyscan -H github.com >> ~/.ssh/known_hosts echo ${CIRCLE_REPOSITORY_URL} - git clone -r https://github.com/varnishcache/pkg-varnish-cache.git . + git clone --recursive https://github.com/varnishcache/pkg-varnish-cache.git . git checkout << pipeline.parameters.pkg-commit >> tar cvzf debian.tar.gz debian --dereference tar cvzf redhat.tar.gz redhat --dereference diff --git a/configure.ac b/configure.ac index eeb0747bc..8511d08c2 100644 --- a/configure.ac +++ b/configure.ac @@ -6,7 +6,7 @@ AC_REVISION([$Id$]) AC_INIT([Varnish], [trunk], [varnish-dev at varnish-cache.org]) AC_CONFIG_SRCDIR(include/miniobj.h) if ! test -f "${srcdir}/bin/varnishtest/vtest2/src/vtc_main.c" ; then - AC_MSG_ERROR([vtest2 seems to be missing, use "git clone -r" or "git submodule update --init"]) + AC_MSG_ERROR([vtest2 seems to be missing, use "git clone --recursive" or "git submodule update --init"]) fi AC_CONFIG_HEADERS([config.h]) AC_CONFIG_MACRO_DIR([m4]) diff --git a/doc/sphinx/installation/install_source.rst b/doc/sphinx/installation/install_source.rst index 20cf3ba85..f0f09635b 100644 --- a/doc/sphinx/installation/install_source.rst +++ b/doc/sphinx/installation/install_source.rst @@ -21,7 +21,7 @@ https://varnish-cache.org/releases/ . Alternatively, if you want to hack on Varnish, you should clone our git repository by doing. - ``git clone -r https://github.com/varnishcache/varnish-cache`` + ``git clone --recursive https://github.com/varnishcache/varnish-cache`` This will recursively check out the vtest2 sources from the default repository URL. If you prefer to use vtest2 from a non-default (local) repository, you can diff --git a/tools/vtest.sh b/tools/vtest.sh index a1837ac0d..005214827 100755 --- a/tools/vtest.sh +++ b/tools/vtest.sh @@ -76,7 +76,7 @@ chown varnish "${TMPDIR}" > /dev/null 2>&1 || true # Establish the SRCDIR we build/run/test if ! (cd varnish-cache 2>/dev/null) ; then - git clone -r \ + git clone --recursive \ https://github.com/varnishcache/varnish-cache.git \ varnish-cache else From nils.goroll at uplex.de Tue May 27 13:36:05 2025 From: nils.goroll at uplex.de (Nils Goroll) Date: Tue, 27 May 2025 13:36:05 +0000 (UTC) Subject: [master] 82fe7af00 build: spell out vtest2/src Message-ID: <20250527133605.A266F12158A@lists.varnish-cache.org> commit 82fe7af00f7984bbe65f0ef3620a19d20f7ee3b2 Author: Nils Goroll Date: Tue May 27 15:26:58 2025 +0200 build: spell out vtest2/src using a variable does not work on the solaris vtesters diff --git a/bin/varnishtest/Makefile.am b/bin/varnishtest/Makefile.am index 3a2235889..a1d5b31fb 100644 --- a/bin/varnishtest/Makefile.am +++ b/bin/varnishtest/Makefile.am @@ -27,45 +27,43 @@ install-exec-hook: uninstall-hook: rm -f $(DESTDIR)$(bindir)/vtest$(EXEEXT) -vtest2_src = vtest2/src - varnishtest_SOURCES = \ - $(vtest2_src)/hpack.h \ - $(vtest2_src)/cmds.h \ - $(vtest2_src)/vtc.h \ - $(vtest2_src)/teken.c \ - $(vtest2_src)/teken.h \ - $(vtest2_src)/teken_scs.h \ - $(vtest2_src)/teken_subr.h \ - $(vtest2_src)/teken_subr_compat.h \ - $(vtest2_src)/teken_wcwidth.h \ - $(vtest2_src)/vtc.c \ - $(vtest2_src)/vtc_barrier.c \ - $(vtest2_src)/vtc_client.c \ - $(vtest2_src)/vtc_gzip.c \ - $(vtest2_src)/vtc_haproxy.c \ - $(vtest2_src)/vtc_h2_enctbl.h \ - $(vtest2_src)/vtc_h2_hpack.c \ - $(vtest2_src)/vtc_h2_priv.h \ - $(vtest2_src)/vtc_h2_stattbl.h \ - $(vtest2_src)/vtc_h2_tbl.c \ - $(vtest2_src)/vtc_http.c \ - $(vtest2_src)/vtc_http.h \ - $(vtest2_src)/vtc_http2.c \ - $(vtest2_src)/vtc_log.h \ - $(vtest2_src)/vtc_log.c \ - $(vtest2_src)/vtc_logexp.c \ - $(vtest2_src)/vtc_misc.c \ - $(vtest2_src)/vtc_main.c \ - $(vtest2_src)/vtc_process.c \ - $(vtest2_src)/vtc_proxy.c \ - $(vtest2_src)/vtc_server.c \ - $(vtest2_src)/vtc_sess.c \ - $(vtest2_src)/vtc_subr.c \ - $(vtest2_src)/vtc_syslog.c \ - $(vtest2_src)/vtc_tunnel.c \ - $(vtest2_src)/vtc_varnish.c \ - $(vtest2_src)/vtc_vsm.c + vtest2/src/hpack.h \ + vtest2/src/cmds.h \ + vtest2/src/vtc.h \ + vtest2/src/teken.c \ + vtest2/src/teken.h \ + vtest2/src/teken_scs.h \ + vtest2/src/teken_subr.h \ + vtest2/src/teken_subr_compat.h \ + vtest2/src/teken_wcwidth.h \ + vtest2/src/vtc.c \ + vtest2/src/vtc_barrier.c \ + vtest2/src/vtc_client.c \ + vtest2/src/vtc_gzip.c \ + vtest2/src/vtc_haproxy.c \ + vtest2/src/vtc_h2_enctbl.h \ + vtest2/src/vtc_h2_hpack.c \ + vtest2/src/vtc_h2_priv.h \ + vtest2/src/vtc_h2_stattbl.h \ + vtest2/src/vtc_h2_tbl.c \ + vtest2/src/vtc_http.c \ + vtest2/src/vtc_http.h \ + vtest2/src/vtc_http2.c \ + vtest2/src/vtc_log.h \ + vtest2/src/vtc_log.c \ + vtest2/src/vtc_logexp.c \ + vtest2/src/vtc_misc.c \ + vtest2/src/vtc_main.c \ + vtest2/src/vtc_process.c \ + vtest2/src/vtc_proxy.c \ + vtest2/src/vtc_server.c \ + vtest2/src/vtc_sess.c \ + vtest2/src/vtc_subr.c \ + vtest2/src/vtc_syslog.c \ + vtest2/src/vtc_tunnel.c \ + vtest2/src/vtc_varnish.c \ + vtest2/src/vtc_vsm.c varnishtest_LDADD = \ $(top_builddir)/lib/libvarnishapi/libvarnishapi.la \ @@ -79,27 +77,27 @@ varnishtest_CFLAGS = \ -DVTEST_WITH_VTC_VSM \ -DTOP_BUILDDIR='"${top_builddir}"' -EXTRA_DIST = $(srcdir)/$(vtest2_src)/../tests/*.vtc \ +EXTRA_DIST = $(srcdir)/vtest2/src/../tests/*.vtc \ $(top_srcdir)/bin/varnishtest/tests/*.vtc \ $(top_srcdir)/bin/varnishtest/tests/common.pem \ $(top_srcdir)/bin/varnishtest/tests/README \ - $(vtest2_src)/gensequences \ - $(vtest2_src)/sequences \ - $(vtest2_src)/teken.3 \ - $(vtest2_src)/huffman_gen.py \ - $(vtest2_src)/tbl/vhp_huffman.h + vtest2/src/gensequences \ + vtest2/src/sequences \ + vtest2/src/teken.3 \ + vtest2/src/huffman_gen.py \ + vtest2/src/tbl/vhp_huffman.h teken.c: teken_state.h -teken_state.h: $(srcdir)/$(vtest2_src)/sequences $(srcdir)/$(vtest2_src)/gensequences - awk -f $(srcdir)/$(vtest2_src)/gensequences $(srcdir)/$(vtest2_src)/sequences \ +teken_state.h: $(srcdir)/vtest2/src/sequences $(srcdir)/vtest2/src/gensequences + awk -f $(srcdir)/vtest2/src/gensequences $(srcdir)/vtest2/src/sequences \ > $@_ mv $@_ $@ vtc_h2_hpack.c: vtc_h2_dectbl.h -vtc_h2_dectbl.h: $(srcdir)/$(vtest2_src)/huffman_gen.py $(srcdir)/$(vtest2_src)/tbl/vhp_huffman.h - $(PYTHON) $(srcdir)/$(vtest2_src)/huffman_gen.py \ - $(srcdir)/$(vtest2_src)/tbl/vhp_huffman.h > $@_ +vtc_h2_dectbl.h: $(srcdir)/vtest2/src/huffman_gen.py $(srcdir)/vtest2/src/tbl/vhp_huffman.h + $(PYTHON) $(srcdir)/vtest2/src/huffman_gen.py \ + $(srcdir)/vtest2/src/tbl/vhp_huffman.h > $@_ mv $@_ $@ BUILT_SOURCES = \ From nils.goroll at uplex.de Tue May 27 13:44:04 2025 From: nils.goroll at uplex.de (Nils Goroll) Date: Tue, 27 May 2025 13:44:04 +0000 (UTC) Subject: [master] a73214af1 vtest: fix handling existing directory Message-ID: <20250527134404.A02B6121AE8@lists.varnish-cache.org> commit a73214af1b53900229f1c9c15f2460118892368d Author: Nils Goroll Date: Tue May 27 15:43:22 2025 +0200 vtest: fix handling existing directory (I hope) Ref 68fb5018622f675ede5853473f57b042935e15e4 diff --git a/tools/vtest.sh b/tools/vtest.sh index 005214827..5dfad996a 100755 --- a/tools/vtest.sh +++ b/tools/vtest.sh @@ -80,7 +80,7 @@ if ! (cd varnish-cache 2>/dev/null) ; then https://github.com/varnishcache/varnish-cache.git \ varnish-cache else - git submodule update --init || true + (cd varnish-cache && git submodule update --init || true) fi export SRCDIR=`pwd`/varnish-cache From nils.goroll at uplex.de Tue May 27 14:47:05 2025 From: nils.goroll at uplex.de (Nils Goroll) Date: Tue, 27 May 2025 14:47:05 +0000 (UTC) Subject: [master] 8f9010fba doc: Add generic process to CONTRIBUTING Message-ID: <20250527144705.4A796123A76@lists.varnish-cache.org> commit 8f9010fba70c2744042ace579f003ab45330865b Author: Nils Goroll Date: Tue May 27 16:44:54 2025 +0200 doc: Add generic process to CONTRIBUTING As agreed at vdd25q2 diff --git a/CONTRIBUTING b/CONTRIBUTING index f15022d2d..8ba7471d3 100644 --- a/CONTRIBUTING +++ b/CONTRIBUTING @@ -20,6 +20,16 @@ through new (and old) tickets. It speeds things up a lot if you can join the channel and answer questions directly when we go over the ticket. +General process +--------------- + +In general, please follow these steps when contributing new features or +non-trivial fixes: + +1) Present the idea on -dev or in an issue +2) Write the documentation and present it +3) Create the full PR + GitHub pull requests -------------------- From nils.goroll at uplex.de Wed May 28 15:08:05 2025 From: nils.goroll at uplex.de (Nils Goroll) Date: Wed, 28 May 2025 15:08:05 +0000 (UTC) Subject: [master] 119832d5e Add missed include Message-ID: <20250528150805.6703E103CFD@lists.varnish-cache.org> commit 119832d5e964b01dd672ef6e15f4e2c128bd0c4e Author: Kirill A. Korinsky Date: Tue Nov 19 20:28:08 2024 +0100 Add missed include diff --git a/bin/varnishd/acceptor/cache_acceptor.h b/bin/varnishd/acceptor/cache_acceptor.h index 79c7a0dec..3815b8289 100644 --- a/bin/varnishd/acceptor/cache_acceptor.h +++ b/bin/varnishd/acceptor/cache_acceptor.h @@ -30,6 +30,8 @@ * */ +#include + /* cache_acceptor.c */ struct listen_sock; struct listen_arg; From nils.goroll at uplex.de Wed May 28 15:08:05 2025 From: nils.goroll at uplex.de (Nils Goroll) Date: Wed, 28 May 2025 15:08:05 +0000 (UTC) Subject: [master] 89ef96445 Use pthread_set_name_np when it available Message-ID: <20250528150805.82127103D02@lists.varnish-cache.org> commit 89ef96445c7d6a42250e09725577c35b06560161 Author: Kirill A. Korinsky Date: Tue Nov 19 20:42:10 2024 +0100 Use pthread_set_name_np when it available diff --git a/bin/varnishd/cache/cache_main.c b/bin/varnishd/cache/cache_main.c index c74292bdb..32a44e3ea 100644 --- a/bin/varnishd/cache/cache_main.c +++ b/bin/varnishd/cache/cache_main.c @@ -163,6 +163,8 @@ THR_SetName(const char *name) # else thr_setname_generic(name); # endif +#elif defined(HAVE_PTHREAD_SET_NAME_NP) + (void)pthread_set_name_np(pthread_self(), name); #endif } diff --git a/configure.ac b/configure.ac index 8511d08c2..2483dfeac 100644 --- a/configure.ac +++ b/configure.ac @@ -228,6 +228,7 @@ AC_CHECK_FUNCS([fnmatch], [], [AC_MSG_ERROR([fnmatch(3) is required])]) save_LIBS="${LIBS}" LIBS="${PTHREAD_LIBS}" AC_CHECK_FUNCS([pthread_setname_np]) +AC_CHECK_FUNCS([pthread_set_name_np]) AC_CHECK_FUNCS([pthread_mutex_isowned_np]) AC_CHECK_FUNCS([pthread_getattr_np]) LIBS="${save_LIBS}" From nils.goroll at uplex.de Wed May 28 15:16:05 2025 From: nils.goroll at uplex.de (Nils Goroll) Date: Wed, 28 May 2025 15:16:05 +0000 (UTC) Subject: [master] 2f0e4b128 vtim: Harden VTIM_format() prototype Message-ID: <20250528151605.5C62D10448A@lists.varnish-cache.org> commit 2f0e4b1283fb3c8edb0c187cba10310424f3b6b1 Author: Dridi Boukelmoune Date: Wed May 21 16:23:00 2025 +0200 vtim: Harden VTIM_format() prototype This should allow compilers and static analyzers to complain about buffers too small, and this emphasizes the difference between the format and parse operations and how they treat their respective string arguments. diff --git a/include/vtim.h b/include/vtim.h index 9652238c0..75f7a9cbe 100644 --- a/include/vtim.h +++ b/include/vtim.h @@ -33,7 +33,7 @@ /* from libvarnish/vtim.c */ extern unsigned VTIM_postel; #define VTIM_FORMAT_SIZE 30 -void VTIM_format(vtim_real t, char *p); +void VTIM_format(vtim_real t, char p[VTIM_FORMAT_SIZE]); vtim_real VTIM_parse(const char *p); vtim_mono VTIM_mono(void); vtim_real VTIM_real(void); diff --git a/lib/libvarnish/vtim.c b/lib/libvarnish/vtim.c index af46aa665..e023569e7 100644 --- a/lib/libvarnish/vtim.c +++ b/lib/libvarnish/vtim.c @@ -157,7 +157,7 @@ VTIM_real(void) } void -VTIM_format(vtim_real t, char *p) +VTIM_format(vtim_real t, char p[VTIM_FORMAT_SIZE]) { struct tm tm; time_t tt; From nils.goroll at uplex.de Wed May 28 18:11:05 2025 From: nils.goroll at uplex.de (Nils Goroll) Date: Wed, 28 May 2025 18:11:05 +0000 (UTC) Subject: [master] 7cd5fd3d5 doc: Add Getting started writing VMODs Message-ID: <20250528181105.B44EE10D36E@lists.varnish-cache.org> commit 7cd5fd3d565b191a7a8b9dd5337e0b78691ef9ad Author: Nils Goroll Date: Wed May 28 17:44:14 2025 +0200 doc: Add Getting started writing VMODs diff --git a/doc/sphinx/reference/vmod.rst b/doc/sphinx/reference/vmod.rst index 20cb31b46..8b328894f 100644 --- a/doc/sphinx/reference/vmod.rst +++ b/doc/sphinx/reference/vmod.rst @@ -45,6 +45,20 @@ extensions written for Varnish Cache: https://www.varnish-cache.org/vmods +Getting started writing VMODs +============================= + +There are some projects which can help interested developers get started writing +VMODs: + +.. _`Varnish Cache Development Kit`: https://git.sr.ht/~dridi/vcdk +.. _`varnish-rs`: https://github.com/varnish-rs/varnish-rs + +* For the C programming language, there is the `Varnish Cache Development Kit`_ + (VCDK) + +* Rustacians might want to have a look at `varnish-rs`_. + The vmod.vcc file ================= From nils.goroll at uplex.de Wed May 28 18:11:05 2025 From: nils.goroll at uplex.de (Nils Goroll) Date: Wed, 28 May 2025 18:11:05 +0000 (UTC) Subject: [master] d2e2bf85e doc: Properly document how we track vmod version information Message-ID: <20250528181105.C9F1410D371@lists.varnish-cache.org> commit d2e2bf85e399d8a5882a123fed3c39d4e9b32292 Author: Nils Goroll Date: Wed May 28 17:53:15 2025 +0200 doc: Properly document how we track vmod version information diff --git a/doc/sphinx/reference/vmod.rst b/doc/sphinx/reference/vmod.rst index 8b328894f..0a87b9ee1 100644 --- a/doc/sphinx/reference/vmod.rst +++ b/doc/sphinx/reference/vmod.rst @@ -59,6 +59,8 @@ VMODs: * Rustacians might want to have a look at `varnish-rs`_. +.. _ref-vmod-vcc: + The vmod.vcc file ================= @@ -973,3 +975,89 @@ A simple example for how to use it:: AZ(close(fd)); } + +VMOD Version Information +======================== + +Panic messages (see :ref:`users_trouble` for an introduction) are an invaluable +information source to developers when analyzing crashes. To avoid uncertainties +about the exact code base a VMOD was built from, Panics contain, if possible, a +maintainer-controlled version and a "version" from a version control system +(VCS, usually ``git``). + +The maintainer-controlled version is taken from the ``$Version`` stanza with a +fallback to ``PACKAGE_STRING`` from the ``Makefile``, see :ref:`ref-vmod-vcc`. + +The VCS version is intended to be more specific than the maintainer-controlled +version and is thus automatically generated unless deliberately overwritten: If +``vmodtool.py`` finds itself invoked from a git repository, it uses the version +output by ``git rev-parse HEAD``. To also have proper VCS version information in +builds from a dist archive (as created with ``make dist`` when using autotools), +``vmodtool.py`` also writes a VCS version it gets from git to +``vmod_vcs_version.txt``. This file is intended to be added to the distribution, +such that when ``vmodtool.py`` is run from a distribution archive, it also has +the VCS version available. + +Supporting vmod_vcs_version.txt with autotools +---------------------------------------------- + +To support ``vmod_vcs_version.txt`` with autotools, VMOD authors are advised to +implement the following: + +* add a dependency from ``vmod_vcs_version.txt`` to (one of) the files generated + by ``vmodtool.py`` + +* add ``vmod_vcs_version.txt`` to ``DISTCLEANFILES`` and ``EXTRA_DIST`` + +``vmod_vcs_version.txt`` should also be added to ``.gitignore`` + +To illustrate, here is an example diff for a trivial vmod by the name +*blobsynth*:: + + diff --git a/src/Makefile.am b/src/Makefile.am + index 63e4d55..d8cc659 100644 + --- a/src/Makefile.am + +++ b/src/Makefile.am + @@ -10,6 +10,10 @@ nodist_libvmod_blobsynth_la_SOURCES = \ + vcc_blobsynth_if.c \ + vcc_blobsynth_if.h + + +DISTCLEANFILES = vmod_vcs_version.txt + + + +vmod_vcs_version.txt: $(nodist_libvmod_blobsynth_la_SOURCES) + + + dist_man_MANS = vmod_blobsynth.3 + + @BUILD_VMOD_BLOBSYNTH@ + @@ -25,4 +29,5 @@ TESTS = @VMOD_TESTS@ + + EXTRA_DIST = \ + vmod_blobsynth.vcc \ + + vmod_vcs_version.txt \ + $(VMOD_TESTS) + + +Retrieving version information using the CLI +-------------------------------------------- + +The ``debug.vmod`` CLI command outputs the version information, for example +(edited for readability, the original output is on a single line):: + + $ varnishadm debug.vmod + 1 dynamic (path=".../vmods/libvmod_dynamic.so", + version="libvmod-dynamic trunk", + vcs="1e4179430404aaf170530af7514fbecb1f38f8af") + + +Reading the VCS version from the binary shared object +----------------------------------------------------- + +As a useful by-product, the version information can also be extracted from the +vmod ``.so`` file on many platforms. For example, on Linux using ``readelf +-p.vmod_vcs ``:: + + $ readelf -p.vmod_vcs .../vmods/libvmod_dynamic.so + + String dump of section '.vmod_vcs': + [ 0] 1e4179430404aaf170530af7514fbecb1f38f8af + From nils.goroll at uplex.de Wed May 28 18:16:05 2025 From: nils.goroll at uplex.de (Nils Goroll) Date: Wed, 28 May 2025 18:16:05 +0000 (UTC) Subject: [master] a95399f5b vmodtool.py: Fix VMOD section attribute for macOS compatibility Message-ID: <20250528181605.D5B3410DA06@lists.varnish-cache.org> commit a95399f5b9eda1bfdba6ee6406c30a1ed0720167 Author: Rui Chen Date: Sun May 25 23:54:57 2025 -0400 vmodtool.py: Fix VMOD section attribute for macOS compatibility Signed-off-by: Rui Chen diff --git a/lib/libvcc/vmodtool.py b/lib/libvcc/vmodtool.py index 9bd44c04a..cd0964ba4 100755 --- a/lib/libvcc/vmodtool.py +++ b/lib/libvcc/vmodtool.py @@ -1255,8 +1255,15 @@ class vcc(): vcs = json.dumps(self.vcs()) vmd = "Vmod_%s_Data" % self.modname fo.write('\n') + + # Choose section name based on platform compatibility + if sys.platform == 'darwin': + section_name = '__TEXT,vmod_vcs' + else: + section_name = '.vmod_vcs' + fo.write('static const char vmod_vcs[] ') - fo.write('v_used_(section(".vmod_vcs")) = %s;\n' % vcs) + fo.write('v_used_(section("%s")) = %s;\n' % (section_name, vcs)) for i in (714, 759, 765): fo.write("/*lint -esym(%d, %s) */\n" % (i, vmd)) fo.write("\nextern const struct vmod_data %s;\n" % vmd) From nils.goroll at uplex.de Sat May 31 18:14:05 2025 From: nils.goroll at uplex.de (Nils Goroll) Date: Sat, 31 May 2025 18:14:05 +0000 (UTC) Subject: [master] 36fad6905 doc: Add recipe for how to read the vmod_vcs symbol on MacOS Message-ID: <20250531181405.DF59D75C7@lists.varnish-cache.org> commit 36fad6905fd4cb6ca01aacbb7625a1966975d2f2 Author: Nils Goroll Date: Sat May 31 20:12:37 2025 +0200 doc: Add recipe for how to read the vmod_vcs symbol on MacOS Contributed by Rui Chen in #4339, thank you! diff --git a/doc/sphinx/reference/vmod.rst b/doc/sphinx/reference/vmod.rst index 0a87b9ee1..0afcae202 100644 --- a/doc/sphinx/reference/vmod.rst +++ b/doc/sphinx/reference/vmod.rst @@ -1053,11 +1053,17 @@ Reading the VCS version from the binary shared object ----------------------------------------------------- As a useful by-product, the version information can also be extracted from the -vmod ``.so`` file on many platforms. For example, on Linux using ``readelf --p.vmod_vcs ``:: +vmod ``.so`` file on many platforms. On Linux using ``readelf -p.vmod_vcs +``:: $ readelf -p.vmod_vcs .../vmods/libvmod_dynamic.so String dump of section '.vmod_vcs': [ 0] 1e4179430404aaf170530af7514fbecb1f38f8af +On MacOS:: + + $ otool -s __TEXT vmod_vcs .../vmods/libvmod_debug.so | + awk '/^[0-9a-f]/ {for(i=2;i<=NF;i++) printf "%s",$i}' | + xxd -r -p | strings + 18e27f081788b5e5d44c480f6d9749c07d53ddb9