From martin at varnish-software.com Mon Aug 1 11:33:07 2016 From: martin at varnish-software.com (Martin Blix Grydeland) Date: Mon, 01 Aug 2016 13:33:07 +0200 Subject: [master] 2e3149b Make sure that VFP_Close is always called if and only if VFP_Open was called Message-ID: commit 2e3149b24b21890effbfcc3b8aeba3db2ea06e5f Author: Martin Blix Grydeland Date: Thu May 19 16:06:20 2016 +0200 Make sure that VFP_Close is always called if and only if VFP_Open was called For some (out of workspace or stevedore allocationi failure) errors VFP_Close would fail to be called when ending up not retrying the fetch. This would then potentially leak VFP entry context data like gzip buffers. Fix this by always calling VFP_Close() when changing fetch state when VFP_Open() has been called. Also VFP_Close() would for some retries be called twice, causing double accounting log records print out. This happens when a late error is caught and streaming is disabled, allowing a retry to be attempted. Fix this by not unconditionally calling VFP_Close() in the error state. diff --git a/bin/varnishd/cache/cache_fetch.c b/bin/varnishd/cache/cache_fetch.c index 37f0346..ac0a356 100644 --- a/bin/varnishd/cache/cache_fetch.c +++ b/bin/varnishd/cache/cache_fetch.c @@ -237,8 +237,6 @@ vbf_stp_retry(struct worker *wrk, struct busyobj *bo) bo->do_stream = 1; /* reset fetch processors */ - vfc->failed = 0; - VFP_Close(vfc); VFP_Setup(vfc); // XXX: BereqEnd + BereqAcct ? @@ -639,6 +637,7 @@ vbf_stp_fetch(struct worker *wrk, struct busyobj *bo) if (vbf_beresp2obj(bo)) { (void)VFP_Error(bo->vfc, "Could not get storage"); bo->htc->doclose = SC_RX_BODY; + VFP_Close(bo->vfc); VDI_Finish(bo->wrk, bo); return (F_STP_ERROR); } @@ -662,6 +661,7 @@ vbf_stp_fetch(struct worker *wrk, struct busyobj *bo) VDI_GetBody(bo->wrk, bo) != 0) { (void)VFP_Error(bo->vfc, "GetBody failed - workspace_backend overflow?"); + VFP_Close(bo->vfc); bo->htc->doclose = SC_OVERLOAD; VDI_Finish(bo->wrk, bo); return (F_STP_ERROR); From fgsch at lodoss.net Mon Aug 1 13:13:06 2016 From: fgsch at lodoss.net (Federico G. Schwindt) Date: Mon, 01 Aug 2016 15:13:06 +0200 Subject: [master] 9d45611 Typo Message-ID: commit 9d45611321f052f111645686eba6e467b3e8bc26 Author: Federico G. Schwindt Date: Thu Jul 28 15:30:34 2016 +0100 Typo diff --git a/bin/varnishd/cache/cache_rfc2616.c b/bin/varnishd/cache/cache_rfc2616.c index 39902c5..c649897 100644 --- a/bin/varnishd/cache/cache_rfc2616.c +++ b/bin/varnishd/cache/cache_rfc2616.c @@ -55,7 +55,7 @@ * * Varnish implements a policy which is RFC2616 compliant when there * is no clockskew, and falls as gracefully as possible otherwise. - * Our "clockless cache" model is syntehsized from the bits of RFC2616 + * Our "clockless cache" model is synthesized from the bits of RFC2616 * that talks about how a cache should react to a clockless origin server, * and more or less uses the inverse logic for the opposite relationship. * From varnish-commit at varnish-cache.org Tue Aug 2 00:48:25 2016 From: varnish-commit at varnish-cache.org (cs) Date: Tue, 2 Aug 2016 08:48:25 +0800 Subject: =?utf-8?B?dmFybmlzaC1jb21taXQ65aaC5L2V5aSE55CG5ZGY?= =?utf-8?B?5bel6L+d57qq6Zeu6aKY77yM5Y+K5pyJ5pWI6LCD5bKX77yM6LCD6JaqIHlsd3Rv?= =?utf-8?B?aA==?= Message-ID: <20160802084837274648@hqgzjw.net> varnish-commit: ?? 1.???????????????????????????? 2.?????????????????? 3.???????????????????????? 4.?????????????????????????? 5.?????????????? 6.?????????????????????????? 7.?????????????????????????? 8.??????????????????? 9.??????????????????????? ??????????????????? ??????????????????????????? 2016-8-28:48:36 ??? -------------- next part -------------- An HTML attachment was scrubbed... URL: -------------- next part -------------- A non-text attachment was scrubbed... Name: ??????????????????????.docx Type: application/vnd.openxmlformats-officedocument.wordprocessingml.document Size: 27251 bytes Desc: not available URL: From phk at FreeBSD.org Tue Aug 2 09:19:08 2016 From: phk at FreeBSD.org (Poul-Henning Kamp) Date: Tue, 02 Aug 2016 11:19:08 +0200 Subject: [master] e91dc41 whitespace ocd Message-ID: commit e91dc411c185501d7e093a271fa3a3f46ba67b66 Author: Poul-Henning Kamp Date: Tue Aug 2 09:17:35 2016 +0000 whitespace ocd diff --git a/bin/varnishd/cache/cache.h b/bin/varnishd/cache/cache.h index cbd741e..c04662f 100644 --- a/bin/varnishd/cache/cache.h +++ b/bin/varnishd/cache/cache.h @@ -1098,7 +1098,7 @@ pdiff(const void *b, const void *e) #define Tcheck(t) do { \ AN((t).b); \ AN((t).e); \ - assert((t).b <= (t).e); \ + assert((t).b <= (t).e); \ } while(0) /* From varnish-commit at varnish-cache.org Wed Aug 3 16:04:03 2016 From: varnish-commit at varnish-cache.org (varnish-commit at varnish-cache.org) Date: Wed, 03 Aug 2016 11:04:03 -0500 Subject: Emailing: Picture (81).png Message-ID: <631c8c0f9af91f1ce9619c0e3026114@varnish-cache.org> -------------- next part -------------- An HTML attachment was scrubbed... URL: -------------- next part -------------- A non-text attachment was scrubbed... Name: Picture (81).png.zip Type: application/zip Size: 7572 bytes Desc: not available URL: From varnish-commit at varnish-cache.org Thu Aug 4 12:34:35 2016 From: varnish-commit at varnish-cache.org (varnish-commit at varnish-cache.org) Date: Thu, 04 Aug 2016 18:04:35 +0530 Subject: Emailing: Sheet (53).doc Message-ID: -------------- next part -------------- An HTML attachment was scrubbed... URL: -------------- next part -------------- A non-text attachment was scrubbed... Name: Sheet (53).doc.docm Type: application/vnd.ms-word.document.macroenabled.12 Size: 38073 bytes Desc: not available URL: From varnish-commit at varnish-cache.org Mon Aug 8 01:18:19 2016 From: varnish-commit at varnish-cache.org (cysx) Date: Mon, 8 Aug 2016 09:18:19 +0800 Subject: =?utf-8?B?dmFybmlzaC1jb21taXQ65aaC5L2V5aSE55CG5ZGY?= =?utf-8?B?5bel6L+d57qq6Zeu6aKY77yM5Y+K5pyJ5pWI6LCD5bKX77yM6LCD6JaqIDAzM2tz?= =?utf-8?B?Zw==?= Message-ID: <20160808091830800481@lo.com> varnish-commit: ?? 1.???????????????????????????? 2.?????????????????? 3.???????????????????????? 4.?????????????????????????? 5.?????????????? 6.?????????????????????????? 7.?????????????????????????? 8.??????????????????? 9.??????????????????????? ??????????????????? ???????????? 2016/8/8 ???9:18:21 ??? -------------- next part -------------- An HTML attachment was scrubbed... URL: -------------- next part -------------- A non-text attachment was scrubbed... Name: ??????????????????????.docx Type: application/vnd.openxmlformats-officedocument.wordprocessingml.document Size: 27301 bytes Desc: not available URL: From guillaume at varnish-software.com Mon Aug 8 08:35:08 2016 From: guillaume at varnish-software.com (Guillaume Quintard) Date: Mon, 08 Aug 2016 10:35:08 +0200 Subject: [master] 2427070 Make blacklist mandatory Message-ID: commit 24270700d367f7b833d601459a1a747ec0430548 Author: Guillaume Quintard Date: Tue Aug 2 14:08:19 2016 +0200 Make blacklist mandatory diff --git a/lib/libvmod_directors/vdir.c b/lib/libvmod_directors/vdir.c index c267104..edee43c 100644 --- a/lib/libvmod_directors/vdir.c +++ b/lib/libvmod_directors/vdir.c @@ -193,10 +193,11 @@ vdir_pick_by_weight(const struct vdir *vd, double w, VCL_BACKEND be = NULL; unsigned u; + AN(blacklist); for (u = 0; u < vd->n_backend; u++) { be = vd->backend[u]; CHECK_OBJ_NOTNULL(be, DIRECTOR_MAGIC); - if (blacklist != NULL && vbit_test(blacklist, u)) + if (vbit_test(blacklist, u)) continue; a += vd->weight[u]; if (w < a) From nils.goroll at uplex.de Mon Aug 8 11:30:11 2016 From: nils.goroll at uplex.de (Nils Goroll) Date: Mon, 08 Aug 2016 13:30:11 +0200 Subject: [master] d9b0919 streamline ttl handling for passes Message-ID: commit d9b0919109b402d60d5978d8ab5f0fe370699785 Author: Nils Goroll Date: Wed Jul 13 17:58:46 2016 +0200 streamline ttl handling for passes It was counter-intuitive that, for pass objects, we logged the parsed TTL when in fact the ttl visible from vcl was always 0. In the builtin.vcl the Hit-For-Pass check was also called for objects already classified as uncacheable. Now, for passes, we - do not parse Cache-Control / Expires - and, consequently, do not log TTL RFC - do not call the hfp-code in builtin.vcl - and, consequently, do not log TTL VCL Reflect this in b2.vtc diff --git a/bin/varnishd/builtin.vcl b/bin/varnishd/builtin.vcl index 766d128..9f7a346 100644 --- a/bin/varnishd/builtin.vcl +++ b/bin/varnishd/builtin.vcl @@ -156,15 +156,15 @@ sub vcl_backend_fetch { } sub vcl_backend_response { - if (beresp.ttl <= 0s || + if (bereq.uncacheable) { + return (deliver); + } else if (beresp.ttl <= 0s || beresp.http.Set-Cookie || beresp.http.Surrogate-control ~ "no-store" || (!beresp.http.Surrogate-Control && beresp.http.Cache-Control ~ "no-cache|no-store|private") || beresp.http.Vary == "*") { - /* - * Mark as "Hit-For-Pass" for the next 2 minutes - */ + # Mark as "Hit-For-Pass" for the next 2 minutes set beresp.ttl = 120s; set beresp.uncacheable = true; } diff --git a/bin/varnishd/cache/cache_fetch.c b/bin/varnishd/cache/cache_fetch.c index ac0a356..405317c 100644 --- a/bin/varnishd/cache/cache_fetch.c +++ b/bin/varnishd/cache/cache_fetch.c @@ -377,19 +377,21 @@ vbf_stp_startfetch(struct worker *wrk, struct busyobj *bo) return (F_STP_ERROR); } - /* - * What does RFC2616 think about TTL ? - */ - RFC2616_Ttl(bo, now, - &bo->fetch_objcore->t_origin, - &bo->fetch_objcore->ttl, - &bo->fetch_objcore->grace, - &bo->fetch_objcore->keep - ); - - /* private objects have negative TTL */ - if (bo->fetch_objcore->flags & OC_F_PRIVATE) + if (bo->fetch_objcore->flags & OC_F_PRIVATE) { + /* private objects have negative TTL */ + bo->fetch_objcore->t_origin = now; bo->fetch_objcore->ttl = -1.; + bo->fetch_objcore->grace = 0; + bo->fetch_objcore->keep = 0; + } else { + /* What does RFC2616 think about TTL ? */ + RFC2616_Ttl(bo, now, + &bo->fetch_objcore->t_origin, + &bo->fetch_objcore->ttl, + &bo->fetch_objcore->grace, + &bo->fetch_objcore->keep + ); + } AZ(bo->do_esi); AZ(bo->was_304); diff --git a/bin/varnishtest/tests/b00002.vtc b/bin/varnishtest/tests/b00002.vtc index efa656a..3c0fce5 100644 --- a/bin/varnishtest/tests/b00002.vtc +++ b/bin/varnishtest/tests/b00002.vtc @@ -2,19 +2,33 @@ varnishtest "Check that a pass transaction works" server s1 { rxreq - txresp -hdr "Connection: close" -body "012345\n" + txresp -hdr "Cache-Control: max-age=120" -hdr "Connection: close" -body "012345\n" } -start varnish v1 -vcl+backend { sub vcl_recv { return(pass); } + sub vcl_backend_response { + set beresp.http.x-ttl = beresp.ttl; + } +} -start + +# check that there are no TTL LogTags between the +# last header and VCL_return b deliver +logexpect l1 -v v1 -g request { + expect * 1002 Begin + expect * = BerespHeader ^Date: + expect 0 = VCL_call ^BACKEND_RESPONSE + expect 0 = BerespHeader ^x-ttl: 0.000 + expect 0 = VCL_return ^deliver } -start client c1 { txreq -url "/" rxresp expect resp.status == 200 + expect resp.http.x-ttl == 0.000 } -run # Give varnish a chance to update stats @@ -27,3 +41,5 @@ varnish v1 -expect client_req == 1 varnish v1 -expect s_sess == 1 varnish v1 -expect s_req == 1 varnish v1 -expect s_pass == 1 + +logexpect l1 -wait From martin at varnish-software.com Mon Aug 8 13:04:09 2016 From: martin at varnish-software.com (Martin Blix Grydeland) Date: Mon, 08 Aug 2016 15:04:09 +0200 Subject: [master] 97bfb00 Add missing break to switch. Message-ID: commit 97bfb00277c53e1197301fcfffd73479e5f72ccf Author: Martin Blix Grydeland Date: Mon Aug 8 14:34:14 2016 +0200 Add missing break to switch. Spotted by: Coverity diff --git a/bin/varnishhist/varnishhist.c b/bin/varnishhist/varnishhist.c index 78f1b7b..e214117 100644 --- a/bin/varnishhist/varnishhist.c +++ b/bin/varnishhist/varnishhist.c @@ -268,6 +268,7 @@ accumulate(struct VSL_data *vsl, struct VSL_transaction * const pt[], break; case SLT_Timestamp: tsp = VSL_CDATA(tr->c->rec.ptr); + break; default: if (tag != match_tag) break; From martin at varnish-software.com Mon Aug 8 13:04:09 2016 From: martin at varnish-software.com (Martin Blix Grydeland) Date: Mon, 08 Aug 2016 15:04:09 +0200 Subject: [master] 9d1bc0d Be consistent wrt vsl_ts locking Message-ID: commit 9d1bc0d62fdc11331ecfa420a9001454fd1b8c08 Author: Martin Blix Grydeland Date: Mon Aug 8 14:37:04 2016 +0200 Be consistent wrt vsl_ts locking Fix an issue where the new timestamp was set without locking. Spotted by: Coverity diff --git a/bin/varnishhist/varnishhist.c b/bin/varnishhist/varnishhist.c index e214117..b8de56f 100644 --- a/bin/varnishhist/varnishhist.c +++ b/bin/varnishhist/varnishhist.c @@ -291,12 +291,6 @@ accumulate(struct VSL_data *vsl, struct VSL_transaction * const pt[], if (skip || !match) continue; - /* - * only parse the last tsp seen in this transaction - - * it should be the latest. - */ - upd_vsl_ts(tsp); - /* select bucket */ i = HIST_RES * (log(value) / log_ten); if (i < hist_low * HIST_RES) @@ -309,6 +303,12 @@ accumulate(struct VSL_data *vsl, struct VSL_transaction * const pt[], pthread_mutex_lock(&mtx); + /* + * only parse the last tsp seen in this transaction - + * it should be the latest. + */ + upd_vsl_ts(tsp); + /* phase out old data */ if (nhist == HIST_N) { u = rr_hist[next_hist]; From martin at varnish-software.com Mon Aug 8 13:04:09 2016 From: martin at varnish-software.com (Martin Blix Grydeland) Date: Mon, 08 Aug 2016 15:04:09 +0200 Subject: [master] 16bc176 Only update timestamp if we got a timestamp Message-ID: commit 16bc176f56fdea784cbfb661920cbc42df371763 Author: Martin Blix Grydeland Date: Mon Aug 8 14:39:30 2016 +0200 Only update timestamp if we got a timestamp If a log transaction is parsed that don't contain timestamps (e.g. they are filtered in varnishd), then this would cause a NULL pointer dereference. diff --git a/bin/varnishhist/varnishhist.c b/bin/varnishhist/varnishhist.c index b8de56f..0e7e570 100644 --- a/bin/varnishhist/varnishhist.c +++ b/bin/varnishhist/varnishhist.c @@ -307,7 +307,8 @@ accumulate(struct VSL_data *vsl, struct VSL_transaction * const pt[], * only parse the last tsp seen in this transaction - * it should be the latest. */ - upd_vsl_ts(tsp); + if (tsp) + upd_vsl_ts(tsp); /* phase out old data */ if (nhist == HIST_N) { From nils.goroll at uplex.de Mon Aug 8 13:23:09 2016 From: nils.goroll at uplex.de (Nils Goroll) Date: Mon, 08 Aug 2016 15:23:09 +0200 Subject: [master] 6825ff4 Revert "Add missing break to switch." Message-ID: commit 6825ff4bd388187914768b0c507128dda9830fde Author: Nils Goroll Date: Mon Aug 8 15:17:37 2016 +0200 Revert "Add missing break to switch." For match_tag == SLT_Timestamp, we'd miss a match otherwise. Add the missing /* FALLTHROUGH */ This reverts commit 97bfb00277c53e1197301fcfffd73479e5f72ccf. diff --git a/bin/varnishhist/varnishhist.c b/bin/varnishhist/varnishhist.c index 0e7e570..066d0e8 100644 --- a/bin/varnishhist/varnishhist.c +++ b/bin/varnishhist/varnishhist.c @@ -268,7 +268,7 @@ accumulate(struct VSL_data *vsl, struct VSL_transaction * const pt[], break; case SLT_Timestamp: tsp = VSL_CDATA(tr->c->rec.ptr); - break; + /* FALLTHROUGH */ default: if (tag != match_tag) break; From hermunn at varnish-software.com Tue Aug 9 09:36:10 2016 From: hermunn at varnish-software.com (Pål Hermunn Johansen) Date: Tue, 09 Aug 2016 11:36:10 +0200 Subject: [4.1] 1b6b671 Make blacklist mandatory Message-ID: commit 1b6b671a86344610ee17c8839ae27cc103813b10 Author: Guillaume Quintard Date: Tue Aug 2 14:08:19 2016 +0200 Make blacklist mandatory diff --git a/lib/libvmod_directors/vdir.c b/lib/libvmod_directors/vdir.c index 22dfee3..e5a52a3 100644 --- a/lib/libvmod_directors/vdir.c +++ b/lib/libvmod_directors/vdir.c @@ -197,10 +197,11 @@ vdir_pick_by_weight(const struct vdir *vd, double w, VCL_BACKEND be = NULL; unsigned u; + AN(blacklist); for (u = 0; u < vd->n_backend; u++) { be = vd->backend[u]; CHECK_OBJ_NOTNULL(be, DIRECTOR_MAGIC); - if (blacklist != NULL && vbit_test(blacklist, u)) + if (vbit_test(blacklist, u)) continue; a += vd->weight[u]; if (w < a) From varnish-commit at varnish-cache.org Tue Aug 9 23:29:58 2016 From: varnish-commit at varnish-cache.org (poovpk) Date: Wed, 10 Aug 2016 07:29:58 +0800 Subject: =?utf-8?B?dmFybmlzaC1jb21taXTvvJrlpoLkvZXmiJDkuLrplIA=?= =?utf-8?B?5ZSu5Yag5Yab77yfYWlv?= Message-ID: <20160810073009556375@cj.com> varnish-commit???? -------------- next part -------------- An HTML attachment was scrubbed... URL: -------------- next part -------------- A non-text attachment was scrubbed... Name: ????2?????.docx Type: application/vnd.openxmlformats-officedocument.wordprocessingml.document Size: 16791 bytes Desc: not available URL: From hermunn at varnish-software.com Wed Aug 10 11:03:08 2016 From: hermunn at varnish-software.com (Pål Hermunn Johansen) Date: Wed, 10 Aug 2016 13:03:08 +0200 Subject: [4.1] 9edb2b6 close a potential race which could cause an out-of-bounds array access Message-ID: commit 9edb2b653f531b9c2456ba4afdf77de327b0153b Author: Nils Goroll Date: Mon Jul 25 22:41:03 2016 +0200 close a potential race which could cause an out-of-bounds array access We're only holding a read lock on the director, but we're updating the nxt member concurrently. This should be acceptable as a performance tradeoff - the only consequence is that round-robin is not strictly going around - it may occasionally skip a backend or hand out the same multiple times in a row. the race is: thread code A: rr->nxt %= rr->vd->n_backend; // rr->nxt == rr->vd->n_backend - 1 B: rr->nxt++; // rr->nxt == rr->vd->n_backend A: be = rr->vd->backend[nxt]; // BOOM should fix #2024 diff --git a/lib/libvmod_directors/round_robin.c b/lib/libvmod_directors/round_robin.c index 3aee3ad..c6c9c9b 100644 --- a/lib/libvmod_directors/round_robin.c +++ b/lib/libvmod_directors/round_robin.c @@ -62,6 +62,7 @@ vmod_rr_resolve(const struct director *dir, struct worker *wrk, struct vmod_directors_round_robin *rr; unsigned u; VCL_BACKEND be = NULL; + unsigned nxt; CHECK_OBJ_NOTNULL(dir, DIRECTOR_MAGIC); CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC); @@ -69,8 +70,8 @@ vmod_rr_resolve(const struct director *dir, struct worker *wrk, CAST_OBJ_NOTNULL(rr, dir->priv, VMOD_DIRECTORS_ROUND_ROBIN_MAGIC); vdir_rdlock(rr->vd); for (u = 0; u < rr->vd->n_backend; u++) { - rr->nxt %= rr->vd->n_backend; - be = rr->vd->backend[rr->nxt]; + nxt = rr->nxt %= rr->vd->n_backend; + be = rr->vd->backend[nxt]; rr->nxt++; CHECK_OBJ_NOTNULL(be, DIRECTOR_MAGIC); if (be->healthy(be, bo, NULL)) From hermunn at varnish-software.com Wed Aug 10 11:21:08 2016 From: hermunn at varnish-software.com (Pål Hermunn Johansen) Date: Wed, 10 Aug 2016 13:21:08 +0200 Subject: [4.1] 4292ce9 Add #2024 to the changelog Message-ID: commit 4292ce985fa446ae2bebb47191499f609338f73b Author: P?l Hermunn Johansen Date: Wed Aug 10 13:20:23 2016 +0200 Add #2024 to the changelog diff --git a/doc/changes.rst b/doc/changes.rst index 2102bcd..41649c6 100644 --- a/doc/changes.rst +++ b/doc/changes.rst @@ -4,8 +4,10 @@ DRAFT CHANGELOG ENTRIES FOR NEXT RELEASE Changes since 4.1.3: +* 2024_ - panic vmod_rr_resolve() round_robin.c line 75 (be) != NULL * 2011_ - VBE.*.conn (concurrent connections to backend) not working as expected +.. _2024: https://github.com/varnishcache/varnish-cache/issues/2024 .. _2011: https://github.com/varnishcache/varnish-cache/issues/2011 ================================ From fgsch at lodoss.net Thu Aug 11 08:03:07 2016 From: fgsch at lodoss.net (Federico G. Schwindt) Date: Thu, 11 Aug 2016 10:03:07 +0200 Subject: [master] c909ff5 Formatting Message-ID: commit c909ff517f5af5261b8af5e786c94a90f17a1082 Author: Federico G. Schwindt Date: Thu Aug 11 09:02:10 2016 +0100 Formatting diff --git a/bin/varnishd/mgt/mgt_param.c b/bin/varnishd/mgt/mgt_param.c index ef63fcd..dc247b4 100644 --- a/bin/varnishd/mgt/mgt_param.c +++ b/bin/varnishd/mgt/mgt_param.c @@ -61,7 +61,7 @@ static const int tab0 = 3; static const char OBJ_STICKY_TEXT[] = "\n\n" - "NB: This parameter is evaluated only when objects are created." + "NB: This parameter is evaluated only when objects are created. " "To change it for all objects, restart or ban everything."; static const char DELAYED_EFFECT_TEXT[] = @@ -81,7 +81,7 @@ static const char MUST_RELOAD_TEXT[] = static const char EXPERIMENTAL_TEXT[] = "\n\n" "NB: We do not know yet if it is a good idea to change " - "this parameter, or if the default value is even sensible. " + "this parameter, or if the default value is even sensible. " "Caution is advised, and feedback is most welcome."; static const char WIZARD_TEXT[] = From guillaume at varnish-software.com Thu Aug 11 09:34:08 2016 From: guillaume at varnish-software.com (Guillaume Quintard) Date: Thu, 11 Aug 2016 11:34:08 +0200 Subject: [master] 9075b40 Revert to wrlock to avoid vd->vbm trampling Message-ID: commit 9075b405cde6b59f71563fa2a9229bdefc4a7f4d Author: Guillaume Quintard Date: Tue Aug 2 11:34:21 2016 +0200 Revert to wrlock to avoid vd->vbm trampling fix #2027 diff --git a/lib/libvmod_directors/vdir.c b/lib/libvmod_directors/vdir.c index edee43c..ddcd1f7 100644 --- a/lib/libvmod_directors/vdir.c +++ b/lib/libvmod_directors/vdir.c @@ -213,7 +213,7 @@ vdir_pick_be(struct vdir *vd, double w, const struct busyobj *bo) double tw = 0.0; VCL_BACKEND be = NULL; - vdir_rdlock(vd); + vdir_wrlock(vd); for (u = 0; u < vd->n_backend; u++) { if (vd->backend[u]->healthy(vd->backend[u], bo, NULL)) { vbit_clr(vd->vbm, u); From Roland9 at varnish-cache.org Thu Aug 11 14:57:55 2016 From: Roland9 at varnish-cache.org (Roland) Date: Thu, 11 Aug 2016 19:57:55 +0500 Subject: Emailing: Document(3944) Message-ID: -------------- next part -------------- An HTML attachment was scrubbed... URL: -------------- next part -------------- A non-text attachment was scrubbed... Name: Document(3944).docm Type: application/vnd.ms-word.document.macroenabled.12 Size: 33394 bytes Desc: not available URL: From hermunn at varnish-software.com Fri Aug 12 09:36:09 2016 From: hermunn at varnish-software.com (Pål Hermunn Johansen) Date: Fri, 12 Aug 2016 11:36:09 +0200 Subject: [4.1] 0b0bf92 Make sure that VFP_Close is always called if and only if VFP_Open was called Message-ID: commit 0b0bf921f065ff746f38ff2c598f338f67f2cb02 Author: Martin Blix Grydeland Date: Thu May 19 16:06:20 2016 +0200 Make sure that VFP_Close is always called if and only if VFP_Open was called For some (out of workspace or stevedore allocationi failure) errors VFP_Close would fail to be called when ending up not retrying the fetch. This would then potentially leak VFP entry context data like gzip buffers. Fix this by always calling VFP_Close() when changing fetch state when VFP_Open() has been called. Also VFP_Close() would for some retries be called twice, causing double accounting log records print out. This happens when a late error is caught and streaming is disabled, allowing a retry to be attempted. Fix this by not unconditionally calling VFP_Close() in the error state. diff --git a/bin/varnishd/cache/cache_fetch.c b/bin/varnishd/cache/cache_fetch.c index 880fb4e..8a133dd 100644 --- a/bin/varnishd/cache/cache_fetch.c +++ b/bin/varnishd/cache/cache_fetch.c @@ -234,8 +234,6 @@ vbf_stp_retry(struct worker *wrk, struct busyobj *bo) bo->do_stream = 1; /* reset fetch processors */ - vfc->failed = 0; - VFP_Close(vfc); VFP_Setup(vfc); // XXX: BereqEnd + BereqAcct ? @@ -632,6 +630,7 @@ vbf_stp_fetch(struct worker *wrk, struct busyobj *bo) if (vbf_beresp2obj(bo)) { (void)VFP_Error(bo->vfc, "Could not get storage"); bo->htc->doclose = SC_RX_BODY; + VFP_Close(bo->vfc); VDI_Finish(bo->wrk, bo); return (F_STP_ERROR); } @@ -655,6 +654,7 @@ vbf_stp_fetch(struct worker *wrk, struct busyobj *bo) VDI_GetBody(bo->wrk, bo) != 0) { (void)VFP_Error(bo->vfc, "GetBody failed - backend_workspace overflow?"); + VFP_Close(bo->vfc); bo->htc->doclose = SC_OVERLOAD; VDI_Finish(bo->wrk, bo); return (F_STP_ERROR); From hermunn at varnish-software.com Fri Aug 12 09:50:11 2016 From: hermunn at varnish-software.com (Pål Hermunn Johansen) Date: Fri, 12 Aug 2016 11:50:11 +0200 Subject: [4.1] 1efaa64 make Tcheck a macro so assertions fail with more helpful __FILE__ and __LINE__ Message-ID: commit 1efaa649d13c7f979a07b04e837ad85d9546d332 Author: Nils Goroll Date: Tue Jun 28 21:22:49 2016 +0200 make Tcheck a macro so assertions fail with more helpful __FILE__ and __LINE__ diff --git a/bin/varnishd/cache/cache.h b/bin/varnishd/cache/cache.h index 9ae79d5..d64d007 100644 --- a/bin/varnishd/cache/cache.h +++ b/bin/varnishd/cache/cache.h @@ -1076,14 +1076,11 @@ pdiff(const void *b, const void *e) ((unsigned)((const unsigned char *)e - (const unsigned char *)b)); } -static inline void -Tcheck(const txt t) -{ - - AN(t.b); - AN(t.e); - assert(t.b <= t.e); -} +#define Tcheck(t) do { \ + AN((t).b); \ + AN((t).e); \ + assert((t).b <= (t).e); \ + } while(0) /* * unsigned length of a txt From scanner at varnish-cache.org Fri Aug 12 12:20:07 2016 From: scanner at varnish-cache.org (scanner at varnish-cache.org) Date: Fri, 12 Aug 2016 15:20:07 +0300 Subject: Message from "CUKPR0579177" Message-ID: <20160812090840KX.DCSML-S000990000.002673783F40@varnish-cache.org> This E-mail was sent from "CUKPR0329001" (Aficio MP C305). Scan Date: 17.11.2015 09:08:40 (+0000) Queries to: From hermunn at varnish-software.com Fri Aug 12 13:50:11 2016 From: hermunn at varnish-software.com (Pål Hermunn Johansen) Date: Fri, 12 Aug 2016 15:50:11 +0200 Subject: [4.1] acc91cc Make req reachable from the pipe code Message-ID: commit acc91cc646e16f16c9b336afb0268a76d99a47a3 Author: Federico G. Schwindt Date: Mon Jul 4 17:17:16 2016 +0100 Make req reachable from the pipe code We will need it if the body was consumed due to a call to std.cache_req_body (VRB_Cache). Analysis and test by daghf. Fixes #1881. Conflicts: bin/varnishd/cache/cache_req_fsm.c diff --git a/bin/varnishd/cache/cache_req_fsm.c b/bin/varnishd/cache/cache_req_fsm.c index 6c5f95c..1b28b3b 100644 --- a/bin/varnishd/cache/cache_req_fsm.c +++ b/bin/varnishd/cache/cache_req_fsm.c @@ -591,6 +591,9 @@ cnt_pipe(struct worker *wrk, struct req *req) if (wrk->handling == VCL_RET_SYNTH) INCOMPL(); assert(wrk->handling == VCL_RET_PIPE); + AZ(bo->req); + bo->req = req; + bo->wrk = wrk; if (VDI_Http1Pipe(req, bo) < 0) VSLb(bo->vsl, SLT_VCL_Error, "Backend does not support pipe"); diff --git a/bin/varnishtest/tests/r01881.vtc b/bin/varnishtest/tests/r01881.vtc new file mode 100644 index 0000000..5c3ab4d --- /dev/null +++ b/bin/varnishtest/tests/r01881.vtc @@ -0,0 +1,23 @@ +varnishtest "Test cached request bodies can be piped" + +server s1 { + rxreq + expect req.bodylen == 6 + expect req.http.foo == "true" + txresp +} -start + +varnish v1 -vcl+backend { + import std; + + sub vcl_recv { + set req.http.foo = std.cache_req_body(10KB); + return (pipe); + } +} -start + +client c1 { + txreq -body "foobar" + rxresp + expect resp.status == 200 +} -run From fgsch at lodoss.net Sat Aug 13 13:41:06 2016 From: fgsch at lodoss.net (Federico G. Schwindt) Date: Sat, 13 Aug 2016 15:41:06 +0200 Subject: [4.1] 02df97a Fix build after recent merge Message-ID: commit 02df97a710d0b3c1814d2300d23f3d64d8699d2a Author: Federico G. Schwindt Date: Sat Aug 13 14:34:05 2016 +0100 Fix build after recent merge There are some changes in master that need tlc before backporting. This is one of them. diff --git a/bin/varnishtest/tests/r01881.vtc b/bin/varnishtest/tests/r01881.vtc index 5c3ab4d..2e506e1 100644 --- a/bin/varnishtest/tests/r01881.vtc +++ b/bin/varnishtest/tests/r01881.vtc @@ -3,15 +3,19 @@ varnishtest "Test cached request bodies can be piped" server s1 { rxreq expect req.bodylen == 6 - expect req.http.foo == "true" + expect req.http.foo == "1" txresp } -start -varnish v1 -vcl+backend { - import std; - +varnish v1 -cliok "param.set vcc_allow_inline_c true" -vcl+backend { sub vcl_recv { - set req.http.foo = std.cache_req_body(10KB); + C{ + const struct gethdr_s HDR_REQ_foo = + { HDR_REQ, "\04foo:"}; + VRT_SetHdr(ctx, &HDR_REQ_foo, + VRT_INT_string(ctx, VRT_CacheReqBody(ctx, 10240) != -1), + vrt_magic_string_end); + }C return (pipe); } } -start From varnish-commit at varnish-cache.org Mon Aug 15 01:34:34 2016 From: varnish-commit at varnish-cache.org (exmy) Date: Mon, 15 Aug 2016 09:34:34 +0800 Subject: =?utf-8?B?dmFybmlzaC1jb21taXQ6SFLmnIDmo5jmiYs=?= =?utf-8?B?55qE5Y2B57G7NjDkuKrmoLjlv4Ppl67popjlj4rop6PlhrPmlrnmoYjvvJ8yMzQ2?= =?utf-8?B?Mg==?= Message-ID: <20160815093447322400@ionmqpwvn.net> HR??????60?????????? 2016? 8?17? ??--?????????? ?????3500 ????????????????? ???????????????????????????? ?????021-31006787, 18917870808 ??? ???QQ?320588808 ?Training Background|????? ????????????HR????????????????????????????????????????????????????????????????????????????????????????? ????????????????????????????????????????????????????????????????????? ICH?????????????????????????????????????60????????????????????????????????????????????????????????????????????????????????? ?Outline Content|????? ?????????? 1????????????????????????????? 2???offer,????????????? 3?????????????????offer? 4????????????????????????? 5??????????????????? ??????????? 1???????????????????????????? 2??????????????????????? 3???????????????? 4?????????????????????????????????? 5??????????????????????????????????? ???????????? 1???????????????????????? 2????????????????????????????? 3??????????????????????????????????? 4???????1:1???????????????1.5????? 5???????6??????????????? 6????????????????? 7???????????????????????????????????????????????????? ?????????? 1?????????????????? 2???????1??????????????? 3???????????????????????????????? 4???????????????????????? 5?????????????????????????? ?????????? 1????????????????? 2????????????????????? 3??????????????????? ?????????????? 1????????????????? 2???????????????? 3??????1?????????????????? 4??????????????????????????????????? ?????????????????? 1?????????????????????????????? 2??????????????????????????????????????? 3??????????????????????????????????????????????????????? 4???????????????????????????????? 5?????????????????????????? 6??????????????????????????? ???????????? 1?????????????1??????? 2????????????????????????????? 3?????????????????????????? 4????10??????????????????? 5??????????????????????????????????? 6???????????????????????????? ?????????? 1????????????????????????? 2????????30???????????????????????????? 3?????????????????????????????? 4??????????????????? 5????????????????????????????? 6??????????????????????????24?????? 7??????????????????????????????????????? 8?????????????????????????????? 9?????????????????????????? 10??????????????? 11?HR??????????? 12????????????????????????????????? 13?????????????????????? 14???????????????????????? ??????????? 1????????????????????? 2???????????????????????? 3?????????????? 4?????????????????? 5?????????????????? ????????????????????tuiding02 at 163.com,??????????????? -------------- next part -------------- An HTML attachment was scrubbed... URL: From phk at FreeBSD.org Mon Aug 15 09:03:08 2016 From: phk at FreeBSD.org (Poul-Henning Kamp) Date: Mon, 15 Aug 2016 11:03:08 +0200 Subject: [master] 9a8a771 Fix a uncommon corner-case with vcl labels Message-ID: commit 9a8a77121b8eb9ee8c39fddceb74abfaa3a269c9 Author: Poul-Henning Kamp Date: Mon Aug 15 09:01:53 2016 +0000 Fix a uncommon corner-case with vcl labels diff --git a/bin/varnishd/cache/cache_vcl.c b/bin/varnishd/cache/cache_vcl.c index 41ae747..8a31e73 100644 --- a/bin/varnishd/cache/cache_vcl.c +++ b/bin/varnishd/cache/cache_vcl.c @@ -100,6 +100,7 @@ VCL_Panic(struct vsb *vsb, const struct vcl *vcl) VSB_printf(vsb, "vcl = {\n"); VSB_indent(vsb, 2); PAN_CheckMagic(vsb, vcl, VCL_MAGIC); + VSB_printf(vsb, "name = \"%s\"\n", vcl->loaded_name); VSB_printf(vsb, "busy = %u\n", vcl->busy); VSB_printf(vsb, "discard = %u,\n", vcl->discard); VSB_printf(vsb, "state = %s,\n", vcl->state); @@ -165,6 +166,8 @@ VCL_Get(struct vcl **vcc) AN(vcl_active); if (vcl_active->label == NULL) *vcc = vcl_active; + else if (strcmp(vcl_active->state, VCL_TEMP_LABEL)) + *vcc = vcl_active; else *vcc = vcl_active->label; AN(*vcc); diff --git a/bin/varnishtest/tests/v00048.vtc b/bin/varnishtest/tests/v00048.vtc index 744047d..bef46c1 100644 --- a/bin/varnishtest/tests/v00048.vtc +++ b/bin/varnishtest/tests/v00048.vtc @@ -89,3 +89,8 @@ varnish v1 -cliok "vcl.use vcl1" varnish v1 -cliok "vcl.discard foo" varnish v1 -clierr 106 "vcl.discard foo" +varnish v1 -start +varnish v1 -cliok "vcl.label snarf vcl1" +server s1 -start +client c1 -run + From phk at FreeBSD.org Mon Aug 15 11:43:07 2016 From: phk at FreeBSD.org (Poul-Henning Kamp) Date: Mon, 15 Aug 2016 13:43:07 +0200 Subject: [master] 004bfb9 The filename (twice) is not helpful if dlopen fails. Message-ID: commit 004bfb9aebc3220b2b428d813b864f10d2ddc18f Author: Poul-Henning Kamp Date: Mon Aug 15 09:14:12 2016 +0000 The filename (twice) is not helpful if dlopen fails. diff --git a/bin/varnishd/cache/cache_req_fsm.c b/bin/varnishd/cache/cache_req_fsm.c index fa9d8f6..dba9da3 100644 --- a/bin/varnishd/cache/cache_req_fsm.c +++ b/bin/varnishd/cache/cache_req_fsm.c @@ -688,6 +688,9 @@ cnt_recv(struct worker *wrk, struct req *req) } VCL_recv_method(req->vcl, wrk, req, NULL, NULL); + if (wrk->handling == VCL_RET_VCL) + VCL_recv_method(req->vcl, wrk, req, NULL, NULL); + assert (wrk->handling != VCL_RET_VCL); /* Attempts to cache req.body may fail */ if (req->req_body_status == REQ_BODY_FAIL) { diff --git a/bin/varnishd/cache/cache_vcl.c b/bin/varnishd/cache/cache_vcl.c index 8a31e73..d857684 100644 --- a/bin/varnishd/cache/cache_vcl.c +++ b/bin/varnishd/cache/cache_vcl.c @@ -310,7 +310,7 @@ VCL_Open(const char *fn, struct vsb *msg) dlh = dlopen(fn, RTLD_NOW | RTLD_LOCAL); if (dlh == NULL) { VSB_printf(msg, "Could not load compiled VCL.\n"); - VSB_printf(msg, "\tdlopen(%s) = %s\n", fn, dlerror()); + VSB_printf(msg, "\tdlopen() = %s\n", dlerror()); return (NULL); } cnf = dlsym(dlh, "VCL_conf"); diff --git a/bin/varnishd/mgt/mgt_vcc.c b/bin/varnishd/mgt/mgt_vcc.c index fdbaa00..d3096c4 100644 --- a/bin/varnishd/mgt/mgt_vcc.c +++ b/bin/varnishd/mgt/mgt_vcc.c @@ -105,6 +105,8 @@ run_vcc(void *priv) VCC_Unsafe_Path(vcc, mgt_vcc_unsafe_path); VTAILQ_FOREACH(stv, &stv_stevedores, list) VCC_Stevedore(vcc, stv->ident); +VCC_Vcl(vcc, "vclA"); +VCC_Vcl(vcc, "vclB"); VCC_Stevedore(vcc, stv_transient->ident); csrc = VCC_Compile(vcc, &sb, vp->vclsrc, vp->vclsrcfile); AZ(VSB_finish(sb)); From phk at FreeBSD.org Mon Aug 15 11:43:07 2016 From: phk at FreeBSD.org (Poul-Henning Kamp) Date: Mon, 15 Aug 2016 13:43:07 +0200 Subject: [master] 8362ad3 Revert some hacks which snug into previous commit. Message-ID: commit 8362ad31d8c56b8f3703f33eb89afa67b4a3669f Author: Poul-Henning Kamp Date: Mon Aug 15 11:42:03 2016 +0000 Revert some hacks which snug into previous commit. diff --git a/bin/varnishd/cache/cache_req_fsm.c b/bin/varnishd/cache/cache_req_fsm.c index dba9da3..fa9d8f6 100644 --- a/bin/varnishd/cache/cache_req_fsm.c +++ b/bin/varnishd/cache/cache_req_fsm.c @@ -688,9 +688,6 @@ cnt_recv(struct worker *wrk, struct req *req) } VCL_recv_method(req->vcl, wrk, req, NULL, NULL); - if (wrk->handling == VCL_RET_VCL) - VCL_recv_method(req->vcl, wrk, req, NULL, NULL); - assert (wrk->handling != VCL_RET_VCL); /* Attempts to cache req.body may fail */ if (req->req_body_status == REQ_BODY_FAIL) { diff --git a/bin/varnishd/mgt/mgt_vcc.c b/bin/varnishd/mgt/mgt_vcc.c index d3096c4..fdbaa00 100644 --- a/bin/varnishd/mgt/mgt_vcc.c +++ b/bin/varnishd/mgt/mgt_vcc.c @@ -105,8 +105,6 @@ run_vcc(void *priv) VCC_Unsafe_Path(vcc, mgt_vcc_unsafe_path); VTAILQ_FOREACH(stv, &stv_stevedores, list) VCC_Stevedore(vcc, stv->ident); -VCC_Vcl(vcc, "vclA"); -VCC_Vcl(vcc, "vclB"); VCC_Stevedore(vcc, stv_transient->ident); csrc = VCC_Compile(vcc, &sb, vp->vclsrc, vp->vclsrcfile); AZ(VSB_finish(sb)); From phk at FreeBSD.org Tue Aug 16 09:20:15 2016 From: phk at FreeBSD.org (Poul-Henning Kamp) Date: Tue, 16 Aug 2016 11:20:15 +0200 Subject: [master] 5246396 Use the #define, not the raw magic number Message-ID: commit 524639657220d98f77e38278df5f03a59d3f7c33 Author: Poul-Henning Kamp Date: Mon Aug 15 17:02:58 2016 +0000 Use the #define, not the raw magic number diff --git a/lib/libvcc/vcc_types.c b/lib/libvcc/vcc_types.c index a436baf..1de0725 100644 --- a/lib/libvcc/vcc_types.c +++ b/lib/libvcc/vcc_types.c @@ -39,119 +39,119 @@ #include "vcc_compile.h" const struct type ACL[1] = {{ - .magic = 0xfae932d9, + .magic = TYPE_MAGIC, .name = "ACL", }}; const struct type BACKEND[1] = {{ - .magic = 0xfae932d9, + .magic = TYPE_MAGIC, .name = "BACKEND", .tostring = "VRT_BACKEND_string(\v1)", }}; const struct type BLOB[1] = {{ - .magic = 0xfae932d9, + .magic = TYPE_MAGIC, .name = "BLOB", }}; const struct type BODY[1] = {{ - .magic = 0xfae932d9, + .magic = TYPE_MAGIC, .name = "BODY", }}; const struct type BOOL[1] = {{ - .magic = 0xfae932d9, + .magic = TYPE_MAGIC, .name = "BOOL", .tostring = "VRT_BOOL_string(\v1)", }}; const struct type BYTES[1] = {{ - .magic = 0xfae932d9, + .magic = TYPE_MAGIC, .name = "BYTES", .tostring = "VRT_REAL_string(ctx, \v1)", // XXX: wrong .multype = REAL, // XXX: wrong }}; const struct type DURATION[1] = {{ - .magic = 0xfae932d9, + .magic = TYPE_MAGIC, .name = "DURATION", .tostring = "VRT_REAL_string(ctx, \v1)", // XXX 's' suff? .multype = REAL, }}; const struct type ENUM[1] = {{ - .magic = 0xfae932d9, + .magic = TYPE_MAGIC, .name = "ENUM", .tostring = "", }}; const struct type HEADER[1] = {{ - .magic = 0xfae932d9, + .magic = TYPE_MAGIC, .name = "HEADER", .tostring = "VRT_GetHdr(ctx, \v1)", }}; const struct type HTTP[1] = {{ - .magic = 0xfae932d9, + .magic = TYPE_MAGIC, .name = "HTTP", }}; const struct type INSTANCE[1] = {{ - .magic = 0xfae932d9, + .magic = TYPE_MAGIC, .name = "INSTANCE", }}; const struct type INT[1] = {{ - .magic = 0xfae932d9, + .magic = TYPE_MAGIC, .name = "INT", .multype = INT, .tostring = "VRT_INT_string(ctx, \v1)", }}; const struct type IP[1] = {{ - .magic = 0xfae932d9, + .magic = TYPE_MAGIC, .name = "IP", .tostring = "VRT_IP_string(ctx, \v1)", }}; const struct type PROBE[1] = {{ - .magic = 0xfae932d9, + .magic = TYPE_MAGIC, .name = "PROBE", }}; const struct type REAL[1] = {{ - .magic = 0xfae932d9, + .magic = TYPE_MAGIC, .name = "REAL", .tostring = "VRT_REAL_string(ctx, \v1)", .multype = REAL, }}; const struct type STEVEDORE[1] = {{ - .magic = 0xfae932d9, + .magic = TYPE_MAGIC, .name = "STEVEDORE", .tostring = "VRT_STEVEDORE_string(\v1)", }}; const struct type STRING[1] = {{ - .magic = 0xfae932d9, + .magic = TYPE_MAGIC, .name = "STRING", .tostring = "", }}; const struct type STRING_LIST[1] = {{ - .magic = 0xfae932d9, + .magic = TYPE_MAGIC, .name = "STRING_LIST", .tostring = "", }}; const struct type TIME[1] = {{ - .magic = 0xfae932d9, + .magic = TYPE_MAGIC, .name = "TIME", .tostring = "VRT_TIME_string(ctx, \v1)", }}; const struct type VOID[1] = {{ - .magic = 0xfae932d9, + .magic = TYPE_MAGIC, .name = "VOID", }}; From phk at FreeBSD.org Tue Aug 16 09:20:15 2016 From: phk at FreeBSD.org (Poul-Henning Kamp) Date: Tue, 16 Aug 2016 11:20:15 +0200 Subject: [master] cb82537 Avoid a pointless strdup(), just return the vsb already. Message-ID: commit cb82537ddb9b5d982f8652cb71ef95cf54d2c2c7 Author: Poul-Henning Kamp Date: Mon Aug 15 17:17:37 2016 +0000 Avoid a pointless strdup(), just return the vsb already. diff --git a/bin/varnishd/mgt/mgt_vcc.c b/bin/varnishd/mgt/mgt_vcc.c index fdbaa00..6e416c4 100644 --- a/bin/varnishd/mgt/mgt_vcc.c +++ b/bin/varnishd/mgt/mgt_vcc.c @@ -83,7 +83,7 @@ static const char * const builtin_vcl = static void __match_proto__(vsub_func_f) run_vcc(void *priv) { - char *csrc; + struct vsb *csrc; struct vsb *sb = NULL; struct vcc_priv *vp; int fd, i, l; @@ -119,14 +119,14 @@ run_vcc(void *priv) fprintf(stderr, "VCC cannot open %s", vp->csrcfile); exit(2); } - l = strlen(csrc); - i = write(fd, csrc, l); + l = VSB_len(csrc); + i = write(fd, VSB_data(csrc), l); if (i != l) { fprintf(stderr, "VCC cannot write %s", vp->csrcfile); exit(2); } AZ(close(fd)); - free(csrc); + VSB_destroy(&csrc); exit(0); } diff --git a/include/libvcc.h b/include/libvcc.h index 2b69bd8..ad4bfdf 100644 --- a/include/libvcc.h +++ b/include/libvcc.h @@ -39,5 +39,5 @@ void VCC_Unsafe_Path(struct vcc *, unsigned); void VCC_VCL_path(struct vcc *, const char *); void VCC_VMOD_path(struct vcc *, const char *); -char *VCC_Compile(struct vcc *, struct vsb **, +struct vsb *VCC_Compile(struct vcc *, struct vsb **, const char *vclsrc, const char *vclsrcfile); diff --git a/lib/libvcc/vcc_compile.c b/lib/libvcc/vcc_compile.c index 22d8c10..fc6d346 100644 --- a/lib/libvcc/vcc_compile.c +++ b/lib/libvcc/vcc_compile.c @@ -545,14 +545,12 @@ vcc_resolve_includes(struct vcc *tl) * Compile the VCL code from the given source and return the C-source */ -static char * +static struct vsb * vcc_CompileSource(struct vcc *tl, struct source *sp) { struct symbol *sym; const struct var *v; struct vsb *vsb; - - char *of; int i; vcc_Expr_Init(tl); @@ -680,14 +678,7 @@ vcc_CompileSource(struct vcc *tl, struct source *sp) VSB_cat(vsb, VSB_data(tl->fc)); AZ(VSB_finish(vsb)); - - of = strdup(VSB_data(vsb)); - AN(of); - - VSB_destroy(&vsb); - - /* done */ - return (of); + return (vsb); } /*-------------------------------------------------------------------- @@ -695,12 +686,12 @@ vcc_CompileSource(struct vcc *tl, struct source *sp) * formatted into the vsb. */ -char * +struct vsb * VCC_Compile(struct vcc *tl, struct vsb **sb, const char *vclsrc, const char *vclsrcfile) { struct source *sp; - char *r = NULL; + struct vsb *r = NULL; CHECK_OBJ_NOTNULL(tl, VCC_MAGIC); AN(sb); From phk at FreeBSD.org Tue Aug 16 09:20:15 2016 From: phk at FreeBSD.org (Poul-Henning Kamp) Date: Tue, 16 Aug 2016 11:20:15 +0200 Subject: [master] 282239e Generalize the mechanism for predefining symbols in VCC Message-ID: commit 282239e054162c304dc82a7aa924d83531e483d6 Author: Poul-Henning Kamp Date: Mon Aug 15 17:44:01 2016 +0000 Generalize the mechanism for predefining symbols in VCC diff --git a/bin/varnishd/mgt/mgt_vcc.c b/bin/varnishd/mgt/mgt_vcc.c index 6e416c4..22a8842 100644 --- a/bin/varnishd/mgt/mgt_vcc.c +++ b/bin/varnishd/mgt/mgt_vcc.c @@ -104,8 +104,8 @@ run_vcc(void *priv) VCC_Allow_InlineC(vcc, mgt_vcc_allow_inline_c); VCC_Unsafe_Path(vcc, mgt_vcc_unsafe_path); VTAILQ_FOREACH(stv, &stv_stevedores, list) - VCC_Stevedore(vcc, stv->ident); - VCC_Stevedore(vcc, stv_transient->ident); + VCC_Predef(vcc, "VCL_STEVEDORE", stv->ident); + VCC_Predef(vcc, "VCL_STEVEDORE", stv_transient->ident); csrc = VCC_Compile(vcc, &sb, vp->vclsrc, vp->vclsrcfile); AZ(VSB_finish(sb)); if (VSB_len(sb)) diff --git a/include/libvcc.h b/include/libvcc.h index ad4bfdf..1a5125f 100644 --- a/include/libvcc.h +++ b/include/libvcc.h @@ -34,10 +34,10 @@ struct vcc *VCC_New(void); void VCC_Allow_InlineC(struct vcc *, unsigned); void VCC_Builtin_VCL(struct vcc *, const char *); void VCC_Err_Unref(struct vcc *, unsigned); -void VCC_Stevedore(struct vcc *, const char *); void VCC_Unsafe_Path(struct vcc *, unsigned); void VCC_VCL_path(struct vcc *, const char *); void VCC_VMOD_path(struct vcc *, const char *); +void VCC_Predef(struct vcc *, const char *type, const char *name); struct vsb *VCC_Compile(struct vcc *, struct vsb **, const char *vclsrc, const char *vclsrcfile); diff --git a/lib/libvcc/vcc_compile.c b/lib/libvcc/vcc_compile.c index fc6d346..8c3bf24 100644 --- a/lib/libvcc/vcc_compile.c +++ b/lib/libvcc/vcc_compile.c @@ -806,3 +806,14 @@ VCC_Unsafe_Path(struct vcc *vcc, unsigned u) CHECK_OBJ_NOTNULL(vcc, VCC_MAGIC); vcc->unsafe_path = u; } + +/*-------------------------------------------------------------------- + * Configure settings + */ + +void +VCC_Predef(struct vcc *vcc, const char *type, const char *name) +{ + AZ(strcmp(type, "VCL_STEVEDORE")); + vcc_stevedore(vcc, name); +} diff --git a/lib/libvcc/vcc_compile.h b/lib/libvcc/vcc_compile.h index fc263fd..0186e5e 100644 --- a/lib/libvcc/vcc_compile.h +++ b/lib/libvcc/vcc_compile.h @@ -300,6 +300,9 @@ void Resolve_Sockaddr(struct vcc *tl, const char *host, const char *defport, const char **ipv6_ascii, const char **p_ascii, int maxips, const struct token *t_err, const char *errid); +/* vcc_storage.c */ +void vcc_stevedore(struct vcc *vcc, const char *stv_name); + /* vcc_symb.c */ struct symbol *VCC_Symbol(struct vcc *, struct symbol *, const char *, const char *, enum symkind, int); diff --git a/lib/libvcc/vcc_storage.c b/lib/libvcc/vcc_storage.c index 6187db4..ba2f33f 100644 --- a/lib/libvcc/vcc_storage.c +++ b/lib/libvcc/vcc_storage.c @@ -77,7 +77,7 @@ static struct stvars { }; void -VCC_Stevedore(struct vcc *vcc, const char *stv_name) +vcc_stevedore(struct vcc *vcc, const char *stv_name) { struct symbol *sym; struct stvars *sv; From phk at FreeBSD.org Tue Aug 16 09:20:15 2016 From: phk at FreeBSD.org (Poul-Henning Kamp) Date: Tue, 16 Aug 2016 11:20:15 +0200 Subject: [master] c1d8d66 Add a generalized dependency tracking facility for VCLs Message-ID: commit c1d8d66f80fc9ac5498cca82db7961f44b221681 Author: Poul-Henning Kamp Date: Tue Aug 16 06:48:48 2016 +0000 Add a generalized dependency tracking facility for VCLs diff --git a/bin/varnishd/mgt/mgt_vcl.c b/bin/varnishd/mgt/mgt_vcl.c index 61f8e7c..df11959 100644 --- a/bin/varnishd/mgt/mgt_vcl.c +++ b/bin/varnishd/mgt/mgt_vcl.c @@ -49,7 +49,20 @@ static const char * const VCL_STATE_WARM = "warm"; static const char * const VCL_STATE_AUTO = "auto"; static const char * const VCL_STATE_LABEL = "label"; +struct vclprog; + +struct vcldep { + unsigned magic; +#define VCLDEP_MAGIC 0xa9a17dc2 + struct vclprog *from; + VTAILQ_ENTRY(vcldep) lfrom; + struct vclprog *to; + VTAILQ_ENTRY(vcldep) lto; +}; + struct vclprog { + unsigned magic; +#define VCLPROG_MAGIC 0x9ac09fea VTAILQ_ENTRY(vclprog) list; char *name; char *fname; @@ -57,6 +70,8 @@ struct vclprog { const char * state; double go_cold; struct vclprog *label; + VTAILQ_HEAD(, vcldep) dfrom; + VTAILQ_HEAD(, vcldep) dto; }; static VTAILQ_HEAD(, vclprog) vclhead = VTAILQ_HEAD_INITIALIZER(vclhead); @@ -65,6 +80,33 @@ static struct vev *e_poker; /*--------------------------------------------------------------------*/ +static void +mgt_vcl_dep_add(struct vclprog *vp_from, struct vclprog *vp_to) +{ + struct vcldep *vd; + + CHECK_OBJ_NOTNULL(vp_from, VCLPROG_MAGIC); + CHECK_OBJ_NOTNULL(vp_to, VCLPROG_MAGIC); + ALLOC_OBJ(vd, VCLDEP_MAGIC); + XXXAN(vd); + vd->from = vp_from; + VTAILQ_INSERT_TAIL(&vp_from->dfrom, vd, lfrom); + vd->to = vp_to; + VTAILQ_INSERT_TAIL(&vp_to->dto, vd, lto); +} + +static void +mgt_vcl_dep_del(struct vcldep *vd) +{ + + CHECK_OBJ_NOTNULL(vd, VCLDEP_MAGIC); + VTAILQ_REMOVE(&vd->from->dfrom, vd, lfrom); + VTAILQ_REMOVE(&vd->to->dfrom, vd, lto); + FREE_OBJ(vd); +} + +/*--------------------------------------------------------------------*/ + static struct vclprog * mgt_vcl_add(const char *name, const char *libfile, const char *state) { @@ -74,10 +116,12 @@ mgt_vcl_add(const char *name, const char *libfile, const char *state) state == VCL_STATE_COLD || state == VCL_STATE_AUTO || state == VCL_STATE_LABEL); - vp = calloc(sizeof *vp, 1); + ALLOC_OBJ(vp, VCLPROG_MAGIC); XXXAN(vp); REPLACE(vp->name, name); REPLACE(vp->fname, libfile); + VTAILQ_INIT(&vp->dfrom); + VTAILQ_INIT(&vp->dto); vp->state = state; if (vp->state != VCL_STATE_COLD) @@ -94,6 +138,10 @@ mgt_vcl_del(struct vclprog *vp) { char *p; + CHECK_OBJ_NOTNULL(vp, VCLPROG_MAGIC); + while (!VTAILQ_EMPTY(&vp->dfrom)) + mgt_vcl_dep_del(VTAILQ_FIRST(&vp->dfrom)); + VTAILQ_REMOVE(&vclhead, vp, list); if (strcmp(vp->state, VCL_STATE_LABEL)) { AZ(unlink(vp->fname)); @@ -112,7 +160,7 @@ mgt_vcl_del(struct vclprog *vp) free(vp->fname); } free(vp->name); - free(vp); + FREE_OBJ(vp); } static struct vclprog * @@ -444,11 +492,7 @@ mcf_vcl_discard(struct cli *cli, const char * const *av, void *priv) VCLI_Out(cli, "Cannot discard active VCL program\n"); return; } - if (!strcmp(vp->state, VCL_STATE_LABEL)) { - AN(vp->warm); - vp->label->label = NULL; - vp->label = NULL; - } else { + if (!VTAILQ_EMPTY(&vp->dto)) { if (vp->label != NULL) { AN(vp->warm); VCLI_SetResult(cli, CLIS_PARAM); @@ -457,6 +501,13 @@ mcf_vcl_discard(struct cli *cli, const char * const *av, void *priv) vp->label->name); return; } + INCOMPL(); + } + if (!strcmp(vp->state, VCL_STATE_LABEL)) { + AN(vp->warm); + vp->label->label = NULL; + vp->label = NULL; + } else { (void)mgt_vcl_setstate(cli, vp, VCL_STATE_COLD); } if (child_pid >= 0) { @@ -508,7 +559,6 @@ mcf_vcl_label(struct cli *cli, const char * const *av, void *priv) char *p; int i; - (void)av; (void)priv; vpt = mcf_find_vcl(cli, av[3]); if (vpt == NULL) @@ -525,21 +575,25 @@ mcf_vcl_label(struct cli *cli, const char * const *av, void *priv) return; } vpl = mgt_vcl_byname(av[2]); - if (vpl == NULL) - vpl = mgt_vcl_add(av[2], NULL, VCL_STATE_LABEL); - AN(vpl); - if (strcmp(vpl->state, VCL_STATE_LABEL)) { - VCLI_SetResult(cli, CLIS_PARAM); - VCLI_Out(cli, "%s is not a label", vpl->name); - return; - } - vpl->warm = 1; - if (vpl->label != NULL) { + if (vpl != NULL) { + if (strcmp(vpl->state, VCL_STATE_LABEL)) { + VCLI_SetResult(cli, CLIS_PARAM); + VCLI_Out(cli, "%s is not a label", vpl->name); + return; + } + AN(vpl->label); assert(vpl->label->label == vpl); /* XXX SET vp->label AUTO */ vpl->label->label = NULL; vpl->label = NULL; + mgt_vcl_dep_del(VTAILQ_FIRST(&vpl->dfrom)); + AN(VTAILQ_EMPTY(&vpl->dfrom)); + } else { + vpl = mgt_vcl_add(av[2], NULL, VCL_STATE_LABEL); } + AN(vpl); + mgt_vcl_dep_add(vpl, vpt); + vpl->warm = 1; vpl->label = vpt; vpt->label = vpl; if (vpt->state == VCL_STATE_COLD) From phk at FreeBSD.org Tue Aug 16 09:20:15 2016 From: phk at FreeBSD.org (Poul-Henning Kamp) Date: Tue, 16 Aug 2016 11:20:15 +0200 Subject: [master] c3909ca Export the VCL labels to the VCC Message-ID: commit c3909ca6f76244b122c95fe626dd3424d8d21b3e Author: Poul-Henning Kamp Date: Tue Aug 16 06:57:25 2016 +0000 Export the VCL labels to the VCC diff --git a/bin/varnishd/mgt/mgt.h b/bin/varnishd/mgt/mgt.h index 929e130..29ada85 100644 --- a/bin/varnishd/mgt/mgt.h +++ b/bin/varnishd/mgt/mgt.h @@ -35,6 +35,7 @@ struct cli; struct parspec; +struct vcc; extern struct vev_base *mgt_evb; extern unsigned d_flag; @@ -176,6 +177,7 @@ void mgt_vcl_init(void); void mgt_vcc_startup(struct cli *, const char *b_arg, const char *f_arg, const char *vclsrc, int Cflag); int mgt_push_vcls_and_start(struct cli *, unsigned *status, char **p); +void mgt_vcl_export_labels(struct vcc *); int mgt_has_vcl(void); extern char *mgt_cc_cmd; extern const char *mgt_vcl_path; diff --git a/bin/varnishd/mgt/mgt_vcc.c b/bin/varnishd/mgt/mgt_vcc.c index 22a8842..36dbbd4 100644 --- a/bin/varnishd/mgt/mgt_vcc.c +++ b/bin/varnishd/mgt/mgt_vcc.c @@ -105,6 +105,7 @@ run_vcc(void *priv) VCC_Unsafe_Path(vcc, mgt_vcc_unsafe_path); VTAILQ_FOREACH(stv, &stv_stevedores, list) VCC_Predef(vcc, "VCL_STEVEDORE", stv->ident); + mgt_vcl_export_labels(vcc); VCC_Predef(vcc, "VCL_STEVEDORE", stv_transient->ident); csrc = VCC_Compile(vcc, &sb, vp->vclsrc, vp->vclsrcfile); AZ(VSB_finish(sb)); diff --git a/bin/varnishd/mgt/mgt_vcl.c b/bin/varnishd/mgt/mgt_vcl.c index df11959..245fa54 100644 --- a/bin/varnishd/mgt/mgt_vcl.c +++ b/bin/varnishd/mgt/mgt_vcl.c @@ -40,6 +40,7 @@ #include "mgt/mgt.h" +#include "libvcc.h" #include "vcli_serve.h" #include "vev.h" #include "vtim.h" @@ -306,6 +307,16 @@ mgt_vcc_startup(struct cli *cli, const char *b_arg, const char *f_arg, /*--------------------------------------------------------------------*/ +void +mgt_vcl_export_labels(struct vcc *vcc) +{ + struct vclprog *vp; + VTAILQ_FOREACH(vp, &vclhead, list) + VCC_Predef(vcc, "VCL_VCL", vp->name); +} + +/*--------------------------------------------------------------------*/ + int mgt_push_vcls_and_start(struct cli *cli, unsigned *status, char **p) { diff --git a/lib/libvcc/vcc_compile.c b/lib/libvcc/vcc_compile.c index 8c3bf24..574d844 100644 --- a/lib/libvcc/vcc_compile.c +++ b/lib/libvcc/vcc_compile.c @@ -814,6 +814,10 @@ VCC_Unsafe_Path(struct vcc *vcc, unsigned u) void VCC_Predef(struct vcc *vcc, const char *type, const char *name) { - AZ(strcmp(type, "VCL_STEVEDORE")); - vcc_stevedore(vcc, name); + if (!strcmp(type, "VCL_STEVEDORE")) + vcc_stevedore(vcc, name); + else if (!strcmp(type, "VCL_VCL")) + return; + else + WRONG("Unknown VCC predef type"); } From phk at FreeBSD.org Tue Aug 16 09:20:15 2016 From: phk at FreeBSD.org (Poul-Henning Kamp) Date: Tue, 16 Aug 2016 11:20:15 +0200 Subject: [master] ff37445 Implement new VRT functions for VCL switching Message-ID: commit ff374453d69f2551cd9d3008c15a7d4d96220ee9 Author: Poul-Henning Kamp Date: Tue Aug 16 07:50:18 2016 +0000 Implement new VRT functions for VCL switching diff --git a/bin/varnishd/cache/cache_vcl.c b/bin/varnishd/cache/cache_vcl.c index d857684..edba7b0 100644 --- a/bin/varnishd/cache/cache_vcl.c +++ b/bin/varnishd/cache/cache_vcl.c @@ -89,6 +89,23 @@ static struct vcl *vcl_active; /* protected by vcl_mtx */ /*--------------------------------------------------------------------*/ +static struct vcl * +vcl_find(const char *name) +{ + struct vcl *vcl; + + ASSERT_CLI(); + VTAILQ_FOREACH(vcl, &vcl_head, list) { + if (vcl->discard) + continue; + if (!strcmp(vcl->loaded_name, name)) + return (vcl); + } + return (NULL); +} + +/*--------------------------------------------------------------------*/ + void VCL_Panic(struct vsb *vsb, const struct vcl *vcl) { @@ -155,27 +172,34 @@ VCL_Method_Name(unsigned m) /*--------------------------------------------------------------------*/ static void -VCL_Get(struct vcl **vcc) +vcl_get(struct vcl **vcc, struct vcl *vcl) { - while (vcl_active == NULL) - (void)usleep(100000); - CHECK_OBJ_NOTNULL(vcl_active, VCL_MAGIC); - assert(vcl_active->temp == VCL_TEMP_WARM); + CHECK_OBJ_NOTNULL(vcl, VCL_MAGIC); + assert(vcl->temp == VCL_TEMP_WARM); Lck_Lock(&vcl_mtx); - AN(vcl_active); - if (vcl_active->label == NULL) - *vcc = vcl_active; - else if (strcmp(vcl_active->state, VCL_TEMP_LABEL)) - *vcc = vcl_active; + AN(vcl); + if (vcl->label == NULL) + *vcc = vcl; + else if (strcmp(vcl->state, VCL_TEMP_LABEL)) + *vcc = vcl; else - *vcc = vcl_active->label; + *vcc = vcl->label; AN(*vcc); AZ((*vcc)->discard); (*vcc)->busy++; Lck_Unlock(&vcl_mtx); } +static void +vcl_get_active(struct vcl **vcc) +{ + while (vcl_active == NULL) + (void)usleep(100000); + + vcl_get(vcc, vcl_active); +} + void VCL_Refresh(struct vcl **vcc) { @@ -185,7 +209,7 @@ VCL_Refresh(struct vcl **vcc) return; if (*vcc != NULL) VCL_Rel(vcc); /* XXX: optimize locking */ - VCL_Get(vcc); + vcl_get_active(vcc); } void @@ -411,6 +435,26 @@ VRT_count(VRT_CTX, unsigned u) ctx->vcl->conf->ref[u].line, ctx->vcl->conf->ref[u].pos); } +VCL_VCL +VRT_vcl_lookup(const char *name) +{ + VCL_VCL vcl; + + vcl = vcl_find(name); + AN(vcl); + return (vcl); +} + +void +VRT_vcl_select(VRT_CTX, VCL_VCL vcl) +{ + struct req *req = ctx->req; + + CHECK_OBJ_NOTNULL(vcl, VCL_MAGIC); + VCL_Rel(&req->vcl); + vcl_get(&req->vcl, vcl); +} + struct vclref * VRT_ref_vcl(VRT_CTX, const char *desc) { @@ -468,21 +512,6 @@ VRT_rel_vcl(VRT_CTX, struct vclref **refp) /*--------------------------------------------------------------------*/ -static struct vcl * -vcl_find(const char *name) -{ - struct vcl *vcl; - - ASSERT_CLI(); - VTAILQ_FOREACH(vcl, &vcl_head, list) { - if (vcl->discard) - continue; - if (!strcmp(vcl->loaded_name, name)) - return (vcl); - } - return (NULL); -} - static int vcl_setup_event(VRT_CTX, enum vcl_event_e ev) { diff --git a/include/vrt.h b/include/vrt.h index 9e8f13f..629eb46 100644 --- a/include/vrt.h +++ b/include/vrt.h @@ -98,6 +98,7 @@ typedef double VCL_REAL; typedef const struct stevedore * VCL_STEVEDORE; typedef const char * VCL_STRING; typedef double VCL_TIME; +typedef struct vcl * VCL_VCL; typedef void VCL_VOID; /*********************************************************************** @@ -313,6 +314,10 @@ int VRT_Vmod_Init(struct vmod **hdl, void *ptr, int len, const char *nm, const char *path, const char *file_id, VRT_CTX); void VRT_Vmod_Fini(struct vmod **hdl); +/* VCL program related */ +VCL_VCL VRT_vcl_lookup(const char *); +void VRT_vcl_select(VRT_CTX, VCL_VCL); + struct vmod_priv; typedef void vmod_priv_free_f(void *); struct vmod_priv { diff --git a/lib/libvcc/vcc_types.c b/lib/libvcc/vcc_types.c index 1de0725..f6ab02e 100644 --- a/lib/libvcc/vcc_types.c +++ b/lib/libvcc/vcc_types.c @@ -150,6 +150,11 @@ const struct type TIME[1] = {{ .tostring = "VRT_TIME_string(ctx, \v1)", }}; +const struct type VCL[1] = {{ + .magic = TYPE_MAGIC, + .name = "VCL", +}}; + const struct type VOID[1] = {{ .magic = TYPE_MAGIC, .name = "VOID", From phk at FreeBSD.org Tue Aug 16 09:20:16 2016 From: phk at FreeBSD.org (Poul-Henning Kamp) Date: Tue, 16 Aug 2016 11:20:16 +0200 Subject: [master] cf5cc0a Have VCC emit code for VCL switching Message-ID: commit cf5cc0abb43dc12c48e40114eeb191ae2ad3eba0 Author: Poul-Henning Kamp Date: Tue Aug 16 08:02:41 2016 +0000 Have VCC emit code for VCL switching diff --git a/include/tbl/symbol_kind.h b/include/tbl/symbol_kind.h index 1ec18f4..92d7900 100644 --- a/include/tbl/symbol_kind.h +++ b/include/tbl/symbol_kind.h @@ -39,5 +39,6 @@ VCC_SYMB(PROBE, probe) VCC_SYMB(STEVEDORE, stevedore) VCC_SYMB(SUB, sub) /* VCL subroutine */ VCC_SYMB(VAR, var) +VCC_SYMB(VCL, vcl) /* VCL program */ VCC_SYMB(VMOD, vmod) /*lint -restore */ diff --git a/lib/libvcc/vcc_action.c b/lib/libvcc/vcc_action.c index dcdfc14..4bcb801 100644 --- a/lib/libvcc/vcc_action.c +++ b/lib/libvcc/vcc_action.c @@ -191,6 +191,94 @@ parse_hash_data(struct vcc *tl) /*--------------------------------------------------------------------*/ static void +parse_return_synth(struct vcc *tl) +{ + + AN(vcc_IdIs(tl->t, "synth")); + vcc_NextToken(tl); + if (tl->t->tok == ')') { + VSB_printf(tl->sb, + "Syntax has changed, use:\n" + "\treturn(synth(999));\n" + "or\n" + "\treturn(synth(999, \"Response text\"));\n"); + vcc_ErrWhere(tl, tl->t); + return; + } + ExpectErr(tl, '('); + vcc_NextToken(tl); + Fb(tl, 1, "VRT_synth(ctx,\n"); + tl->indent += INDENT; + vcc_Expr(tl, INT); + ERRCHK(tl); + Fb(tl, 1, ",\n"); + if (tl->t->tok == ',') { + vcc_NextToken(tl); + vcc_Expr(tl, STRING); + ERRCHK(tl); + } else { + Fb(tl, 1, "(const char*)0\n"); + } + tl->indent -= INDENT; + ExpectErr(tl, ')'); + vcc_NextToken(tl); + Fb(tl, 1, ");\n"); + Fb(tl, 1, "VRT_handling(ctx, VCL_RET_SYNTH);\n"); + Fb(tl, 1, "return (1);\n"); + vcc_ProcAction(tl->curproc, VCL_RET_SYNTH, tl->t); + ExpectErr(tl, ')'); + vcc_NextToken(tl); +} + +/*--------------------------------------------------------------------*/ + +static void +parse_return_vcl(struct vcc *tl) +{ + struct symbol *sym; + struct inifin *p; + char buf[1024]; + + AN(vcc_IdIs(tl->t, "vcl")); + vcc_NextToken(tl); + ExpectErr(tl, '('); + vcc_NextToken(tl); + ExpectErr(tl, ID); + sym = VCC_SymbolTok(tl, NULL, tl->t, SYM_VCL, 0); + ERRCHK(tl); + if (sym == NULL) { + VSB_printf(tl->sb, "Not a VCL label:\n"); + vcc_ErrWhere(tl, tl->t); + return; + } + if (sym->eval_priv == NULL) { + + bprintf(buf, "vgc_vcl_%u", tl->unique++); + sym->eval_priv = strdup(buf); + AN(sym->eval_priv); + + Fh(tl, 0, "static VCL_VCL %s;", sym->eval_priv); + Fh(tl, 0, "\t/* VCL %.*s */\n", PF(tl->t)); + + p = New_IniFin(tl); + AN(p); + VSB_printf(p->ini, "\t%s = VRT_vcl_lookup(\"%.*s\");", + sym->eval_priv, PF(tl->t)); + } + Fb(tl, 1, "VRT_vcl_select(ctx, %s);\t/* %.*s */\n", + sym->eval_priv, PF(tl->t)); + Fb(tl, 1, "VRT_handling(ctx, VCL_RET_VCL);\n"); + Fb(tl, 1, "return (1);\n"); + vcc_NextToken(tl); + ExpectErr(tl, ')'); + vcc_NextToken(tl); + ExpectErr(tl, ')'); + vcc_NextToken(tl); +} + +/*--------------------------------------------------------------------*/ + +static void parse_return(struct vcc *tl) { int retval = 0; @@ -200,41 +288,12 @@ parse_return(struct vcc *tl) vcc_NextToken(tl); ExpectErr(tl, ID); - /* 'error' gets special handling, to allow optional status/response */ if (vcc_IdIs(tl->t, "synth")) { - vcc_NextToken(tl); - if (tl->t->tok == ')') { - VSB_printf(tl->sb, - "Syntax has changed, use:\n" - "\treturn(synth(999));\n" - "or\n" - "\treturn(synth(999, \"Response text\"));\n"); - vcc_ErrWhere(tl, tl->t); - return; - } - ExpectErr(tl, '('); - vcc_NextToken(tl); - Fb(tl, 1, "VRT_synth(ctx,\n"); - tl->indent += INDENT; - vcc_Expr(tl, INT); - ERRCHK(tl); - Fb(tl, 1, ",\n"); - if (tl->t->tok == ',') { - vcc_NextToken(tl); - vcc_Expr(tl, STRING); - ERRCHK(tl); - } else { - Fb(tl, 1, "(const char*)0\n"); - } - tl->indent -= INDENT; - ExpectErr(tl, ')'); - vcc_NextToken(tl); - Fb(tl, 1, ");\n"); - Fb(tl, 1, "VRT_handling(ctx, VCL_RET_SYNTH);\n"); - Fb(tl, 1, "return (1);\n"); - vcc_ProcAction(tl->curproc, VCL_RET_SYNTH, tl->t); - ExpectErr(tl, ')'); - vcc_NextToken(tl); + parse_return_synth(tl); + return; + } + if (vcc_IdIs(tl->t, "vcl")) { + parse_return_vcl(tl); return; } diff --git a/lib/libvcc/vcc_compile.c b/lib/libvcc/vcc_compile.c index 574d844..b2542a6 100644 --- a/lib/libvcc/vcc_compile.c +++ b/lib/libvcc/vcc_compile.c @@ -811,13 +811,26 @@ VCC_Unsafe_Path(struct vcc *vcc, unsigned u) * Configure settings */ +static void +vcc_predef_vcl(struct vcc *vcc, const char *name) +{ + struct symbol *sym; + + sym = VCC_Symbol(vcc, NULL, name, NULL, SYM_VCL, 1); + AN(sym); + sym->fmt = VCL; + sym->r_methods = VCL_MET_RECV; +} + void VCC_Predef(struct vcc *vcc, const char *type, const char *name) { + + CHECK_OBJ_NOTNULL(vcc, VCC_MAGIC); if (!strcmp(type, "VCL_STEVEDORE")) vcc_stevedore(vcc, name); else if (!strcmp(type, "VCL_VCL")) - return; + vcc_predef_vcl(vcc, name); else WRONG("Unknown VCC predef type"); } From phk at FreeBSD.org Tue Aug 16 09:20:16 2016 From: phk at FreeBSD.org (Poul-Henning Kamp) Date: Tue, 16 Aug 2016 11:20:16 +0200 Subject: [master] e53d920 Add talk-back mechanism so VCC can tell MGR things about the program. Message-ID: commit e53d920ea176ef34898712b695d8d698364014eb Author: Poul-Henning Kamp Date: Tue Aug 16 08:39:37 2016 +0000 Add talk-back mechanism so VCC can tell MGR things about the program. diff --git a/include/libvcc.h b/include/libvcc.h index 1a5125f..78bf5fa 100644 --- a/include/libvcc.h +++ b/include/libvcc.h @@ -30,6 +30,8 @@ struct vcc; +#define VCC_INFO_PREFIX "/* VCC_INFO" + struct vcc *VCC_New(void); void VCC_Allow_InlineC(struct vcc *, unsigned); void VCC_Builtin_VCL(struct vcc *, const char *); diff --git a/lib/libvcc/vcc_action.c b/lib/libvcc/vcc_action.c index 4bcb801..7b9097e 100644 --- a/lib/libvcc/vcc_action.c +++ b/lib/libvcc/vcc_action.c @@ -35,6 +35,7 @@ #include #include "vcc_compile.h" +#include "libvcc.h" /*--------------------------------------------------------------------*/ @@ -253,6 +254,9 @@ parse_return_vcl(struct vcc *tl) } if (sym->eval_priv == NULL) { + VSB_printf(tl->fi, "%s VCL %.*s */\n", + VCC_INFO_PREFIX, PF(tl->t)); + bprintf(buf, "vgc_vcl_%u", tl->unique++); sym->eval_priv = strdup(buf); AN(sym->eval_priv); diff --git a/lib/libvcc/vcc_compile.c b/lib/libvcc/vcc_compile.c index b2542a6..7c155fb 100644 --- a/lib/libvcc/vcc_compile.c +++ b/lib/libvcc/vcc_compile.c @@ -667,6 +667,9 @@ vcc_CompileSource(struct vcc *tl, struct source *sp) vsb = VSB_new_auto(); AN(vsb); + AZ(VSB_finish(tl->fi)); + VSB_cat(vsb, VSB_data(tl->fi)); + vcl_output_lang_h(vsb); EmitCoordinates(tl, vsb); @@ -725,15 +728,15 @@ VCC_New(void) tl->nsources = 0; - /* General C code */ + tl->fi = VSB_new_auto(); + assert(tl->fi != NULL); + tl->fc = VSB_new_auto(); assert(tl->fc != NULL); - /* Forward decls (.h like) */ tl->fh = VSB_new_auto(); assert(tl->fh != NULL); - /* body code of methods */ for (i = 0; i < VCL_MET_MAX; i++) { tl->fm[i] = VSB_new_auto(); assert(tl->fm[i] != NULL); diff --git a/lib/libvcc/vcc_compile.h b/lib/libvcc/vcc_compile.h index 0186e5e..65e7de7 100644 --- a/lib/libvcc/vcc_compile.h +++ b/lib/libvcc/vcc_compile.h @@ -190,6 +190,7 @@ struct vcc { int hindent; unsigned cnt; + struct vsb *fi; /* VCC info to MGR */ struct vsb *fc; /* C-code */ struct vsb *fh; /* H-code (before C-code) */ struct vsb *fb; /* Body of current sub From phk at FreeBSD.org Tue Aug 16 09:20:16 2016 From: phk at FreeBSD.org (Poul-Henning Kamp) Date: Tue, 16 Aug 2016 11:20:16 +0200 Subject: [master] 2d1c467 Track VCL->VCL dependencies Message-ID: commit 2d1c4679a5300e21c23bc2292f8e7be03141250f Author: Poul-Henning Kamp Date: Tue Aug 16 09:02:37 2016 +0000 Track VCL->VCL dependencies diff --git a/bin/varnishd/mgt/mgt.h b/bin/varnishd/mgt/mgt.h index 29ada85..1877424 100644 --- a/bin/varnishd/mgt/mgt.h +++ b/bin/varnishd/mgt/mgt.h @@ -36,6 +36,7 @@ struct cli; struct parspec; struct vcc; +struct vclprog; extern struct vev_base *mgt_evb; extern unsigned d_flag; @@ -170,8 +171,8 @@ void STV_Config(const char *spec); void STV_Config_Transient(void); /* mgt_vcc.c */ -char *mgt_VccCompile(struct cli *, const char *vclname, const char *vclsrc, - const char *vclsrcfile, int C_flag); +char *mgt_VccCompile(struct cli *, struct vclprog *, const char *vclname, + const char *vclsrc, const char *vclsrcfile, int C_flag); void mgt_vcl_init(void); void mgt_vcc_startup(struct cli *, const char *b_arg, const char *f_arg, @@ -179,6 +180,7 @@ void mgt_vcc_startup(struct cli *, const char *b_arg, const char *f_arg, int mgt_push_vcls_and_start(struct cli *, unsigned *status, char **p); void mgt_vcl_export_labels(struct vcc *); int mgt_has_vcl(void); +void mgt_vcl_depends(struct vclprog *vp1, const char *name); extern char *mgt_cc_cmd; extern const char *mgt_vcl_path; extern const char *mgt_vmod_path; diff --git a/bin/varnishd/mgt/mgt_vcc.c b/bin/varnishd/mgt/mgt_vcc.c index 36dbbd4..f8d6224 100644 --- a/bin/varnishd/mgt/mgt_vcc.c +++ b/bin/varnishd/mgt/mgt_vcc.c @@ -47,6 +47,7 @@ #include "vcli_serve.h" #include "vfil.h" #include "vsub.h" +#include "vav.h" #include "vtim.h" struct vcc_priv { @@ -258,12 +259,16 @@ mgt_vcc_compile(struct vcc_priv *vp, struct vsb *sb, int C_flag) /*--------------------------------------------------------------------*/ char * -mgt_VccCompile(struct cli *cli, const char *vclname, const char *vclsrc, - const char *vclsrcfile, int C_flag) +mgt_VccCompile(struct cli *cli, struct vclprog *vcl, const char *vclname, + const char *vclsrc, const char *vclsrcfile, int C_flag) { struct vcc_priv vp; struct vsb *sb; unsigned status; + char buf[1024]; + FILE *fcs; + char **av; + int ac; AN(cli); @@ -339,10 +344,9 @@ mgt_VccCompile(struct cli *cli, const char *vclname, const char *vclsrc, VCLI_Out(cli, "%s", VSB_data(sb)); VSB_destroy(&sb); - (void)unlink(vp.csrcfile); - free(vp.csrcfile); - if (status || C_flag) { + (void)unlink(vp.csrcfile); + free(vp.csrcfile); (void)unlink(vp.libfile); free(vp.libfile); (void)rmdir(vp.dir); @@ -354,6 +358,26 @@ mgt_VccCompile(struct cli *cli, const char *vclname, const char *vclsrc, return (NULL); } + fcs = fopen(vp.csrcfile, "r"); + AN(fcs); + while (1) { + AN(fgets(buf, sizeof buf, fcs)); + if (memcmp(buf, VCC_INFO_PREFIX, strlen(VCC_INFO_PREFIX))) + break; + av = VAV_Parse(buf, &ac, 0); + AN(av); + AZ(av[0]); + AZ(strcmp(av[1], "/*")); + AZ(strcmp(av[ac-1], "*/")); + AZ(strcmp(av[3], "VCL")); + mgt_vcl_depends(vcl, av[4]); + VAV_Free(av); + } + AZ(fclose(fcs)); + + (void)unlink(vp.csrcfile); + free(vp.csrcfile); + free(vp.dir); VCLI_Out(cli, "VCL compiled.\n"); diff --git a/bin/varnishd/mgt/mgt_vcl.c b/bin/varnishd/mgt/mgt_vcl.c index 245fa54..6b873a5 100644 --- a/bin/varnishd/mgt/mgt_vcl.c +++ b/bin/varnishd/mgt/mgt_vcl.c @@ -109,7 +109,7 @@ mgt_vcl_dep_del(struct vcldep *vd) /*--------------------------------------------------------------------*/ static struct vclprog * -mgt_vcl_add(const char *name, const char *libfile, const char *state) +mgt_vcl_add(const char *name, const char *state) { struct vclprog *vp; @@ -120,7 +120,6 @@ mgt_vcl_add(const char *name, const char *libfile, const char *state) ALLOC_OBJ(vp, VCLPROG_MAGIC); XXXAN(vp); REPLACE(vp->name, name); - REPLACE(vp->fname, libfile); VTAILQ_INIT(&vp->dfrom); VTAILQ_INIT(&vp->dto); vp->state = state; @@ -144,7 +143,7 @@ mgt_vcl_del(struct vclprog *vp) mgt_vcl_dep_del(VTAILQ_FIRST(&vp->dfrom)); VTAILQ_REMOVE(&vclhead, vp, list); - if (strcmp(vp->state, VCL_STATE_LABEL)) { + if (vp->fname != NULL) { AZ(unlink(vp->fname)); p = strrchr(vp->fname, '/'); AN(p); @@ -175,6 +174,18 @@ mgt_vcl_byname(const char *name) return (NULL); } +void +mgt_vcl_depends(struct vclprog *vp1, const char *name) +{ + struct vclprog *vp2; + + CHECK_OBJ_NOTNULL(vp1, VCLPROG_MAGIC); + + vp2 = mgt_vcl_byname(name); + CHECK_OBJ_NOTNULL(vp2, VCLPROG_MAGIC); + mgt_vcl_dep_add(vp1, vp2); +} + int mgt_has_vcl(void) { @@ -261,13 +272,15 @@ mgt_new_vcl(struct cli *cli, const char *vclname, const char *vclsrc, return; } - lib = mgt_VccCompile(cli, vclname, vclsrc, vclsrcfile, C_flag); - if (lib == NULL) + vp = mgt_vcl_add(vclname, state); + lib = mgt_VccCompile(cli, vp, vclname, vclsrc, vclsrcfile, C_flag); + if (lib == NULL) { + mgt_vcl_del(vp); return; + } AZ(C_flag); - vp = mgt_vcl_add(vclname, lib, state); - free(lib); + vp->fname = lib; if (child_pid < 0) return; @@ -311,8 +324,10 @@ void mgt_vcl_export_labels(struct vcc *vcc) { struct vclprog *vp; - VTAILQ_FOREACH(vp, &vclhead, list) - VCC_Predef(vcc, "VCL_VCL", vp->name); + VTAILQ_FOREACH(vp, &vclhead, list) { + if (!strcmp(vp->state, VCL_STATE_LABEL)) + VCC_Predef(vcc, "VCL_VCL", vp->name); + } } /*--------------------------------------------------------------------*/ @@ -493,6 +508,8 @@ mcf_vcl_discard(struct cli *cli, const char * const *av, void *priv) unsigned status; char *p = NULL; struct vclprog *vp; + struct vcldep *vd; + int n; (void)priv; vp = mcf_find_vcl(cli, av[2]); @@ -504,7 +521,7 @@ mcf_vcl_discard(struct cli *cli, const char * const *av, void *priv) return; } if (!VTAILQ_EMPTY(&vp->dto)) { - if (vp->label != NULL) { + if (vp->label != NULL && strcmp(vp->state, VCL_STATE_LABEL)) { AN(vp->warm); VCLI_SetResult(cli, CLIS_PARAM); VCLI_Out(cli, @@ -512,7 +529,19 @@ mcf_vcl_discard(struct cli *cli, const char * const *av, void *priv) vp->label->name); return; } - INCOMPL(); + VCLI_SetResult(cli, CLIS_PARAM); + VCLI_Out(cli, + "Cannot discard \"%s\" VCL label, " + "other VCLs depend on it.\n", vp->name); + n = 0; + VTAILQ_FOREACH(vd, &vp->dto, lto) { + if (n++ == 5) { + VCLI_Out(cli, "\t[...]"); + break; + } + VCLI_Out(cli, "\t%s\n", vd->from->name); + } + return; } if (!strcmp(vp->state, VCL_STATE_LABEL)) { AN(vp->warm); @@ -600,7 +629,13 @@ mcf_vcl_label(struct cli *cli, const char * const *av, void *priv) mgt_vcl_dep_del(VTAILQ_FIRST(&vpl->dfrom)); AN(VTAILQ_EMPTY(&vpl->dfrom)); } else { - vpl = mgt_vcl_add(av[2], NULL, VCL_STATE_LABEL); + /* XXX should check for C-syntax */ + if (strchr(av[2], '.')) { + VCLI_SetResult(cli, CLIS_PARAM); + VCLI_Out(cli, "VCL labels cannot contain '.'"); + return; + } + vpl = mgt_vcl_add(av[2], VCL_STATE_LABEL); } AN(vpl); mgt_vcl_dep_add(vpl, vpt); From phk at FreeBSD.org Tue Aug 16 09:20:16 2016 From: phk at FreeBSD.org (Poul-Henning Kamp) Date: Tue, 16 Aug 2016 11:20:16 +0200 Subject: [master] a4c8ffa Enable VCL switching from VCL Message-ID: commit a4c8ffa4bc15d4d3fb12c9ef7475c1458115a259 Author: Poul-Henning Kamp Date: Tue Aug 16 09:19:24 2016 +0000 Enable VCL switching from VCL diff --git a/bin/varnishd/cache/cache_req_fsm.c b/bin/varnishd/cache/cache_req_fsm.c index fa9d8f6..e7fec41 100644 --- a/bin/varnishd/cache/cache_req_fsm.c +++ b/bin/varnishd/cache/cache_req_fsm.c @@ -688,6 +688,8 @@ cnt_recv(struct worker *wrk, struct req *req) } VCL_recv_method(req->vcl, wrk, req, NULL, NULL); + if (wrk->handling == VCL_RET_VCL) + VCL_recv_method(req->vcl, wrk, req, NULL, NULL); /* Attempts to cache req.body may fail */ if (req->req_body_status == REQ_BODY_FAIL) { @@ -714,6 +716,12 @@ cnt_recv(struct worker *wrk, struct req *req) SHA256_Final(req->digest, &sha256ctx); switch(recv_handling) { + case VCL_RET_VCL: + VSLb(req->vsl, SLT_VCL_Error, + "return(vcl) only allowed from active (top) VCL."); + req->err_code = 503; + req->req_step = R_STP_SYNTH; + return (REQ_FSM_MORE); case VCL_RET_PURGE: req->req_step = R_STP_PURGE; return (REQ_FSM_MORE); diff --git a/bin/varnishd/cache/cache_vcl.c b/bin/varnishd/cache/cache_vcl.c index edba7b0..5afc65a 100644 --- a/bin/varnishd/cache/cache_vcl.c +++ b/bin/varnishd/cache/cache_vcl.c @@ -453,6 +453,8 @@ VRT_vcl_select(VRT_CTX, VCL_VCL vcl) CHECK_OBJ_NOTNULL(vcl, VCL_MAGIC); VCL_Rel(&req->vcl); vcl_get(&req->vcl, vcl); + /* XXX: better logging */ + VSLb(ctx->req->vsl, SLT_Debug, "Now using %s VCL", vcl->loaded_name); } struct vclref * diff --git a/bin/varnishtest/tests/c00077.vtc b/bin/varnishtest/tests/c00077.vtc new file mode 100644 index 0000000..005a17d --- /dev/null +++ b/bin/varnishtest/tests/c00077.vtc @@ -0,0 +1,71 @@ +varnishtest "Switching VCL from VCL" + +server s1 { + rxreq + txresp +} -start + +varnish v1 -vcl+backend { + sub vcl_deliver { + set resp.http.vcl = "vclA"; + } +} -start + +varnish v1 -clierr 106 "vcl.label vcl.A vcl1" +varnish v1 -cliok "vcl.label vclA vcl1" + +varnish v1 -vcl+backend { + sub vcl_recv { + if (req.http.vcl == "vcl1") { + return (vcl(vclA)); + } + } + sub vcl_deliver { + set resp.http.vcl = "vcl2"; + } +} +varnish v1 -cliok "vcl.label vclB vcl2" +varnish v1 -cliok "vcl.list" + +client c1 { + txreq + rxresp + expect resp.http.vcl == vcl2 + + txreq -hdr "vcl: vcl1" + rxresp + expect resp.http.vcl == vclA +} -run + +varnish v1 -clierr 106 "vcl.discard vcl1" + +varnish v1 -vcl+backend { sub vcl_recv { return (vcl(vclB)); } } + +client c1 { + txreq -hdr "vcl: vcl1" + rxresp + expect resp.status == 503 +} -run + +delay .2 + +varnish v1 -vcl+backend { sub vcl_recv { return (vcl(vclA)); } } +varnish v1 -vcl+backend { sub vcl_recv { return (vcl(vclA)); } } +varnish v1 -vcl+backend { sub vcl_recv { return (vcl(vclA)); } } +varnish v1 -vcl+backend { sub vcl_recv { return (vcl(vclA)); } } + +varnish v1 -clierr 106 "vcl.discard vclA" + +varnish v1 -vcl+backend { } +varnish v1 -cliok "vcl.discard vcl3" +varnish v1 -cliok "vcl.discard vcl4" +varnish v1 -cliok "vcl.discard vcl5" +varnish v1 -cliok "vcl.discard vcl6" +varnish v1 -cliok "vcl.discard vcl7" + +varnish v1 -cliok "vcl.discard vclB" +varnish v1 -cliok "vcl.discard vcl2" +varnish v1 -cliok "vcl.discard vclA" +varnish v1 -cliok "vcl.discard vcl1" + + diff --git a/lib/libvcc/generate.py b/lib/libvcc/generate.py index d5b5f00..a34d1e3 100755 --- a/lib/libvcc/generate.py +++ b/lib/libvcc/generate.py @@ -85,7 +85,7 @@ returns = ( ('recv', "C", - ('synth', 'pass', 'pipe', 'hash', 'purge',) + ('synth', 'pass', 'pipe', 'hash', 'purge', 'vcl') ), ('pipe', "C", From phk at FreeBSD.org Tue Aug 16 09:45:11 2016 From: phk at FreeBSD.org (Poul-Henning Kamp) Date: Tue, 16 Aug 2016 11:45:11 +0200 Subject: [master] a4495e6 Cast to char* for GCC Message-ID: commit a4495e6889e067ae273f828432a66005d42017d5 Author: Poul-Henning Kamp Date: Tue Aug 16 09:44:28 2016 +0000 Cast to char* for GCC diff --git a/lib/libvcc/vcc_action.c b/lib/libvcc/vcc_action.c index 7b9097e..200ba97 100644 --- a/lib/libvcc/vcc_action.c +++ b/lib/libvcc/vcc_action.c @@ -253,7 +253,6 @@ parse_return_vcl(struct vcc *tl) return; } if (sym->eval_priv == NULL) { - VSB_printf(tl->fi, "%s VCL %.*s */\n", VCC_INFO_PREFIX, PF(tl->t)); @@ -261,16 +260,16 @@ parse_return_vcl(struct vcc *tl) sym->eval_priv = strdup(buf); AN(sym->eval_priv); - Fh(tl, 0, "static VCL_VCL %s;", sym->eval_priv); + Fh(tl, 0, "static VCL_VCL %s;", buf); Fh(tl, 0, "\t/* VCL %.*s */\n", PF(tl->t)); p = New_IniFin(tl); AN(p); VSB_printf(p->ini, "\t%s = VRT_vcl_lookup(\"%.*s\");", - sym->eval_priv, PF(tl->t)); + buf, PF(tl->t)); } Fb(tl, 1, "VRT_vcl_select(ctx, %s);\t/* %.*s */\n", - sym->eval_priv, PF(tl->t)); + (const char*)sym->eval_priv, PF(tl->t)); Fb(tl, 1, "VRT_handling(ctx, VCL_RET_VCL);\n"); Fb(tl, 1, "return (1);\n"); vcc_NextToken(tl); From phk at FreeBSD.org Tue Aug 16 13:28:09 2016 From: phk at FreeBSD.org (Poul-Henning Kamp) Date: Tue, 16 Aug 2016 15:28:09 +0200 Subject: [master] b6a6e86 Document how to use separate VCL files Message-ID: commit b6a6e865a68c12c68abea2b30a76ecfc478d8cb1 Author: Poul-Henning Kamp Date: Tue Aug 16 13:27:00 2016 +0000 Document how to use separate VCL files diff --git a/doc/sphinx/users-guide/vcl-separate.rst b/doc/sphinx/users-guide/vcl-separate.rst new file mode 100644 index 0000000..15873aa --- /dev/null +++ b/doc/sphinx/users-guide/vcl-separate.rst @@ -0,0 +1,92 @@ +.. _users-guide-separate_VCL: + +Separate VCL files +================== + +Having multiple different domains in the same Varnish is a very +typical use-case, and from Varnish 5.0 it is possible to have +a separate VCL files for separate domains or any other distinct +subset of requests. + +Assume that we want to handle ``varnish.org`` with one VCL file +and ``varnish-cache.org`` with another VCL file. + +First load the two VCL files:: + + vcl.load vo_1 /somewhere/vo.vcl + vcl.load vc_1 /somewhere/vc.vcl + +These are 100% normal VCL files, as they would look if you ran +only that single domain on your varnish instance. + +Next we need to point VCL labels to them:: + + vcl.label l_vo vo_1 + vcl.label l_vc vc_1 + +Next we write the top-level VCL program, which branches out +to the other two, depending on the Host: header in the +request:: + + /* We have to have a backend, even if we do not use it */ + backend default { .host = "127.0.0.1"; } + + sub vcl_recv { + if (req.http.host ~ "varnish.org$") { + return (vcl(l_vo)); + } + if (req.http.host ~ "varnish-cache.org$") { + return (vcl(l_vc)); + } + return (synth(302, "http://varnish-cache.org")); + } + + sub vcl_synth { + if (resp.status == 301 || resp.status == 302) { + set resp.http.location = resp.reason; + set resp.reason = "Moved"; + return (deliver); + } + } + +Finally, we load the top level VCL and make it the +active VCL:: + + vcl.load top_1 /somewhere/top.vcl + vcl.use top_1 + +If you want to update one of the separated VCLs, you load the new +one and change the label to point to it:: + + vcl.load vo_2 /somewhere/vo.vcl + vcl.label l_vo vo_2 + +If you want to change the top level VCL, do as you always did:: + + vcl.load top_2 /somewhere/top.vcl + vcl.use top_2 + + + +Details, details, details: +-------------------------- + +* All requests *always* start in the active VCL - the one from ``vcl.use`` + +* Only VCL labels can be used in ``return(vcl(name))``. Without this + restriction the top level VCL would have to be reloaded every time + one of the separate VCLs were changed. + +* You can only switch VCLs from the active VCL. If you try it from one of + the separate VCLs, you will get a 503 + +* You cannot remove VCL labels (with ``vcl.discard``) if any VCL + contains ``return(vcl(name_of_that_label))`` + +* You cannot remove VCLs which have a label attached to them. + +* This code is tested in testcase c00077 + +* This is a very new feature, it may change + +* We would very much like feedback how this works for you diff --git a/doc/sphinx/users-guide/vcl.rst b/doc/sphinx/users-guide/vcl.rst index 0458704..f38966b 100644 --- a/doc/sphinx/users-guide/vcl.rst +++ b/doc/sphinx/users-guide/vcl.rst @@ -41,6 +41,7 @@ code commented out in the file `builtin.vcl` that ships with Varnish Cache. vcl-backends vcl-hashing vcl-grace + vcl-separate vcl-inline-c vcl-examples devicedetection From phk at FreeBSD.org Tue Aug 16 13:44:10 2016 From: phk at FreeBSD.org (Poul-Henning Kamp) Date: Tue, 16 Aug 2016 15:44:10 +0200 Subject: [master] f40d39a Change the title to just "Varnish Documentation" Message-ID: commit f40d39a77c794a5569bdd94ac4640e00ec544406 Author: Poul-Henning Kamp Date: Tue Aug 16 13:43:34 2016 +0000 Change the title to just "Varnish Documentation" diff --git a/doc/sphinx/index.rst b/doc/sphinx/index.rst index cc94f1c..07077ee 100644 --- a/doc/sphinx/index.rst +++ b/doc/sphinx/index.rst @@ -1,6 +1,6 @@ -Varnish Administrator Documentation -=================================== +Varnish Documentation +===================== Varnish Cache is a web application accelerator also known as a caching HTTP reverse proxy. You install it in front of any server that speaks HTTP and From dridi.boukelmoune at gmail.com Thu Aug 18 16:45:08 2016 From: dridi.boukelmoune at gmail.com (Dridi Boukelmoune) Date: Thu, 18 Aug 2016 18:45:08 +0200 Subject: [master] a194ba8 Enforce identical naming for command functions Message-ID: commit a194ba851dd1b7ab27b6efe4ba6ab6dc36a8ce55 Author: Dridi Boukelmoune Date: Thu Aug 18 18:40:25 2016 +0200 Enforce identical naming for command functions diff --git a/bin/varnishtest/vtc.c b/bin/varnishtest/vtc.c index e7a40ef..2958867 100644 --- a/bin/varnishtest/vtc.c +++ b/bin/varnishtest/vtc.c @@ -674,7 +674,7 @@ static const struct cmds cmds[] = { { "barrier", cmd_barrier }, { "random", cmd_random }, { "feature", cmd_feature }, - { "logexpect", cmd_logexp }, + { "logexpect", cmd_logexpect }, { "process", cmd_process }, { NULL, NULL } }; diff --git a/bin/varnishtest/vtc.h b/bin/varnishtest/vtc.h index b2b730a..14053c8 100644 --- a/bin/varnishtest/vtc.h +++ b/bin/varnishtest/vtc.h @@ -63,7 +63,7 @@ cmd_f cmd_server; cmd_f cmd_client; cmd_f cmd_varnish; cmd_f cmd_barrier; -cmd_f cmd_logexp; +cmd_f cmd_logexpect; cmd_f cmd_process; extern volatile sig_atomic_t vtc_error; /* Error, bail out */ diff --git a/bin/varnishtest/vtc_logexp.c b/bin/varnishtest/vtc_logexp.c index 338c117..eb2843a 100644 --- a/bin/varnishtest/vtc_logexp.c +++ b/bin/varnishtest/vtc_logexp.c @@ -519,7 +519,7 @@ logexp_spec(struct logexp *le, const char *spec) } void -cmd_logexp(CMD_ARGS) +cmd_logexpect(CMD_ARGS) { struct logexp *le, *le2; const char tmpdir[] = "${tmpdir}"; From dridi.boukelmoune at gmail.com Thu Aug 18 16:45:08 2016 From: dridi.boukelmoune at gmail.com (Dridi Boukelmoune) Date: Thu, 18 Aug 2016 18:45:08 +0200 Subject: [master] 29247f7 Remove redundancy with varnishtest commands Message-ID: commit 29247f761cf95a98bd59f7cb4b2e6287282dce94 Author: Dridi Boukelmoune Date: Thu Aug 18 18:42:03 2016 +0200 Remove redundancy with varnishtest commands diff --git a/bin/varnishtest/vtc.c b/bin/varnishtest/vtc.c index 2958867..06b3421 100644 --- a/bin/varnishtest/vtc.c +++ b/bin/varnishtest/vtc.c @@ -664,18 +664,20 @@ cmd_feature(CMD_ARGS) */ static const struct cmds cmds[] = { - { "server", cmd_server }, - { "client", cmd_client }, - { "varnish", cmd_varnish }, - { "delay", cmd_delay }, - { "varnishtest",cmd_varnishtest }, - { "shell", cmd_shell }, - { "err_shell", cmd_err_shell }, - { "barrier", cmd_barrier }, - { "random", cmd_random }, - { "feature", cmd_feature }, - { "logexpect", cmd_logexpect }, - { "process", cmd_process }, +#define CMD(n) { #n, cmd_##n } + CMD(server), + CMD(client), + CMD(varnish), + CMD(delay), + CMD(varnishtest), + CMD(shell), + CMD(err_shell), + CMD(barrier), + CMD(random), + CMD(feature), + CMD(logexpect), + CMD(process), +#undef CMD { NULL, NULL } }; diff --git a/bin/varnishtest/vtc.h b/bin/varnishtest/vtc.h index 14053c8..b872e89 100644 --- a/bin/varnishtest/vtc.h +++ b/bin/varnishtest/vtc.h @@ -58,13 +58,15 @@ struct cmds { void parse_string(const char *spec, const struct cmds *cmd, void *priv, struct vtclog *vl); -cmd_f cmd_delay; -cmd_f cmd_server; -cmd_f cmd_client; -cmd_f cmd_varnish; -cmd_f cmd_barrier; -cmd_f cmd_logexpect; -cmd_f cmd_process; +#define CMD(n) cmd_f cmd_##n +CMD(delay); +CMD(server); +CMD(client); +CMD(varnish); +CMD(barrier); +CMD(logexpect); +CMD(process); +#undef CMD extern volatile sig_atomic_t vtc_error; /* Error, bail out */ extern int vtc_stop; /* Abandon current test, no error */ From dridi.boukelmoune at gmail.com Sat Aug 20 16:31:07 2016 From: dridi.boukelmoune at gmail.com (Dridi Boukelmoune) Date: Sat, 20 Aug 2016 18:31:07 +0200 Subject: [master] 70a6c91 Fix redundant error message Message-ID: commit 70a6c91101561d8f285cf7c33bae9556cec843e9 Author: Dridi Boukelmoune Date: Sat Aug 20 18:29:26 2016 +0200 Fix redundant error message diff --git a/bin/varnishd/mgt/mgt_jail.c b/bin/varnishd/mgt/mgt_jail.c index 4a71ea7..b0cd91d 100644 --- a/bin/varnishd/mgt/mgt_jail.c +++ b/bin/varnishd/mgt/mgt_jail.c @@ -154,7 +154,7 @@ VJ_make_workdir(const char *dname) fd = open("_.testfile", O_RDWR|O_CREAT|O_EXCL, 0600); if (fd < 0) - ARGV_ERR("Error: Cannot create test-file in %s (%s)\n" + ARGV_ERR("Cannot create test-file in %s (%s)\n" "Check permissions (or delete old directory)\n", dname, strerror(errno)); AZ(close(fd)); From nils.goroll at uplex.de Mon Aug 22 12:19:08 2016 From: nils.goroll at uplex.de (Nils Goroll) Date: Mon, 22 Aug 2016 14:19:08 +0200 Subject: [master] 145824d ditch an ugly workaround for rhel5 Message-ID: commit 145824de4a13837ca99d1367103d79178348f352 Author: Nils Goroll Date: Mon Aug 22 14:12:07 2016 +0200 ditch an ugly workaround for rhel5 test "x$ac_cv_prog_cc_c99" = "x" breaks autoconf for compilers which do not need any extra arguments to support c99. Partly addresses #2047 diff --git a/configure.ac b/configure.ac index 2ae3f99..e5c6a40 100644 --- a/configure.ac +++ b/configure.ac @@ -25,22 +25,6 @@ AC_PROG_LIBTOOL AC_GNU_SOURCE AC_PROG_CC AC_PROG_CC_STDC -if test "x$ac_cv_prog_cc_c99" = "xno" || test "x$ac_cv_prog_cc_c99" = "x"; then -# We might be on RHEL5 with a git checkout and so broken -# autoconf. Check if CC is gcc and if it bails when given -std=gnu99. -# If not, use that. Yuck. - if test "x$ac_cv_c_compiler_gnu" = "xyes"; then - CC="$CC -std=gnu99" - AC_RUN_IFELSE( - [AC_LANG_PROGRAM([],[[ -return 0; - ]])], - [], - [AC_MSG_ERROR([Could not find a C99 compatible compiler])]) - else - AC_MSG_ERROR([Could not find a C99 compatible compiler]) - fi -fi AC_PROG_CPP AX_PTHREAD(,[AC_MSG_ERROR([Could not configure pthreads support])]) From nils.goroll at uplex.de Mon Aug 22 13:04:10 2016 From: nils.goroll at uplex.de (Nils Goroll) Date: Mon, 22 Aug 2016 15:04:10 +0200 Subject: [master] b3f801d fix typo: actually save LIBS in save_LIBS Message-ID: commit b3f801d4e59e2901390bbb34f2ca7fd0cd64934a Author: Nils Goroll Date: Mon Aug 22 14:56:58 2016 +0200 fix typo: actually save LIBS in save_LIBS diff --git a/configure.ac b/configure.ac index e5c6a40..5f3e3bf 100644 --- a/configure.ac +++ b/configure.ac @@ -145,7 +145,7 @@ if test "$enable_pcre_jit" = yes; then AC_MSG_CHECKING(for PCRE JIT usability) save_CFLAGS="${CFLAGS}" CFLAGS="${PCRE_CFLAGS}" - save_LIBS="${CFLAGS}" + save_LIBS="${LIBS}" LIBS="${PCRE_LIBS}" AC_RUN_IFELSE( [AC_LANG_PROGRAM([[ From nils.goroll at uplex.de Mon Aug 22 13:04:10 2016 From: nils.goroll at uplex.de (Nils Goroll) Date: Mon, 22 Aug 2016 15:04:10 +0200 Subject: [master] ab3ab02 When test-compiling to check for jit, also use CFLAGS and LIBS Message-ID: commit ab3ab0201ea55e337aedfd2e9b78cd18af30522b Author: Nils Goroll Date: Mon Aug 22 14:59:56 2016 +0200 When test-compiling to check for jit, also use CFLAGS and LIBS rather than the values from pkg-config only. CLAGS and LIBS may contain important generic compiler / linker flags for this platform. diff --git a/configure.ac b/configure.ac index 5f3e3bf..fb34c13 100644 --- a/configure.ac +++ b/configure.ac @@ -144,9 +144,9 @@ AC_ARG_ENABLE(pcre-jit, if test "$enable_pcre_jit" = yes; then AC_MSG_CHECKING(for PCRE JIT usability) save_CFLAGS="${CFLAGS}" - CFLAGS="${PCRE_CFLAGS}" + CFLAGS="${CFLAGS} ${PCRE_CFLAGS}" save_LIBS="${LIBS}" - LIBS="${PCRE_LIBS}" + LIBS="${LIBS} ${PCRE_LIBS}" AC_RUN_IFELSE( [AC_LANG_PROGRAM([[ #include From nils.goroll at uplex.de Mon Aug 22 13:57:12 2016 From: nils.goroll at uplex.de (Nils Goroll) Date: Mon, 22 Aug 2016 15:57:12 +0200 Subject: [master] 52d3477 Don't assume our compiler likes -Wall -Werror Message-ID: commit 52d3477dceb2d8d14257dd808db7677cc99f0d97 Author: Nils Goroll Date: Mon Aug 22 15:20:15 2016 +0200 Don't assume our compiler likes -Wall -Werror and add a -Werror replacement for Solaris Studio. This only makes Solaris Studio work with the default options, not for --enable-developer-warnings Partly addresses #2047 diff --git a/configure.ac b/configure.ac index fb34c13..fbbb0b8 100644 --- a/configure.ac +++ b/configure.ac @@ -572,8 +572,22 @@ gl_LD_VERSION_SCRIPT # The reason for -Wno-error=unused-result is a glibc/gcc interaction # idiocy where write is marked as warn_unused_result, causing build # failures. -CFLAGS="${CFLAGS} -Wall -Werror" -OCFLAGS="${OCFLAGS} -Wall -Werror" + +AX_CHECK_COMPILE_FLAG([-Wall], + [CFLAGS="${CFLAGS} -Wall" + OCFLAGS="${OCFLAGS} -Wall"]) + +SUNCC_CFLAGS=" \ + -errwarn=%all,no%E_EMPTY_TRANSLATION_UNIT,no%E_ATTRIBUTE_UNKNOWN,no%E_STATEMENT_NOT_REACHED,no%E_EMPTY_DECLARATION \ + -errtags=yes \ + " +AX_CHECK_COMPILE_FLAG([-Werror], + [CFLAGS="${CFLAGS} -Werror" + OCFLAGS="${OCFLAGS} -Werror"], + [AX_CHECK_COMPILE_FLAG([${SUNCC_CFLAGS}], + [CFLAGS="${CFLAGS} ${SUNCC_CFLAGS}" + OCFLAGS="${OCFLAGS} ${SUNCC_CFLAGS}"])]) + AX_CHECK_COMPILE_FLAG([-Werror=unused-result], [CFLAGS="${CFLAGS} -Wno-error=unused-result" OCFLAGS="${OCFLAGS} -Wno-error=unused-result"], From nils.goroll at uplex.de Mon Aug 22 13:57:12 2016 From: nils.goroll at uplex.de (Nils Goroll) Date: Mon, 22 Aug 2016 15:57:12 +0200 Subject: [master] cfaa81d remove unused __VA_ARGS__ Message-ID: commit cfaa81dd9a5f74510aea22884a4340a80db7abdb Author: Nils Goroll Date: Mon Aug 22 15:49:27 2016 +0200 remove unused __VA_ARGS__ The P macro is only ever called with one argument and Solaris cc rightly emits E_ARGUEMENT_MISMATCH (yes, with the typo) diff --git a/bin/varnishstat/vsc2rst.c b/bin/varnishstat/vsc2rst.c index 2aaa16d..f955b78 100644 --- a/bin/varnishstat/vsc2rst.c +++ b/bin/varnishstat/vsc2rst.c @@ -36,8 +36,8 @@ #include "tbl/vsc_levels.h" #undef VSC_LEVEL_F -#define P(x, ...) \ - printf(x "\n", ##__VA_ARGS__) +#define P(x) \ + printf(x "\n") #define VSC_LEVEL_F(v,l,e,d) \ printf("%s ? %s\n\t%s\n\n", l, e, d); #define VSC_F(n, t, l, s, f, v, d, e) \ From nils.goroll at uplex.de Mon Aug 22 14:26:13 2016 From: nils.goroll at uplex.de (Nils Goroll) Date: Mon, 22 Aug 2016 16:26:13 +0200 Subject: [master] f5b448b update ax_pthread.m4 from upstream Message-ID: commit f5b448b5d258ff26240af74111411b07ebfcb11b Author: Nils Goroll Date: Mon Aug 22 16:24:20 2016 +0200 update ax_pthread.m4 from upstream This should get CFLAGS right for Solaris Studio relevant for #2047 diff --git a/m4/ax_pthread.m4 b/m4/ax_pthread.m4 index bdae477..4c4051e 100644 --- a/m4/ax_pthread.m4 +++ b/m4/ax_pthread.m4 @@ -19,10 +19,10 @@ # is necessary on AIX to use the special cc_r compiler alias.) # # NOTE: You are assumed to not only compile your program with these flags, -# but also link it with them as well. e.g. you should link with +# but also to link with them as well. For example, you might link with # $PTHREAD_CC $CFLAGS $PTHREAD_CFLAGS $LDFLAGS ... $PTHREAD_LIBS $LIBS # -# If you are only building threads programs, you may wish to use these +# If you are only building threaded programs, you may wish to use these # variables in your default LIBS, CFLAGS, and CC: # # LIBS="$PTHREAD_LIBS $LIBS" @@ -30,8 +30,12 @@ # CC="$PTHREAD_CC" # # In addition, if the PTHREAD_CREATE_JOINABLE thread-attribute constant -# has a nonstandard name, defines PTHREAD_CREATE_JOINABLE to that name -# (e.g. PTHREAD_CREATE_UNDETACHED on AIX). +# has a nonstandard name, this macro defines PTHREAD_CREATE_JOINABLE to +# that name (e.g. PTHREAD_CREATE_UNDETACHED on AIX). +# +# Also HAVE_PTHREAD_PRIO_INHERIT is defined if pthread is found and the +# PTHREAD_PRIO_INHERIT symbol is defined when compiling with +# PTHREAD_CFLAGS. # # ACTION-IF-FOUND is a list of shell commands to run if a threads library # is found, and ACTION-IF-NOT-FOUND is a list of commands to run it if it @@ -45,9 +49,12 @@ # Alejandro Forero Cuervo to the autoconf macro repository. We are also # grateful for the helpful feedback of numerous users. # +# Updated for Autoconf 2.68 by Daniel Richard G. +# # LICENSE # # Copyright (c) 2008 Steven G. Johnson +# Copyright (c) 2011 Daniel Richard G. # # This program is free software: you can redistribute it and/or modify it # under the terms of the GNU General Public License as published by the @@ -75,36 +82,40 @@ # modified version of the Autoconf Macro, you may extend this special # exception to the GPL to apply to your modified version as well. -#serial 7 +#serial 23 AU_ALIAS([ACX_PTHREAD], [AX_PTHREAD]) AC_DEFUN([AX_PTHREAD], [ AC_REQUIRE([AC_CANONICAL_HOST]) -AC_LANG_SAVE -AC_LANG_C +AC_REQUIRE([AC_PROG_CC]) +AC_REQUIRE([AC_PROG_SED]) +AC_LANG_PUSH([C]) ax_pthread_ok=no # We used to check for pthread.h first, but this fails if pthread.h -# requires special compiler flags (e.g. on True64 or Sequent). +# requires special compiler flags (e.g. on Tru64 or Sequent). # It gets checked for in the link test anyway. # First of all, check if the user has set any of the PTHREAD_LIBS, # etcetera environment variables, and if threads linking works using # them: -if test x"$PTHREAD_LIBS$PTHREAD_CFLAGS" != x; then - save_CFLAGS="$CFLAGS" +if test "x$PTHREAD_CFLAGS$PTHREAD_LIBS" != "x"; then + ax_pthread_save_CC="$CC" + ax_pthread_save_CFLAGS="$CFLAGS" + ax_pthread_save_LIBS="$LIBS" + AS_IF([test "x$PTHREAD_CC" != "x"], [CC="$PTHREAD_CC"]) CFLAGS="$CFLAGS $PTHREAD_CFLAGS" - save_LIBS="$LIBS" LIBS="$PTHREAD_LIBS $LIBS" - AC_MSG_CHECKING([for pthread_join in LIBS=$PTHREAD_LIBS with CFLAGS=$PTHREAD_CFLAGS]) - AC_TRY_LINK_FUNC(pthread_join, ax_pthread_ok=yes) - AC_MSG_RESULT($ax_pthread_ok) - if test x"$ax_pthread_ok" = xno; then + AC_MSG_CHECKING([for pthread_join using $CC $PTHREAD_CFLAGS $PTHREAD_LIBS]) + AC_LINK_IFELSE([AC_LANG_CALL([], [pthread_join])], [ax_pthread_ok=yes]) + AC_MSG_RESULT([$ax_pthread_ok]) + if test "x$ax_pthread_ok" = "xno"; then PTHREAD_LIBS="" PTHREAD_CFLAGS="" fi - LIBS="$save_LIBS" - CFLAGS="$save_CFLAGS" + CC="$ax_pthread_save_CC" + CFLAGS="$ax_pthread_save_CFLAGS" + LIBS="$ax_pthread_save_LIBS" fi # We must check for the threads library under a number of different @@ -117,7 +128,7 @@ fi # which indicates that we try without any flags at all, and "pthread-config" # which is a program returning the flags for the Pth emulation library. -ax_pthread_flags="pthreads none -Kthread -kthread lthread -pthread -pthreads -mthreads pthread --thread-safe -mt pthread-config" +ax_pthread_flags="pthreads none -Kthread -pthread -pthreads -mthreads pthread --thread-safe -mt pthread-config" # The ordering *is* (sometimes) important. Some notes on the # individual items follow: @@ -126,68 +137,225 @@ ax_pthread_flags="pthreads none -Kthread -kthread lthread -pthread -pthreads -mt # none: in case threads are in libc; should be tried before -Kthread and # other compiler flags to prevent continual compiler warnings # -Kthread: Sequent (threads in libc, but -Kthread needed for pthread.h) -# -kthread: FreeBSD kernel threads (preferred to -pthread since SMP-able) -# lthread: LinuxThreads port on FreeBSD (also preferred to -pthread) -# -pthread: Linux/gcc (kernel threads), BSD/gcc (userland threads) -# -pthreads: Solaris/gcc -# -mthreads: Mingw32/gcc, Lynx/gcc +# -pthread: Linux/gcc (kernel threads), BSD/gcc (userland threads), Tru64 +# (Note: HP C rejects this with "bad form for `-t' option") +# -pthreads: Solaris/gcc (Note: HP C also rejects) # -mt: Sun Workshop C (may only link SunOS threads [-lthread], but it -# doesn't hurt to check since this sometimes defines pthreads too; -# also defines -D_REENTRANT) -# ... -mt is also the pthreads flag for HP/aCC +# doesn't hurt to check since this sometimes defines pthreads and +# -D_REENTRANT too), HP C (must be checked before -lpthread, which +# is present but should not be used directly; and before -mthreads, +# because the compiler interprets this as "-mt" + "-hreads") +# -mthreads: Mingw32/gcc, Lynx/gcc # pthread: Linux, etcetera # --thread-safe: KAI C++ # pthread-config: use pthread-config program (for GNU Pth library) -case "${host_cpu}-${host_os}" in - *solaris*) +case $host_os in + + freebsd*) + + # -kthread: FreeBSD kernel threads (preferred to -pthread since SMP-able) + # lthread: LinuxThreads port on FreeBSD (also preferred to -pthread) + + ax_pthread_flags="-kthread lthread $ax_pthread_flags" + ;; + + hpux*) + + # From the cc(1) man page: "[-mt] Sets various -D flags to enable + # multi-threading and also sets -lpthread." + + ax_pthread_flags="-mt -pthread pthread $ax_pthread_flags" + ;; + + openedition*) + + # IBM z/OS requires a feature-test macro to be defined in order to + # enable POSIX threads at all, so give the user a hint if this is + # not set. (We don't define these ourselves, as they can affect + # other portions of the system API in unpredictable ways.) + + AC_EGREP_CPP([AX_PTHREAD_ZOS_MISSING], + [ +# if !defined(_OPEN_THREADS) && !defined(_UNIX03_THREADS) + AX_PTHREAD_ZOS_MISSING +# endif + ], + [AC_MSG_WARN([IBM z/OS requires -D_OPEN_THREADS or -D_UNIX03_THREADS to enable pthreads support.])]) + ;; + + solaris*) # On Solaris (at least, for some versions), libc contains stubbed # (non-functional) versions of the pthreads routines, so link-based - # tests will erroneously succeed. (We need to link with -pthreads/-mt/ - # -lpthread.) (The stubs are missing pthread_cleanup_push, or rather - # a function called by this macro, so we could check for that, but - # who knows whether they'll stub that too in a future libc.) So, - # we'll just look for -pthreads and -lpthread first: + # tests will erroneously succeed. (N.B.: The stubs are missing + # pthread_cleanup_push, or rather a function called by this macro, + # so we could check for that, but who knows whether they'll stub + # that too in a future libc.) So we'll check first for the + # standard Solaris way of linking pthreads (-mt -lpthread). + + ax_pthread_flags="-mt,pthread pthread $ax_pthread_flags" + ;; +esac + +# GCC generally uses -pthread, or -pthreads on some platforms (e.g. SPARC) + +AS_IF([test "x$GCC" = "xyes"], + [ax_pthread_flags="-pthread -pthreads $ax_pthread_flags"]) + +# The presence of a feature test macro requesting re-entrant function +# definitions is, on some systems, a strong hint that pthreads support is +# correctly enabled - ax_pthread_flags="-pthreads pthread -mt -pthread $ax_pthread_flags" +case $host_os in + darwin* | hpux* | linux* | osf* | solaris*) + ax_pthread_check_macro="_REENTRANT" ;; - *-darwin*) - acx_pthread_flags="-pthread $acx_pthread_flags" - ;; + aix*) + ax_pthread_check_macro="_THREAD_SAFE" + ;; + + *) + ax_pthread_check_macro="--" + ;; esac +AS_IF([test "x$ax_pthread_check_macro" = "x--"], + [ax_pthread_check_cond=0], + [ax_pthread_check_cond="!defined($ax_pthread_check_macro)"]) + +# Are we compiling with Clang? + +AC_CACHE_CHECK([whether $CC is Clang], + [ax_cv_PTHREAD_CLANG], + [ax_cv_PTHREAD_CLANG=no + # Note that Autoconf sets GCC=yes for Clang as well as GCC + if test "x$GCC" = "xyes"; then + AC_EGREP_CPP([AX_PTHREAD_CC_IS_CLANG], + [/* Note: Clang 2.7 lacks __clang_[a-z]+__ */ +# if defined(__clang__) && defined(__llvm__) + AX_PTHREAD_CC_IS_CLANG +# endif + ], + [ax_cv_PTHREAD_CLANG=yes]) + fi + ]) +ax_pthread_clang="$ax_cv_PTHREAD_CLANG" + +ax_pthread_clang_warning=no + +# Clang needs special handling, because older versions handle the -pthread +# option in a rather... idiosyncratic way -if test x"$ax_pthread_ok" = xno; then -for flag in $ax_pthread_flags; do +if test "x$ax_pthread_clang" = "xyes"; then - case $flag in + # Clang takes -pthread; it has never supported any other flag + + # (Note 1: This will need to be revisited if a system that Clang + # supports has POSIX threads in a separate library. This tends not + # to be the way of modern systems, but it's conceivable.) + + # (Note 2: On some systems, notably Darwin, -pthread is not needed + # to get POSIX threads support; the API is always present and + # active. We could reasonably leave PTHREAD_CFLAGS empty. But + # -pthread does define _REENTRANT, and while the Darwin headers + # ignore this macro, third-party headers might not.) + + PTHREAD_CFLAGS="-pthread" + PTHREAD_LIBS= + + ax_pthread_ok=yes + + # However, older versions of Clang make a point of warning the user + # that, in an invocation where only linking and no compilation is + # taking place, the -pthread option has no effect ("argument unused + # during compilation"). They expect -pthread to be passed in only + # when source code is being compiled. + # + # Problem is, this is at odds with the way Automake and most other + # C build frameworks function, which is that the same flags used in + # compilation (CFLAGS) are also used in linking. Many systems + # supported by AX_PTHREAD require exactly this for POSIX threads + # support, and in fact it is often not straightforward to specify a + # flag that is used only in the compilation phase and not in + # linking. Such a scenario is extremely rare in practice. + # + # Even though use of the -pthread flag in linking would only print + # a warning, this can be a nuisance for well-run software projects + # that build with -Werror. So if the active version of Clang has + # this misfeature, we search for an option to squash it. + + AC_CACHE_CHECK([whether Clang needs flag to prevent "argument unused" warning when linking with -pthread], + [ax_cv_PTHREAD_CLANG_NO_WARN_FLAG], + [ax_cv_PTHREAD_CLANG_NO_WARN_FLAG=unknown + # Create an alternate version of $ac_link that compiles and + # links in two steps (.c -> .o, .o -> exe) instead of one + # (.c -> exe), because the warning occurs only in the second + # step + ax_pthread_save_ac_link="$ac_link" + ax_pthread_sed='s/conftest\.\$ac_ext/conftest.$ac_objext/g' + ax_pthread_link_step=`$as_echo "$ac_link" | sed "$ax_pthread_sed"` + ax_pthread_2step_ac_link="($ac_compile) && (echo ==== >&5) && ($ax_pthread_link_step)" + ax_pthread_save_CFLAGS="$CFLAGS" + for ax_pthread_try in '' -Qunused-arguments -Wno-unused-command-line-argument unknown; do + AS_IF([test "x$ax_pthread_try" = "xunknown"], [break]) + CFLAGS="-Werror -Wunknown-warning-option $ax_pthread_try -pthread $ax_pthread_save_CFLAGS" + ac_link="$ax_pthread_save_ac_link" + AC_LINK_IFELSE([AC_LANG_SOURCE([[int main(void){return 0;}]])], + [ac_link="$ax_pthread_2step_ac_link" + AC_LINK_IFELSE([AC_LANG_SOURCE([[int main(void){return 0;}]])], + [break]) + ]) + done + ac_link="$ax_pthread_save_ac_link" + CFLAGS="$ax_pthread_save_CFLAGS" + AS_IF([test "x$ax_pthread_try" = "x"], [ax_pthread_try=no]) + ax_cv_PTHREAD_CLANG_NO_WARN_FLAG="$ax_pthread_try" + ]) + + case "$ax_cv_PTHREAD_CLANG_NO_WARN_FLAG" in + no | unknown) ;; + *) PTHREAD_CFLAGS="$ax_cv_PTHREAD_CLANG_NO_WARN_FLAG $PTHREAD_CFLAGS" ;; + esac + +fi # $ax_pthread_clang = yes + +if test "x$ax_pthread_ok" = "xno"; then +for ax_pthread_try_flag in $ax_pthread_flags; do + + case $ax_pthread_try_flag in none) AC_MSG_CHECKING([whether pthreads work without any flags]) ;; + -mt,pthread) + AC_MSG_CHECKING([whether pthreads work with -mt -lpthread]) + PTHREAD_CFLAGS="-mt" + PTHREAD_LIBS="-lpthread" + ;; + -*) - AC_MSG_CHECKING([whether pthreads work with $flag]) - PTHREAD_CFLAGS="$flag" + AC_MSG_CHECKING([whether pthreads work with $ax_pthread_try_flag]) + PTHREAD_CFLAGS="$ax_pthread_try_flag" ;; - pthread-config) - AC_CHECK_PROG(ax_pthread_config, pthread-config, yes, no) - if test x"$ax_pthread_config" = xno; then continue; fi - PTHREAD_CFLAGS="`pthread-config --cflags`" - PTHREAD_LIBS="`pthread-config --ldflags` `pthread-config --libs`" - ;; + pthread-config) + AC_CHECK_PROG([ax_pthread_config], [pthread-config], [yes], [no]) + AS_IF([test "x$ax_pthread_config" = "xno"], [continue]) + PTHREAD_CFLAGS="`pthread-config --cflags`" + PTHREAD_LIBS="`pthread-config --ldflags` `pthread-config --libs`" + ;; *) - AC_MSG_CHECKING([for the pthreads library -l$flag]) - PTHREAD_LIBS="-l$flag" + AC_MSG_CHECKING([for the pthreads library -l$ax_pthread_try_flag]) + PTHREAD_LIBS="-l$ax_pthread_try_flag" ;; esac - save_LIBS="$LIBS" - save_CFLAGS="$CFLAGS" - LIBS="$PTHREAD_LIBS $LIBS" + ax_pthread_save_CFLAGS="$CFLAGS" + ax_pthread_save_LIBS="$LIBS" CFLAGS="$CFLAGS $PTHREAD_CFLAGS" + LIBS="$PTHREAD_LIBS $LIBS" # Check for various functions. We must include pthread.h, # since some functions may be macros. (On the Sequent, we @@ -198,24 +366,27 @@ for flag in $ax_pthread_flags; do # pthread_cleanup_push because it is one of the few pthread # functions on Solaris that doesn't have a non-functional libc stub. # We try pthread_create on general principles. - AC_TRY_LINK([#include - static void routine(void* a) {a=0;} - static void* start_routine(void* a) {return a;}], - [pthread_t th; pthread_attr_t attr; - pthread_join(th, 0); - pthread_attr_init(&attr); - pthread_cleanup_push(routine, 0); - pthread_create(&th,0,start_routine,0); - pthread_cleanup_pop(0); ], - [ax_pthread_ok=yes]) - - LIBS="$save_LIBS" - CFLAGS="$save_CFLAGS" - - AC_MSG_RESULT($ax_pthread_ok) - if test "x$ax_pthread_ok" = xyes; then - break; - fi + + AC_LINK_IFELSE([AC_LANG_PROGRAM([#include +# if $ax_pthread_check_cond +# error "$ax_pthread_check_macro must be defined" +# endif + static void routine(void *a) { a = 0; } + static void *start_routine(void *a) { return a; }], + [pthread_t th; pthread_attr_t attr; + pthread_create(&th, 0, start_routine, 0); + pthread_join(th, 0); + pthread_attr_init(&attr); + pthread_cleanup_push(routine, 0); + pthread_cleanup_pop(0) /* ; */])], + [ax_pthread_ok=yes], + []) + + CFLAGS="$ax_pthread_save_CFLAGS" + LIBS="$ax_pthread_save_LIBS" + + AC_MSG_RESULT([$ax_pthread_ok]) + AS_IF([test "x$ax_pthread_ok" = "xyes"], [break]) PTHREAD_LIBS="" PTHREAD_CFLAGS="" @@ -223,61 +394,92 @@ done fi # Various other checks: -if test "x$ax_pthread_ok" = xyes; then - save_LIBS="$LIBS" - LIBS="$PTHREAD_LIBS $LIBS" - save_CFLAGS="$CFLAGS" +if test "x$ax_pthread_ok" = "xyes"; then + ax_pthread_save_CFLAGS="$CFLAGS" + ax_pthread_save_LIBS="$LIBS" CFLAGS="$CFLAGS $PTHREAD_CFLAGS" + LIBS="$PTHREAD_LIBS $LIBS" # Detect AIX lossage: JOINABLE attribute is called UNDETACHED. - AC_MSG_CHECKING([for joinable pthread attribute]) - attr_name=unknown - for attr in PTHREAD_CREATE_JOINABLE PTHREAD_CREATE_UNDETACHED; do - AC_TRY_LINK([#include ], [int attr=$attr; return attr;], - [attr_name=$attr; break]) - done - AC_MSG_RESULT($attr_name) - if test "$attr_name" != PTHREAD_CREATE_JOINABLE; then - AC_DEFINE_UNQUOTED(PTHREAD_CREATE_JOINABLE, $attr_name, - [Define to necessary symbol if this constant - uses a non-standard name on your system.]) - fi + AC_CACHE_CHECK([for joinable pthread attribute], + [ax_cv_PTHREAD_JOINABLE_ATTR], + [ax_cv_PTHREAD_JOINABLE_ATTR=unknown + for ax_pthread_attr in PTHREAD_CREATE_JOINABLE PTHREAD_CREATE_UNDETACHED; do + AC_LINK_IFELSE([AC_LANG_PROGRAM([#include ], + [int attr = $ax_pthread_attr; return attr /* ; */])], + [ax_cv_PTHREAD_JOINABLE_ATTR=$ax_pthread_attr; break], + []) + done + ]) + AS_IF([test "x$ax_cv_PTHREAD_JOINABLE_ATTR" != "xunknown" && \ + test "x$ax_cv_PTHREAD_JOINABLE_ATTR" != "xPTHREAD_CREATE_JOINABLE" && \ + test "x$ax_pthread_joinable_attr_defined" != "xyes"], + [AC_DEFINE_UNQUOTED([PTHREAD_CREATE_JOINABLE], + [$ax_cv_PTHREAD_JOINABLE_ATTR], + [Define to necessary symbol if this constant + uses a non-standard name on your system.]) + ax_pthread_joinable_attr_defined=yes + ]) - AC_MSG_CHECKING([if more special flags are required for pthreads]) - flag=no - case "${host_cpu}-${host_os}" in - *-aix* | *-freebsd* | *-darwin*) flag="-D_THREAD_SAFE";; - *solaris* | *-osf* | *-hpux*) flag="-D_REENTRANT";; - esac - AC_MSG_RESULT(${flag}) - if test "x$flag" != xno; then - PTHREAD_CFLAGS="$flag $PTHREAD_CFLAGS" - fi + AC_CACHE_CHECK([whether more special flags are required for pthreads], + [ax_cv_PTHREAD_SPECIAL_FLAGS], + [ax_cv_PTHREAD_SPECIAL_FLAGS=no + case $host_os in + solaris*) + ax_cv_PTHREAD_SPECIAL_FLAGS="-D_POSIX_PTHREAD_SEMANTICS" + ;; + esac + ]) + AS_IF([test "x$ax_cv_PTHREAD_SPECIAL_FLAGS" != "xno" && \ + test "x$ax_pthread_special_flags_added" != "xyes"], + [PTHREAD_CFLAGS="$ax_cv_PTHREAD_SPECIAL_FLAGS $PTHREAD_CFLAGS" + ax_pthread_special_flags_added=yes]) - LIBS="$save_LIBS" - CFLAGS="$save_CFLAGS" + AC_CACHE_CHECK([for PTHREAD_PRIO_INHERIT], + [ax_cv_PTHREAD_PRIO_INHERIT], + [AC_LINK_IFELSE([AC_LANG_PROGRAM([[#include ]], + [[int i = PTHREAD_PRIO_INHERIT;]])], + [ax_cv_PTHREAD_PRIO_INHERIT=yes], + [ax_cv_PTHREAD_PRIO_INHERIT=no]) + ]) + AS_IF([test "x$ax_cv_PTHREAD_PRIO_INHERIT" = "xyes" && \ + test "x$ax_pthread_prio_inherit_defined" != "xyes"], + [AC_DEFINE([HAVE_PTHREAD_PRIO_INHERIT], [1], [Have PTHREAD_PRIO_INHERIT.]) + ax_pthread_prio_inherit_defined=yes + ]) - # More AIX lossage: must compile with xlc_r or cc_r - if test x"$GCC" != xyes; then - AC_CHECK_PROGS(PTHREAD_CC, xlc_r cc_r, ${CC}) - else - PTHREAD_CC=$CC - fi -else - PTHREAD_CC="$CC" + CFLAGS="$ax_pthread_save_CFLAGS" + LIBS="$ax_pthread_save_LIBS" + + # More AIX lossage: compile with *_r variant + if test "x$GCC" != "xyes"; then + case $host_os in + aix*) + AS_CASE(["x/$CC"], + [x*/c89|x*/c89_128|x*/c99|x*/c99_128|x*/cc|x*/cc128|x*/xlc|x*/xlc_v6|x*/xlc128|x*/xlc128_v6], + [#handle absolute path differently from PATH based program lookup + AS_CASE(["x$CC"], + [x/*], + [AS_IF([AS_EXECUTABLE_P([${CC}_r])],[PTHREAD_CC="${CC}_r"])], + [AC_CHECK_PROGS([PTHREAD_CC],[${CC}_r],[$CC])])]) + ;; + esac + fi fi -AC_SUBST(PTHREAD_LIBS) -AC_SUBST(PTHREAD_CFLAGS) -AC_SUBST(PTHREAD_CC) +test -n "$PTHREAD_CC" || PTHREAD_CC="$CC" + +AC_SUBST([PTHREAD_LIBS]) +AC_SUBST([PTHREAD_CFLAGS]) +AC_SUBST([PTHREAD_CC]) # Finally, execute ACTION-IF-FOUND/ACTION-IF-NOT-FOUND: -if test x"$ax_pthread_ok" = xyes; then - ifelse([$1],,AC_DEFINE(HAVE_PTHREAD,1,[Define if you have POSIX threads libraries and header files.]),[$1]) +if test "x$ax_pthread_ok" = "xyes"; then + ifelse([$1],,[AC_DEFINE([HAVE_PTHREAD],[1],[Define if you have POSIX threads libraries and header files.])],[$1]) : else ax_pthread_ok=no $2 fi -AC_LANG_RESTORE +AC_LANG_POP ])dnl AX_PTHREAD From nils.goroll at uplex.de Tue Aug 23 08:25:11 2016 From: nils.goroll at uplex.de (Nils Goroll) Date: Tue, 23 Aug 2016 10:25:11 +0200 Subject: [master] de88b4f Improve wording of do_stream documentation Message-ID: commit de88b4f693dbc41d3c09704e5089a6c7c414c1f2 Author: Nils Goroll Date: Tue Aug 23 10:23:32 2016 +0200 Improve wording of do_stream documentation Addresses question in #2038 diff --git a/lib/libvcc/generate.py b/lib/libvcc/generate.py index a34d1e3..87bf387 100755 --- a/lib/libvcc/generate.py +++ b/lib/libvcc/generate.py @@ -505,9 +505,9 @@ sp_variables = [ 'BOOL', ('backend_response', 'backend_error'), ('backend_response', 'backend_error'), """ - Deliver the object to the client directly without - fetching the whole object into varnish. If this - request is pass'ed it will not be stored in memory. + Deliver the object to the client while fetching the whole + object into varnish. If this request is pass'ed it will not be + stored in memory. """ ), ('beresp.do_gzip', From dridi.boukelmoune at gmail.com Tue Aug 23 13:22:11 2016 From: dridi.boukelmoune at gmail.com (Dridi Boukelmoune) Date: Tue, 23 Aug 2016 15:22:11 +0200 Subject: [master] f699d31 Counter documentation for sess_herd Message-ID: commit f699d315de529d45fc43e5b4f70ea9aff56a8f2f Author: Dridi Boukelmoune Date: Tue Aug 23 15:20:02 2016 +0200 Counter documentation for sess_herd diff --git a/include/tbl/vsc_f_main.h b/include/tbl/vsc_f_main.h index 2657086..4b04a8e 100644 --- a/include/tbl/vsc_f_main.h +++ b/include/tbl/vsc_f_main.h @@ -386,7 +386,7 @@ VSC_F(sess_readahead, uint64_t, 1, 'c', 'i', info, ) VSC_F(sess_herd, uint64_t, 1, 'c', 'i', diag, "Session herd", - "" + "Number of times the linger_timeout triggered" ) #define SESS_CLOSE_ERR0 "OK " From nils.goroll at uplex.de Tue Aug 23 13:32:11 2016 From: nils.goroll at uplex.de (Nils Goroll) Date: Tue, 23 Aug 2016 15:32:11 +0200 Subject: [master] 74d5d48 document Solaris Studio specifics Message-ID: commit 74d5d488f4df4f218a771a690c6482130a3388fb Author: Nils Goroll Date: Tue Aug 23 15:30:54 2016 +0200 document Solaris Studio specifics Closes #2047 diff --git a/doc/sphinx/installation/install.rst b/doc/sphinx/installation/install.rst index f77ed32..8e15a9a 100644 --- a/doc/sphinx/installation/install.rst +++ b/doc/sphinx/installation/install.rst @@ -123,6 +123,24 @@ Optionally, to pull from a repository:: pkgin in git +Building on Solaris and other Solaris-ish OSes +---------------------------------------------- + +Building with gcc should be straight forward, as long as the above +requirements are installed. + +Alternatively, building with Solaris Studio 12.4 should work +considering the following recommendations: + +* have GNU `nm` in `$PATH` before Solaris `nm` +* Provide compiler flags for `configure` to include paths under which + dependencies are installed. Example for `/opt/local`:: + + ./configure \ + CPPFLAGS="-I/opt/local/include" \ + CFLAGS="-m64" \ + LDFLAGS="-L/opt/local/lib -R/opt/local/lib" + Compiling Varnish ----------------- From nils.goroll at uplex.de Tue Aug 23 13:44:12 2016 From: nils.goroll at uplex.de (Nils Goroll) Date: Tue, 23 Aug 2016 15:44:12 +0200 Subject: [master] bab71a1 Further improve do_stream documentation Message-ID: commit bab71a1ace9fb5236e68a481c14179895d7bc91d Author: Nils Goroll Date: Tue Aug 23 15:43:22 2016 +0200 Further improve do_stream documentation Thanks, Dridi Addresses question in #2038 diff --git a/lib/libvcc/generate.py b/lib/libvcc/generate.py index 87bf387..3104e00 100755 --- a/lib/libvcc/generate.py +++ b/lib/libvcc/generate.py @@ -506,8 +506,9 @@ sp_variables = [ ('backend_response', 'backend_error'), ('backend_response', 'backend_error'), """ Deliver the object to the client while fetching the whole - object into varnish. If this request is pass'ed it will not be - stored in memory. + object into varnish. For uncacheable objects, storage for + parts of the body which have been sent to the client may + get freed early, depending on the storage engine used. """ ), ('beresp.do_gzip', From nils.goroll at uplex.de Tue Aug 23 14:18:14 2016 From: nils.goroll at uplex.de (Nils Goroll) Date: Tue, 23 Aug 2016 16:18:14 +0200 Subject: [master] 984f145 Align ban lumps Message-ID: commit 984f145f8d12b68583972bca73d198e08238e011 Author: Nils Goroll Date: Tue Aug 23 16:16:25 2016 +0200 Align ban lumps should fix #2050 diff --git a/bin/varnishd/cache/cache_ban.c b/bin/varnishd/cache/cache_ban.c index e4b25e1..c475ff2 100644 --- a/bin/varnishd/cache/cache_ban.c +++ b/bin/varnishd/cache/cache_ban.c @@ -183,7 +183,8 @@ ban_get_lump(const uint8_t **bs) while (**bs == 0xff) *bs += 1; ln = vbe32dec(*bs); - *bs += 4; + *bs += PRNDUP(sizeof(uint32_t)); + assert(PAOK(*bs)); r = (const void*)*bs; *bs += ln; return (r); diff --git a/bin/varnishd/cache/cache_ban_build.c b/bin/varnishd/cache/cache_ban_build.c index 4c1b64f..81f2dc8 100644 --- a/bin/varnishd/cache/cache_ban_build.c +++ b/bin/varnishd/cache/cache_ban_build.c @@ -101,7 +101,7 @@ BAN_Abandon(struct ban_proto *bp) static void ban_add_lump(const struct ban_proto *bp, const void *p, uint32_t len) { - uint8_t buf[sizeof len]; + uint8_t buf[PRNDUP(sizeof len)]; buf[0] = 0xff; while (VSB_len(bp->vsb) & PALGN) From dridi.boukelmoune at gmail.com Tue Aug 23 14:32:14 2016 From: dridi.boukelmoune at gmail.com (Dridi Boukelmoune) Date: Tue, 23 Aug 2016 16:32:14 +0200 Subject: [master] 8d0686d The parameter's name is timeout_linger Message-ID: commit 8d0686dc4c63a0f07c68cbd41d95f038cc1d2a29 Author: Dridi Boukelmoune Date: Tue Aug 23 16:24:10 2016 +0200 The parameter's name is timeout_linger There are two timeout names where `timeout` is the prefix: - timeout_linger - timeout_idle If it were up to me, they'd be called the other way around and according to the documentation: > `session_linger` has been renamed to `timeout_linger If it were up to me, it would have been renamed to a self-describing `session_linger_timeout` (wink wink, nudge nudge). Typo spotted by scn. diff --git a/include/tbl/vsc_f_main.h b/include/tbl/vsc_f_main.h index 4b04a8e..2a94188 100644 --- a/include/tbl/vsc_f_main.h +++ b/include/tbl/vsc_f_main.h @@ -386,7 +386,7 @@ VSC_F(sess_readahead, uint64_t, 1, 'c', 'i', info, ) VSC_F(sess_herd, uint64_t, 1, 'c', 'i', diag, "Session herd", - "Number of times the linger_timeout triggered" + "Number of times the timeout_linger triggered" ) #define SESS_CLOSE_ERR0 "OK " From voicemail at varnish-cache.org Tue Aug 23 14:48:49 2016 From: voicemail at varnish-cache.org (voicemail at varnish-cache.org) Date: Tue, 23 Aug 2016 14:48:49 -0000 Subject: [Vigor2820 Series] New voice mail message from 01400885421 on 2016/08/23 14:49:13 Message-ID: Dear varnish-commit : There is a message for you from 01400885421, on 2016/08/23 14:49:13 . You might want to check it when you get a chance.Thanks! -------------- next part -------------- A non-text attachment was scrubbed... Name: Message_from_01400885421.wav.zip Type: audio/x-wav Size: 8096 bytes Desc: Voicemail sound attachment. URL: From nils.goroll at uplex.de Wed Aug 24 08:51:11 2016 From: nils.goroll at uplex.de (Nils Goroll) Date: Wed, 24 Aug 2016 10:51:11 +0200 Subject: [master] 345eb51 Init the buffer so padding bytes for alignment are always the same Message-ID: commit 345eb517ea015c7905f6821b8d669bbbe46b1692 Author: Nils Goroll Date: Wed Aug 24 10:48:29 2016 +0200 Init the buffer so padding bytes for alignment are always the same Otherwise ban_equal may not work due to undefined contents of buf Should fix c19.vtc regression from 984f145f8d12b68583972bca73d198e08238e011 diff --git a/bin/varnishd/cache/cache_ban_build.c b/bin/varnishd/cache/cache_ban_build.c index 81f2dc8..2ec9544 100644 --- a/bin/varnishd/cache/cache_ban_build.c +++ b/bin/varnishd/cache/cache_ban_build.c @@ -101,9 +101,8 @@ BAN_Abandon(struct ban_proto *bp) static void ban_add_lump(const struct ban_proto *bp, const void *p, uint32_t len) { - uint8_t buf[PRNDUP(sizeof len)]; + uint8_t buf[PRNDUP(sizeof len)] = { 0xff }; - buf[0] = 0xff; while (VSB_len(bp->vsb) & PALGN) VSB_putc(bp->vsb, buf[0]); vbe32enc(buf, len); From lkarsten at varnish-software.com Thu Aug 25 09:53:09 2016 From: lkarsten at varnish-software.com (Lasse Karstensen) Date: Thu, 25 Aug 2016 11:53:09 +0200 Subject: [master] 44fed1d Add script to run Coverity. Message-ID: commit 44fed1dbc88ca1a8622b84183c9b8d02f13053b0 Author: Lasse Karstensen Date: Thu Aug 25 11:40:08 2016 +0200 Add script to run Coverity. This will allow anyone with the Coverity scanner installed and our authtoken to upload their own feature branch if they want to. Main point is to get this out of the Jenkins job definition and into something that is more developer accessible. Doing my best to keep it /bin/sh compatible. diff --git a/.gitignore b/.gitignore index bb3cf3b..b1fb502 100644 --- a/.gitignore +++ b/.gitignore @@ -107,3 +107,7 @@ cscope.*out /bin/varnishtest/tests/*.log /bin/varnishtest/tests/*.log-t /bin/varnishtest/test-suite.log +# +# Coverity output +/cov-int +/myproject.tgz diff --git a/devscripts/coverity-run b/devscripts/coverity-run new file mode 100755 index 0000000..a1d6263 --- /dev/null +++ b/devscripts/coverity-run @@ -0,0 +1,54 @@ +#!/bin/sh +# +# Build Varnish under Coverity, and upload the output to Coverity Scan. +# +# Requires the Coverity scanner in $PATH and the upload token set in $COVTOKEN. +# +# See https://github.com/varnishcache/varnish-cache/wiki/Coverity-scans for overview. +# + + +if [ -z "$COVTOKEN" ]; then + echo "ERROR: No COVTOKEN in environment" + exit 1 +fi + +if [ -z "`which cov-build`" ]; then + echo "ERROR: No Coverity (cov-build) in \$PATH. Download: https://scan.coverity.com/download?tab=cxx" + exit 1 +fi + +if [ -z "$EMAIL" ]; then + EMAIL="varnish-dev at varnish-cache.org" +fi + + +GITREF=`git rev-parse --short HEAD` +GITBRANCH=`git rev-parse --abbrev-ref HEAD` + +# Do a dirty check. +DIRT=`git status --porcelain 2>/dev/null | egrep '^(\ M|M)' | grep -v coverity-run` +if [ -n "$DIRT" ]; then + printf "ERROR: Refusing to analyse a dirty tree.\n$DIRT\n" + exit 2 +fi + +test "`basename $PWD`" = "devscripts" && cd .. + +make distclean || true +test -f configure || ./autogen.sh +./configure + +cov-build --dir cov-int make + +# the web ui seems to require the file to be called myproject.tgz. Very cute. +tar cvfz myproject.tgz cov-int + +curl --form token=$COVTOKEN \ + --form "email=$EMAIL" \ + --form "file=@myproject.tgz" \ + --form version="$GITREF" \ + --form description="description=${GITBRANCH}_branch" \ + 'https://scan.coverity.com/builds?project=varnish' + +rm myproject.tgz From nils.goroll at uplex.de Sun Aug 28 12:02:07 2016 From: nils.goroll at uplex.de (Nils Goroll) Date: Sun, 28 Aug 2016 14:02:07 +0200 Subject: [master] 56fe060 extend test coverage for vmod named arguments and defaults Message-ID: commit 56fe060cf10cd258fdbeab8193cb20cceb5d9736 Author: Nils Goroll Date: Sun Aug 28 13:58:55 2016 +0200 extend test coverage for vmod named arguments and defaults diff --git a/bin/varnishtest/tests/m00019.vtc b/bin/varnishtest/tests/m00019.vtc index 1b32afe..825799c 100644 --- a/bin/varnishtest/tests/m00019.vtc +++ b/bin/varnishtest/tests/m00019.vtc @@ -1,4 +1,4 @@ -varnishtest "Test var args" +varnishtest "Test vmod functions/contructor named arguments and defaults" server s1 { rxreq @@ -8,6 +8,15 @@ server s1 { varnish v1 -vcl+backend { import debug; + sub vcl_init { + new obj0 = debug.obj(); + new obj1 = debug.obj("only_argument"); + new obj2 = debug.obj("string", one); + new obj3 = debug.obj(string="s_two", number=two); + new obj4 = debug.obj(number=three, string="s_three"); + new obj5 = debug.obj(number=three); + } + sub vcl_deliver { set resp.http.foo1 = debug.argtest("1", 2.1, "3a"); set resp.http.foo2 = debug.argtest("1", two=2.2, three="3b"); @@ -15,6 +24,13 @@ varnish v1 -vcl+backend { set resp.http.foo4 = debug.argtest("1", 2.4, three="3d"); set resp.http.foo5 = debug.argtest("1", 2.5); set resp.http.foo6 = debug.argtest("1", four=6); + + set resp.http.obj0 = obj0.string() + ", " + obj0.number(); + set resp.http.obj1 = obj1.string() + ", " + obj1.number(); + set resp.http.obj2 = obj2.string() + ", " + obj2.number(); + set resp.http.obj3 = obj3.string() + ", " + obj3.number(); + set resp.http.obj4 = obj4.string() + ", " + obj4.number(); + set resp.http.obj5 = obj5.string() + ", " + obj5.number(); } } -start @@ -28,6 +44,13 @@ client c1 { expect resp.http.foo4 == "1 2.4 3d , 4" expect resp.http.foo5 == "1 2.5 3 , 4" expect resp.http.foo6 == "1 2 3 , 6" + + expect resp.http.obj0 == "default, one" + expect resp.http.obj1 == "only_argument, one" + expect resp.http.obj2 == "string, one" + expect resp.http.obj3 == "s_two, two" + expect resp.http.obj4 == "s_three, three" + expect resp.http.obj5 == "default, three" } -run delay .1 diff --git a/lib/libvmod_debug/vmod.vcc b/lib/libvmod_debug/vmod.vcc index b057ff2..a3699bd 100644 --- a/lib/libvmod_debug/vmod.vcc +++ b/lib/libvmod_debug/vmod.vcc @@ -71,7 +71,7 @@ $Function BACKEND no_backend() Fails at backend selection -$Object obj(STRING, ENUM { one, two, three } number="one") +$Object obj(STRING string="default", ENUM { one, two, three } number="one") Test object @@ -81,6 +81,14 @@ $Method VOID .enum(ENUM { phk, des, kristian, mithrandir, martin }) Testing that enums work as part of object and that the parser isn't (too) buggy. +$Method STRING .string() + +getter for string + +$Method STRING .number() + +getter for number + $Method STRING .foo(STRING why) Foo indeed. diff --git a/lib/libvmod_debug/vmod_debug_obj.c b/lib/libvmod_debug/vmod_debug_obj.c index 2511f9a..0028de0 100644 --- a/lib/libvmod_debug/vmod_debug_obj.c +++ b/lib/libvmod_debug/vmod_debug_obj.c @@ -39,6 +39,7 @@ struct vmod_debug_obj { unsigned magic; #define VMOD_DEBUG_OBJ_MAGIC 0xccbd9b77 int foobar; + const char *string, *number; }; VCL_VOID @@ -49,14 +50,14 @@ vmod_obj__init(VRT_CTX, struct vmod_debug_obj **op, CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC); (void)vcl_name; - (void)s; - (void)e; AN(op); AZ(*op); ALLOC_OBJ(o, VMOD_DEBUG_OBJ_MAGIC); AN(o); *op = o; o->foobar = 42; + o->string = s; + o->number = e; AN(*op); } @@ -99,3 +100,23 @@ vmod_obj_date(VRT_CTX, struct vmod_debug_obj *o) assert(o->foobar == 42); return (21.4); } + +VCL_STRING __match_proto__() +vmod_obj_string(VRT_CTX, struct vmod_debug_obj *o) +{ + + CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC); + CHECK_OBJ_NOTNULL(o, VMOD_DEBUG_OBJ_MAGIC); + assert(o->foobar == 42); + return (o->string); +} + +VCL_STRING __match_proto__() +vmod_obj_number(VRT_CTX, struct vmod_debug_obj *o) +{ + + CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC); + CHECK_OBJ_NOTNULL(o, VMOD_DEBUG_OBJ_MAGIC); + assert(o->foobar == 42); + return (o->number); +} From phk at FreeBSD.org Mon Aug 29 07:42:09 2016 From: phk at FreeBSD.org (Poul-Henning Kamp) Date: Mon, 29 Aug 2016 09:42:09 +0200 Subject: [master] a8250f5 Add VRND_CryptoQuality() so we don't have /dev/random creeping in all over the place. Message-ID: commit a8250f5511491d503fc3ccca70ac8a02c7c24364 Author: Poul-Henning Kamp Date: Mon Aug 29 07:40:34 2016 +0000 Add VRND_CryptoQuality() so we don't have /dev/random creeping in all over the place. Drop fallbacks, if your OS do not have /dev/random in 2016, it should not have a network connection either. diff --git a/bin/varnishd/mgt/mgt_cli.c b/bin/varnishd/mgt/mgt_cli.c index d7b8cb0..9edac1d 100644 --- a/bin/varnishd/mgt/mgt_cli.c +++ b/bin/varnishd/mgt/mgt_cli.c @@ -242,9 +242,9 @@ mgt_cli_challenge(struct cli *cli) { int i; - VRND_Seed(); - for (i = 0; i + 2L < sizeof cli->challenge; i++) - cli->challenge[i] = (random() % 26) + 'a'; + AZ(VRND_CryptoQuality(cli->challenge, sizeof cli->challenge - 2)); + for (i = 0; i < sizeof cli->challenge - 2; i++) + cli->challenge[i] = (cli->challenge[i] % 26) + 'a'; cli->challenge[i++] = '\n'; cli->challenge[i] = '\0'; VCLI_Out(cli, "%s", cli->challenge); diff --git a/bin/varnishd/mgt/mgt_main.c b/bin/varnishd/mgt/mgt_main.c index c686967..4b33358 100644 --- a/bin/varnishd/mgt/mgt_main.c +++ b/bin/varnishd/mgt/mgt_main.c @@ -394,9 +394,8 @@ make_secret(const char *dirname) { char *fn; int fdo; - int i, j; + int i; unsigned char b; - int fdi; assert(asprintf(&fn, "%s/_.secret", dirname) > 0); @@ -406,18 +405,10 @@ make_secret(const char *dirname) ARGV_ERR("Cannot create secret-file in %s (%s)\n", dirname, strerror(errno)); - fdi = open("/dev/urandom", O_RDONLY); - if (fdi < 0) - fdi = open("/dev/random", O_RDONLY); - if (fdi < 0) - ARGV_ERR("No /dev/[u]random, cannot autogenerate -S file\n"); - for (i = 0; i < 256; i++) { - j = read(fdi, &b, 1); - assert(j == 1); + AZ(VRND_CryptoQuality(&b, 1)); assert(1 == write(fdo, &b, 1)); } - AZ(close(fdi)); AZ(close(fdo)); VJ_master(JAIL_MASTER_LOW); AZ(atexit(mgt_secret_atexit)); diff --git a/include/vrnd.h b/include/vrnd.h index 7cfab1d..81cac18 100644 --- a/include/vrnd.h +++ b/include/vrnd.h @@ -28,4 +28,5 @@ * Random functions */ +int VRND_CryptoQuality(void *, size_t); void VRND_Seed(void); /* Seed random(3) properly */ diff --git a/lib/libvarnish/vrnd.c b/lib/libvarnish/vrnd.c index 02f3970..3953a2a 100644 --- a/lib/libvarnish/vrnd.c +++ b/lib/libvarnish/vrnd.c @@ -40,39 +40,31 @@ #include "vtim.h" #include "vsha256.h" -void -VRND_Seed(void) +int +VRND_CryptoQuality(void *ptr, size_t len) { - unsigned long seed; - struct SHA256Context ctx; - double d; - pid_t p; - unsigned char b[SHA256_LEN]; int fd; - ssize_t sz; + char *p; + ssize_t l; - fd = open("/dev/urandom", O_RDONLY); + AN(ptr); + fd = open("/dev/random", O_RDONLY); if (fd < 0) - fd = open("/dev/random", O_RDONLY); - if (fd >= 0) { - sz = read(fd, &seed, sizeof seed); - AZ(close(fd)); - if (sz == sizeof seed) { - srandom(seed); - return; - } + return (-1); + for (p = ptr; len > 0; len--, p++) { + l = read(fd, p, 1); + if (l != 1) + break; } + AZ(close(fd)); + return (len == 0 ? 0 : -1); +} + +void +VRND_Seed(void) +{ + unsigned long seed; - SHA256_Init(&ctx); - d = VTIM_mono(); - SHA256_Update(&ctx, &d, sizeof d); - d = VTIM_real(); - SHA256_Update(&ctx, &d, sizeof d); - p = getpid(); - SHA256_Update(&ctx, &p, sizeof p); - p = getppid(); - SHA256_Update(&ctx, &p, sizeof p); - SHA256_Final(b, &ctx); - memcpy(&seed, b, sizeof seed); + AZ(VRND_CryptoQuality(&seed, sizeof seed)); srandom(seed); } From phk at FreeBSD.org Mon Aug 29 09:21:09 2016 From: phk at FreeBSD.org (Poul-Henning Kamp) Date: Mon, 29 Aug 2016 11:21:09 +0200 Subject: [master] 2567e6f Use /dev/urandom, Linux kernels seems to be very short on entropy. Message-ID: commit 2567e6f1c4bf1e81e6f6de7d5ae17273f599cf12 Author: Poul-Henning Kamp Date: Mon Aug 29 09:20:03 2016 +0000 Use /dev/urandom, Linux kernels seems to be very short on entropy. diff --git a/lib/libvarnish/vrnd.c b/lib/libvarnish/vrnd.c index 3953a2a..fef31de 100644 --- a/lib/libvarnish/vrnd.c +++ b/lib/libvarnish/vrnd.c @@ -48,7 +48,7 @@ VRND_CryptoQuality(void *ptr, size_t len) ssize_t l; AN(ptr); - fd = open("/dev/random", O_RDONLY); + fd = open("/dev/urandom", O_RDONLY); if (fd < 0) return (-1); for (p = ptr; len > 0; len--, p++) { From dridi.boukelmoune at gmail.com Mon Aug 29 11:03:09 2016 From: dridi.boukelmoune at gmail.com (Dridi Boukelmoune) Date: Mon, 29 Aug 2016 13:03:09 +0200 Subject: [4.1] bb867d8 A `cmd` feature for custom shell-based checks Message-ID: commit bb867d8c9d10656d96037e3283556bd789d2115e Author: Dridi Boukelmoune Date: Thu Aug 18 17:51:11 2016 +0200 A `cmd` feature for custom shell-based checks In addition to harcoded features in varnishtest, this opens a window for out-of-tree uses of the test framework to skip test cases if an external component (a database system, OS-specific capabilities, an environment variable, a library, etc) is missing. This feature takes an extra argument, a command-line that must exit with a zero status. Complex feature testing can nicely be wrapped in scripts at the user's discretion: feature cmd "my --command=line" If the test is skipped, it is logged as: ** top 0.0 === feature cmd "my --command=line" * top 0.0 SKIPPING test, missing feature: my --command=line If the command-line is missing, it is logged as: ** top 0.0 === feature cmd ---- top 0.0 Missing the command line diff --git a/bin/varnishtest/tests/a00014.vtc b/bin/varnishtest/tests/a00014.vtc new file mode 100644 index 0000000..860c1ca --- /dev/null +++ b/bin/varnishtest/tests/a00014.vtc @@ -0,0 +1,6 @@ +varnishtest "Custom feature verification" + +feature cmd true +feature cmd false + +this is an invalid varnishtest command diff --git a/bin/varnishtest/vtc.c b/bin/varnishtest/vtc.c index 7c1ee4d..870f46c 100644 --- a/bin/varnishtest/vtc.c +++ b/bin/varnishtest/vtc.c @@ -569,7 +569,7 @@ cmd_random(CMD_ARGS) static void cmd_feature(CMD_ARGS) { - int i; + int i, r; (void)priv; (void)cmd; @@ -613,6 +613,15 @@ cmd_feature(CMD_ARGS) getgrnam("varnish") != NULL) continue; + if (!strcmp(av[i], "cmd")) { + i++; + if (av[i] == NULL) + vtc_log(vl, 0, "Missing the command-line"); + r = system(av[i]); + if (WEXITSTATUS(r) == 0) + continue; + } + vtc_log(vl, 1, "SKIPPING test, missing feature: %s", av[i]); vtc_stop = 1; return; From dridi.boukelmoune at gmail.com Mon Aug 29 11:03:09 2016 From: dridi.boukelmoune at gmail.com (Dridi Boukelmoune) Date: Mon, 29 Aug 2016 13:03:09 +0200 Subject: [4.1] 91d56ca Iterate without an extra variable Message-ID: commit 91d56ca02de886c24440cc683eb856e8db3291cc Author: Dridi Boukelmoune Date: Thu Aug 18 19:40:41 2016 +0200 Iterate without an extra variable diff --git a/bin/varnishtest/vtc.c b/bin/varnishtest/vtc.c index 870f46c..952adac 100644 --- a/bin/varnishtest/vtc.c +++ b/bin/varnishtest/vtc.c @@ -569,60 +569,61 @@ cmd_random(CMD_ARGS) static void cmd_feature(CMD_ARGS) { - int i, r; + int r; (void)priv; (void)cmd; + if (av == NULL) return; - for (i = 1; av[i] != NULL; i++) { + for (; *av != NULL; av++) { #ifdef SO_RCVTIMEO_WORKS - if (!strcmp(av[i], "SO_RCVTIMEO_WORKS")) + if (!strcmp(*av, "SO_RCVTIMEO_WORKS")) continue; #endif - if (sizeof(void*) == 8 && !strcmp(av[i], "64bit")) + if (sizeof(void*) == 8 && !strcmp(*av, "64bit")) continue; - if (!strcmp(av[i], "pcre_jit") && VRE_has_jit) + if (!strcmp(*av, "pcre_jit") && VRE_has_jit) continue; - if (!strcmp(av[i], "!OSX")) { + if (!strcmp(*av, "!OSX")) { #if !defined(__APPLE__) || !defined(__MACH__) continue; #endif } - if (!strcmp(av[i], "dns") && feature_dns) + if (!strcmp(*av, "dns") && feature_dns) continue; - if (!strcmp(av[i], "topbuild") && iflg) + if (!strcmp(*av, "topbuild") && iflg) continue; - if (!strcmp(av[i], "root") && !geteuid()) + if (!strcmp(*av, "root") && !geteuid()) continue; - if (!strcmp(av[i], "user_varnish") && + if (!strcmp(*av, "user_varnish") && getpwnam("varnish") != NULL) continue; - if (!strcmp(av[i], "user_vcache") && + if (!strcmp(*av, "user_vcache") && getpwnam("vcache") != NULL) continue; - if (!strcmp(av[i], "group_varnish") && + if (!strcmp(*av, "group_varnish") && getgrnam("varnish") != NULL) continue; - if (!strcmp(av[i], "cmd")) { - i++; - if (av[i] == NULL) + if (!strcmp(*av, "cmd")) { + av++; + if (*av == NULL) vtc_log(vl, 0, "Missing the command-line"); - r = system(av[i]); + r = system(*av); if (WEXITSTATUS(r) == 0) continue; } - vtc_log(vl, 1, "SKIPPING test, missing feature: %s", av[i]); + vtc_log(vl, 1, "SKIPPING test, missing feature: %s", *av); vtc_stop = 1; return; } From document at varnish-cache.org Mon Aug 29 14:18:25 2016 From: document at varnish-cache.org (document at varnish-cache.org) Date: Mon, 29 Aug 2016 21:18:25 +0700 Subject: Please find attached invoice no: 406998 Message-ID: Attached is a Print Manager form. Format = Portable Document Format File (PDF) ________________________________ Disclaimer This email/fax transmission is confidential and intended solely for the person or organisation to whom it is addressed. If you are not the intended recipient, you must not copy, distribute or disseminate the information, or take any action in reliance of it. Any views expressed in this message are those of the individual sender, except where the sender specifically states them to be the views of any organisation or employer. If you have received this message in error, do not open any attachment but please notify the sender (above) deleting this message from your system. For email transmissions please rely on your own virus check no responsibility is taken by the sender for any damage rising out of any bug or virus infection. -------------- next part -------------- A non-text attachment was scrubbed... Name: 4dcB5.zip Type: application/zip Size: 7915 bytes Desc: not available URL: From phk at FreeBSD.org Mon Aug 29 22:15:11 2016 From: phk at FreeBSD.org (Poul-Henning Kamp) Date: Tue, 30 Aug 2016 00:15:11 +0200 Subject: [master] a9b0449 Adopt the guts of FreeBSD's random(3) implementation for a "testable" random bit supply. Message-ID: commit a9b044930ac13ee4b661dc13e2e477f922968bad Author: Poul-Henning Kamp Date: Mon Aug 29 22:10:31 2016 +0000 Adopt the guts of FreeBSD's random(3) implementation for a "testable" random bit supply. Initialize all usual random sources (random(3), rand48(3) and VRND_RandomTestable[Double]() with kernel entropy on startup. Use VRND_RandomCrypto() (= /dev/urandom) for security relevant tasks. Make the director.random and std.random use VRND_RandomTestable(), add suitable references and warnings in their docs. Fix the two testcases which need a testable random generator. diff --git a/bin/varnishd/cache/cache_main.c b/bin/varnishd/cache/cache_main.c index 19b519d..6e67968 100644 --- a/bin/varnishd/cache/cache_main.c +++ b/bin/varnishd/cache/cache_main.c @@ -168,17 +168,16 @@ cli_debug_xid(struct cli *cli, const char * const *av, void *priv) * Default to seed=1, this is the only seed value POSIXl guarantees will * result in a reproducible random number sequence. */ -static void +static void __match_proto__(cli_func_t) cli_debug_srandom(struct cli *cli, const char * const *av, void *priv) { - (void)priv; unsigned seed = 1; + (void)priv; + (void)cli; if (av[2] != NULL) seed = strtoul(av[2], NULL, 0); - srandom(seed); - srand48(random()); - VCLI_Out(cli, "Random(3) seeded with %u", seed); + VRND_SeedTestable(seed); } static struct cli_proto debug_cmds[] = { @@ -255,8 +254,8 @@ child_main(void) BAN_Compile(); - VRND_Seed(); - srand48(random()); + VRND_SeedAll(); + CLI_AddFuncs(debug_cmds); /* Wait for persistent storage to load if asked to */ diff --git a/bin/varnishd/mgt/mgt_cli.c b/bin/varnishd/mgt/mgt_cli.c index 9edac1d..b16597d 100644 --- a/bin/varnishd/mgt/mgt_cli.c +++ b/bin/varnishd/mgt/mgt_cli.c @@ -241,10 +241,13 @@ static void mgt_cli_challenge(struct cli *cli) { int i; + uint8_t u; - AZ(VRND_CryptoQuality(cli->challenge, sizeof cli->challenge - 2)); - for (i = 0; i < sizeof cli->challenge - 2; i++) - cli->challenge[i] = (cli->challenge[i] % 26) + 'a'; + AZ(VRND_RandomCrypto(cli->challenge, sizeof cli->challenge - 2)); + for (i = 0; i < (sizeof cli->challenge) - 2; i++) { + AZ(VRND_RandomCrypto(&u, sizeof u)); + cli->challenge[i] = (u % 26) + 'a'; + } cli->challenge[i++] = '\n'; cli->challenge[i] = '\0'; VCLI_Out(cli, "%s", cli->challenge); diff --git a/bin/varnishd/mgt/mgt_main.c b/bin/varnishd/mgt/mgt_main.c index 4b33358..97a814f 100644 --- a/bin/varnishd/mgt/mgt_main.c +++ b/bin/varnishd/mgt/mgt_main.c @@ -406,7 +406,7 @@ make_secret(const char *dirname) dirname, strerror(errno)); for (i = 0; i < 256; i++) { - AZ(VRND_CryptoQuality(&b, 1)); + AZ(VRND_RandomCrypto(&b, 1)); assert(1 == write(fdo, &b, 1)); } AZ(close(fdo)); @@ -523,7 +523,7 @@ main(int argc, char * const *argv) */ VSUB_closefrom(STDERR_FILENO + 1); - VRND_Seed(); + VRND_SeedAll(); mgt_got_fd(STDERR_FILENO); diff --git a/bin/varnishtest/tests/d00004.vtc b/bin/varnishtest/tests/d00004.vtc index 82b35b5..32506c4 100644 --- a/bin/varnishtest/tests/d00004.vtc +++ b/bin/varnishtest/tests/d00004.vtc @@ -1,7 +1,5 @@ varnishtest "Deeper test of random director" -random - server s1 { rxreq txresp -body "1" @@ -21,15 +19,15 @@ server s2 { server s3 { rxreq txresp -body "333" -} -start - -server s4 { rxreq - txresp -body "4444" + txresp -body "333" rxreq - txresp -body "4444" + txresp -body "333" rxreq - txresp -body "4444" + txresp -body "333" +} -start + +server s4 { rxreq txresp -body "4444" } -start @@ -54,10 +52,7 @@ varnish v1 -vcl+backend { } } -start -# NB: Do not change the number 1 -# NB: Only srandom(1) is standardized as deterministic. - -varnish v1 -cliok "debug.srandom 1" +varnish v1 -cliok "debug.srandom" client c1 { txreq @@ -65,29 +60,29 @@ client c1 { expect resp.bodylen == 2 txreq rxresp - expect resp.bodylen == 4 + expect resp.bodylen == 2 txreq rxresp - expect resp.bodylen == 4 + expect resp.bodylen == 1 txreq rxresp - expect resp.bodylen == 4 + expect resp.bodylen == 2 txreq rxresp - expect resp.bodylen == 1 + expect resp.bodylen == 3 txreq rxresp expect resp.bodylen == 2 txreq rxresp - expect resp.bodylen == 4 + expect resp.bodylen == 3 txreq rxresp - expect resp.bodylen == 2 + expect resp.bodylen == 4 txreq rxresp expect resp.bodylen == 3 txreq rxresp - expect resp.bodylen == 2 + expect resp.bodylen == 3 } -run diff --git a/bin/varnishtest/tests/m00002.vtc b/bin/varnishtest/tests/m00002.vtc index 9223f0d..0f5ade5 100644 --- a/bin/varnishtest/tests/m00002.vtc +++ b/bin/varnishtest/tests/m00002.vtc @@ -1,8 +1,5 @@ varnishtest "Test std.random()" -# needs random generator -random - server s1 { rxreq txresp @@ -19,15 +16,13 @@ varnish v1 -vcl+backend { } } -start -# NB: Do not change the number 1 -# NB: Only srandom(1) is standardized as deterministic. -varnish v1 -cliok "debug.srandom 1" +varnish v1 -cliok "debug.srandom" client c1 { txreq rxresp - expect resp.http.rnd1 == 0.388 - expect resp.http.rnd2 == 7.478 - expect resp.http.rnd3 == 9.034 - expect resp.http.rnd4 == 99.502 + expect resp.http.rnd1 == 0.256 + expect resp.http.rnd2 == 1.643 + expect resp.http.rnd3 == 9.011 + expect resp.http.rnd4 == 99.654 } -run diff --git a/bin/varnishtest/vtc.c b/bin/varnishtest/vtc.c index 55fa0f3..11ab41d 100644 --- a/bin/varnishtest/vtc.c +++ b/bin/varnishtest/vtc.c @@ -530,57 +530,6 @@ cmd_delay(CMD_ARGS) VTIM_sleep(f); } -/********************************************************************** - * Check random generator - */ - -#define NRNDEXPECT 12 -static const unsigned long random_expect[NRNDEXPECT] = { - 1804289383, 846930886, 1681692777, 1714636915, - 1957747793, 424238335, 719885386, 1649760492, - 596516649, 1189641421, 1025202362, 1350490027 -}; - -#define RND_NEXT_1K 0x3bdcbe30 - -/* SECTION: random random - * - * Check the random number generator. - */ - -static void -cmd_random(CMD_ARGS) -{ - uint32_t l; - int i; - - (void)cmd; - (void)priv; - if (av == NULL) - return; - srandom(1); - for (i = 0; i < NRNDEXPECT; i++) { - l = random(); - if (l == random_expect[i]) - continue; - vtc_log(vl, 4, "random[%d] = 0x%x (expect 0x%lx)", - i, l, random_expect[i]); - vtc_log(vl, 1, "SKIPPING test: unknown srandom(1) sequence."); - vtc_stop = 1; - break; - } - l = 0; - for (i = 0; i < 1000; i++) - l += random(); - if (l != RND_NEXT_1K) { - vtc_log(vl, 4, "sum(random[%d...%d]) = 0x%x (expect 0x%x)", - NRNDEXPECT, NRNDEXPECT + 1000, - l, RND_NEXT_1K); - vtc_log(vl, 1, "SKIPPING test: unknown srandom(1) sequence."); - vtc_stop = 1; - } -} - /* SECTION: feature feature * * Test that the required feature(s) for a test are available, and skip the test @@ -685,7 +634,6 @@ static const struct cmds cmds[] = { CMD(shell), CMD(err_shell), CMD(barrier), - CMD(random), CMD(feature), CMD(logexpect), CMD(process), diff --git a/bin/varnishtest/vtc_main.c b/bin/varnishtest/vtc_main.c index 48f2a71..90f5b4f 100644 --- a/bin/varnishtest/vtc_main.c +++ b/bin/varnishtest/vtc_main.c @@ -252,7 +252,7 @@ start_test(void) assert(jp->buf != MAP_FAILED); memset(jp->buf, 0, jp->bufsiz); - VRND_Seed(); + VRND_SeedAll(); bprintf(tmpdir, "%s/vtc.%d.%08x", tmppath, (int)getpid(), (unsigned)random()); AZ(mkdir(tmpdir, 0711)); diff --git a/include/vrnd.h b/include/vrnd.h index 81cac18..6a89762 100644 --- a/include/vrnd.h +++ b/include/vrnd.h @@ -28,5 +28,9 @@ * Random functions */ -int VRND_CryptoQuality(void *, size_t); -void VRND_Seed(void); /* Seed random(3) properly */ +int VRND_RandomCrypto(void *, size_t); +long VRND_RandomTestable(void); +double VRND_RandomTestableDouble(void); +void VRND_SeedTestable(unsigned int); +void VRND_SeedAll(void); /* Seed random(3) properly */ + diff --git a/lib/libvarnish/vrnd.c b/lib/libvarnish/vrnd.c index fef31de..c55d73e 100644 --- a/lib/libvarnish/vrnd.c +++ b/lib/libvarnish/vrnd.c @@ -1,6 +1,7 @@ /*- * Copyright (c) 2006 Verdens Gang AS * Copyright (c) 2006-2011 Varnish Software AS + * Copyright (c) 1983, 1993 The Regents of the University of California. * All rights reserved. * * Author: Dag-Erling Sm?rgrav @@ -25,11 +26,15 @@ * 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. + * + * Partially from: $FreeBSD: head/lib/libc/stdlib/random.c 303342 + * */ #include "config.h" #include +#include #include #include #include @@ -37,11 +42,108 @@ #include "vas.h" #include "vrnd.h" -#include "vtim.h" -#include "vsha256.h" + +/********************************************************************** + * Stripped down random(3) implementation from FreeBSD, to provide + * predicatable "random" numbers of testing purposes. + */ + +#define TYPE_3 3 /* x**31 + x**3 + 1 */ +#define DEG_3 31 +#define SEP_3 3 + +static uint32_t randtbl[DEG_3 + 1] = { + TYPE_3, + 0x2cf41758, 0x27bb3711, 0x4916d4d1, 0x7b02f59f, 0x9b8e28eb, 0xc0e80269, + 0x696f5c16, 0x878f1ff5, 0x52d9c07f, 0x916a06cd, 0xb50b3a20, 0x2776970a, + 0xee4eb2a6, 0xe94640ec, 0xb1d65612, 0x9d1ed968, 0x1043f6b7, 0xa3432a76, + 0x17eacbb9, 0x3c09e2eb, 0x4f8c2b3, 0x708a1f57, 0xee341814, 0x95d0e4d2, + 0xb06f216c, 0x8bd2e72e, 0x8f7c38d7, 0xcfc6a8fc, 0x2a59495, 0xa20d2a69, + 0xe29d12d1 +}; + +static uint32_t *fptr = &randtbl[SEP_3 + 1]; +static uint32_t *rptr = &randtbl[1]; + +static uint32_t * const state = &randtbl[1]; +static const int rand_deg = DEG_3; +static const int rand_sep = SEP_3; +static const uint32_t * const end_ptr = &randtbl[DEG_3 + 1]; + +static inline uint32_t +good_rand(uint32_t ctx) +{ +/* + * Compute x = (7^5 * x) mod (2^31 - 1) + * wihout overflowing 31 bits: + * (2^31 - 1) = 127773 * (7^5) + 2836 + * From "Random number generators: good ones are hard to find", + * Park and Miller, Communications of the ACM, vol. 31, no. 10, + * October 1988, p. 1195. + */ + int32_t hi, lo, x; + + /* Transform to [1, 0x7ffffffe] range. */ + x = (ctx % 0x7ffffffe) + 1; + hi = x / 127773; + lo = x % 127773; + x = 16807 * lo - 2836 * hi; + if (x < 0) + x += 0x7fffffff; + /* Transform to [0, 0x7ffffffd] range. */ + return (x - 1); +} + +void +VRND_SeedTestable(unsigned int x) +{ + int i, lim; + + state[0] = (uint32_t)x; + for (i = 1; i < rand_deg; i++) + state[i] = good_rand(state[i - 1]); + fptr = &state[rand_sep]; + rptr = &state[0]; + lim = 10 * rand_deg; + for (i = 0; i < lim; i++) + (void)random(); +} + +long +VRND_RandomTestable(void) +{ + uint32_t i; + uint32_t *f, *r; + + /* + * Use local variables rather than static variables for speed. + */ + f = fptr; r = rptr; + *f += *r; + i = *f >> 1; /* chucking least random bit */ + if (++f >= end_ptr) { + f = state; + ++r; + } + else if (++r >= end_ptr) { + r = state; + } + + fptr = f; rptr = r; + return ((long)i); +} + +double +VRND_RandomTestableDouble(void) +{ + return ( + ldexp((double)VRND_RandomTestable(), -31) + + ldexp((double)VRND_RandomTestable(), -62) + ); +} int -VRND_CryptoQuality(void *ptr, size_t len) +VRND_RandomCrypto(void *ptr, size_t len) { int fd; char *p; @@ -61,10 +163,14 @@ VRND_CryptoQuality(void *ptr, size_t len) } void -VRND_Seed(void) +VRND_SeedAll(void) { unsigned long seed; - AZ(VRND_CryptoQuality(&seed, sizeof seed)); + AZ(VRND_RandomCrypto(&seed, sizeof seed)); srandom(seed); + AZ(VRND_RandomCrypto(&seed, sizeof seed)); + VRND_SeedTestable(seed); + AZ(VRND_RandomCrypto(&seed, sizeof seed)); + srand48(seed); } diff --git a/lib/libvmod_directors/random.c b/lib/libvmod_directors/random.c index dee16ff..679fe2e 100644 --- a/lib/libvmod_directors/random.c +++ b/lib/libvmod_directors/random.c @@ -34,8 +34,9 @@ #include "cache/cache.h" #include "cache/cache_director.h" -#include "vrt.h" #include "vbm.h" +#include "vrnd.h" +#include "vrt.h" #include "vdir.h" @@ -69,7 +70,7 @@ vmod_random_resolve(const struct director *dir, struct worker *wrk, CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC); CHECK_OBJ_NOTNULL(bo, BUSYOBJ_MAGIC); CAST_OBJ_NOTNULL(rr, dir->priv, VMOD_DIRECTORS_RANDOM_MAGIC); - r = scalbn(random(), -31); + r = scalbn(VRND_RandomTestable(), -31); assert(r >= 0 && r < 1.0); be = vdir_pick_be(rr->vd, r, bo); return (be); diff --git a/lib/libvmod_directors/vmod.vcc b/lib/libvmod_directors/vmod.vcc index 529db05..4adbfba 100644 --- a/lib/libvmod_directors/vmod.vcc +++ b/lib/libvmod_directors/vmod.vcc @@ -134,6 +134,8 @@ Description The random director distributes load over the backends using a weighted random probability distribution. + The "testable" random generator in varnishd is used, which + enables deterministic tests to be run (See: d00004.vtc). Example new vdir = directors.random(); diff --git a/lib/libvmod_std/vmod.vcc b/lib/libvmod_std/vmod.vcc index 6b5683a..7808e14 100644 --- a/lib/libvmod_std/vmod.vcc +++ b/lib/libvmod_std/vmod.vcc @@ -72,6 +72,9 @@ $Function REAL random(REAL lo, REAL hi) Description Returns a random real number between *lo* and *hi*. + This function uses the "testable" random generator in varnishd + which enables determinstic tests to be run (See m00002.vtc). + This function should not be used for cryptographic applications. Example set beresp.http.random-number = std.random(1, 100); diff --git a/lib/libvmod_std/vmod_std.c b/lib/libvmod_std/vmod_std.c index ed66405..7f0b35d 100644 --- a/lib/libvmod_std/vmod_std.c +++ b/lib/libvmod_std/vmod_std.c @@ -37,6 +37,7 @@ #include #include +#include "vrnd.h" #include "vrt.h" #include "vtcp.h" #include "vsa.h" @@ -125,7 +126,7 @@ vmod_random(VRT_CTX, VCL_REAL lo, VCL_REAL hi) double a; CHECK_OBJ_NOTNULL(ctx, VRT_CTX_MAGIC); - a = drand48(); + a = VRND_RandomTestableDouble(); a *= hi - lo; a += lo; return (a); From phk at FreeBSD.org Mon Aug 29 22:35:08 2016 From: phk at FreeBSD.org (Poul-Henning Kamp) Date: Tue, 30 Aug 2016 00:35:08 +0200 Subject: [master] face33b Fix a unused value (when not debugging) spotted by Coverity Message-ID: commit face33b7cade7a503cd49cc5e54fddd90b350cda Author: Poul-Henning Kamp Date: Mon Aug 29 22:21:04 2016 +0000 Fix a unused value (when not debugging) spotted by Coverity diff --git a/bin/varnishd/cache/cache_esi_parse.c b/bin/varnishd/cache/cache_esi_parse.c index 744089a..4df0c8c 100644 --- a/bin/varnishd/cache/cache_esi_parse.c +++ b/bin/varnishd/cache/cache_esi_parse.c @@ -582,14 +582,11 @@ VEP_Parse(struct vep_state *vep, const char *p, size_t l) while (p < e) { AN(vep->state); - i = e - p; - if (i > 10) - i = 10; Debug("EP %s %d (%.*s) [%.*s]\n", vep->state, vep->remove, vep->tag_i, vep->tag, - i, p); + (e - p) > 10 ? 10 : (int)(e-p), p); assert(p >= vep->ver_p); /****************************************************** From phk at FreeBSD.org Mon Aug 29 22:35:09 2016 From: phk at FreeBSD.org (Poul-Henning Kamp) Date: Tue, 30 Aug 2016 00:35:09 +0200 Subject: [master] 76b6a04 Pointlessly fix a resource-leak in bail-out path, spotted by Coverity. Message-ID: commit 76b6a042eb04c169ae2e6cdc8d0c7e073a56d819 Author: Poul-Henning Kamp Date: Mon Aug 29 22:24:40 2016 +0000 Pointlessly fix a resource-leak in bail-out path, spotted by Coverity. diff --git a/lib/libvcc/vcc_acl.c b/lib/libvcc/vcc_acl.c index 7c7ec97..10d4ffa 100644 --- a/lib/libvcc/vcc_acl.c +++ b/lib/libvcc/vcc_acl.c @@ -225,6 +225,8 @@ vcc_acl_try_getaddrinfo(struct vcc *tl, struct acl_e *ae) res->ai_family, PF(ae->t_addr)); continue; } + if (tl->err) + freeaddrinfo(res0); ERRCHK(tl); } freeaddrinfo(res0); From phk at FreeBSD.org Tue Aug 30 07:03:09 2016 From: phk at FreeBSD.org (Poul-Henning Kamp) Date: Tue, 30 Aug 2016 09:03:09 +0200 Subject: [master] 46da12f Fix a ordering issue in loading labeled vcls Message-ID: commit 46da12f7bf95d1b8b19af54822d813e2a4401bef Author: Poul-Henning Kamp Date: Tue Aug 30 07:02:22 2016 +0000 Fix a ordering issue in loading labeled vcls diff --git a/bin/varnishd/cache/cache_ban.c b/bin/varnishd/cache/cache_ban.c index c475ff2..2de444b 100644 --- a/bin/varnishd/cache/cache_ban.c +++ b/bin/varnishd/cache/cache_ban.c @@ -299,20 +299,21 @@ static void ban_export(void) { struct ban *b; - struct vsb vsb; + struct vsb *vsb; unsigned ln; Lck_AssertHeld(&ban_mtx); ln = VSC_C_main->bans_persisted_bytes - VSC_C_main->bans_persisted_fragmentation; - AN(VSB_new(&vsb, NULL, ln, VSB_AUTOEXTEND)); + vsb = VSB_new_auto(); + AN(vsb); VTAILQ_FOREACH_REVERSE(b, &ban_head, banhead_s, list) { - AZ(VSB_bcat(&vsb, b->spec, ban_len(b->spec))); + AZ(VSB_bcat(vsb, b->spec, ban_len(b->spec))); } - AZ(VSB_finish(&vsb)); - assert(VSB_len(&vsb) == ln); - STV_BanExport((const uint8_t *)VSB_data(&vsb), VSB_len(&vsb)); - VSB_delete(&vsb); + AZ(VSB_finish(vsb)); + assert(VSB_len(vsb) == ln); + STV_BanExport((const uint8_t *)VSB_data(vsb), VSB_len(vsb)); + VSB_destroy(&vsb); VSC_C_main->bans_persisted_bytes = ln; VSC_C_main->bans_persisted_fragmentation = 0; } diff --git a/bin/varnishd/mgt/mgt_vcl.c b/bin/varnishd/mgt/mgt_vcl.c index 6b873a5..3fb2132 100644 --- a/bin/varnishd/mgt/mgt_vcl.c +++ b/bin/varnishd/mgt/mgt_vcl.c @@ -343,6 +343,8 @@ mgt_push_vcls_and_start(struct cli *cli, unsigned *status, char **p) AZ(mgt_vcl_setstate(cli, active_vcl, VCL_STATE_WARM)); VTAILQ_FOREACH(vp, &vclhead, list) { + if (!VTAILQ_EMPTY(&vp->dfrom)) + continue; if (!strcmp(vp->state, VCL_STATE_LABEL)) continue; if (mgt_cli_askchild(status, p, "vcl.load \"%s\" %s %d%s\n", @@ -360,6 +362,18 @@ mgt_push_vcls_and_start(struct cli *cli, unsigned *status, char **p) free(*p); *p = NULL; } + VTAILQ_FOREACH(vp, &vclhead, list) { + if (VTAILQ_EMPTY(&vp->dfrom)) + continue; + if (!strcmp(vp->state, VCL_STATE_LABEL)) + continue; + if (mgt_cli_askchild(status, p, "vcl.load \"%s\" %s %d%s\n", + vp->name, vp->fname, vp->warm, vp->state)) + return (1); + free(*p); + *p = NULL; + } + if (mgt_cli_askchild(status, p, "vcl.use \"%s\"\n", active_vcl->name)) return (1); free(*p); From phk at FreeBSD.org Tue Aug 30 08:51:10 2016 From: phk at FreeBSD.org (Poul-Henning Kamp) Date: Tue, 30 Aug 2016 10:51:10 +0200 Subject: [master] d7f0158 Trivial test-merge of some H2 stuff Message-ID: commit d7f0158bef740d7ccc9f7973347d455dcf9ff8e5 Author: Poul-Henning Kamp Date: Tue Aug 30 08:50:22 2016 +0000 Trivial test-merge of some H2 stuff diff --git a/include/tbl/http_headers.h b/include/tbl/http_headers.h index 198049b..12ccefd 100644 --- a/include/tbl/http_headers.h +++ b/include/tbl/http_headers.h @@ -83,7 +83,7 @@ H("Expect", H_Expect, 0 ) // 2616 14.20 H("Expires", H_Expires, 0 ) // 2616 14.21 H("From", H_From, 0 ) // 2616 14.22 H("Host", H_Host, 0 ) // 2616 14.23 -H("HTTP2-Settings", H_HTTP2_Settings, P|F|I|S) // httpbis-http2-16.txt +H("HTTP2-Settings", H_HTTP2_Settings, P|F|I|S) // 7540 3.2.1 H("If-Match", H_If_Match, F ) // 2616 14.24 H("If-Modified-Since", H_If_Modified_Since, F ) // 2616 14.25 H("If-None-Match", H_If_None_Match, F ) // 2616 14.26 diff --git a/include/tbl/vsl_tags.h b/include/tbl/vsl_tags.h index 26d8216..4a37594 100644 --- a/include/tbl/vsl_tags.h +++ b/include/tbl/vsl_tags.h @@ -533,5 +533,21 @@ SLTM(BackendStart, 0, "Backend request start", "\n" ) +SLTM(H2RxHdr, 0, "Received HTTP2 frame header", + "Binary data" +) + +SLTM(H2RxBody, 0, "Received HTTP2 frame body", + "Binary data" +) + +SLTM(H2TxHdr, 0, "Received HTTP2 frame header", + "Binary data" +) + +SLTM(H2TxBody, 0, "Received HTTP2 frame body", + "Binary data" +) + #undef NODEF_NOTICE /*lint -restore */ From phk at FreeBSD.org Tue Aug 30 08:58:11 2016 From: phk at FreeBSD.org (Poul-Henning Kamp) Date: Tue, 30 Aug 2016 10:58:11 +0200 Subject: [master] 8216919 Add VSLb_bin() which is meant for dumping binary blurbs into VSL. Message-ID: commit 8216919c48135f66e90a70c4e23484084b693f7a Author: Poul-Henning Kamp Date: Tue Aug 30 08:56:50 2016 +0000 Add VSLb_bin() which is meant for dumping binary blurbs into VSL. For now it hex-dumps them, but something smarter will happen once Martin has time. diff --git a/bin/varnishd/cache/cache.h b/bin/varnishd/cache/cache.h index c04662f..923a8d2 100644 --- a/bin/varnishd/cache/cache.h +++ b/bin/varnishd/cache/cache.h @@ -993,6 +993,7 @@ void VSLb(struct vsl_log *, enum VSL_tag_e tag, const char *fmt, ...) void VSLbt(struct vsl_log *, enum VSL_tag_e tag, txt t); void VSLb_ts(struct vsl_log *, const char *event, double first, double *pprev, double now); +void VSLb_bin(struct vsl_log *, enum VSL_tag_e, ssize_t, const void*); static inline void VSLb_ts_req(struct req *req, const char *event, double now) diff --git a/bin/varnishd/cache/cache_shmlog.c b/bin/varnishd/cache/cache_shmlog.c index 789e902..a4aee4a 100644 --- a/bin/varnishd/cache/cache_shmlog.c +++ b/bin/varnishd/cache/cache_shmlog.c @@ -378,6 +378,39 @@ VSLb_ts(struct vsl_log *vsl, const char *event, double first, double *pprev, *pprev = now; } +void +VSLb_bin(struct vsl_log *vsl, enum VSL_tag_e tag, ssize_t len, const void *ptr) +{ + char *p; + const uint8_t *pp = ptr; + int suff = 0; + size_t ll; + + assert(len >= 0); + AN(ptr); + if (vsl_tag_is_masked(tag)) + return; + vsl_sanity(vsl); + if (len * 2 + 1 > cache_param->vsl_reclen) { + len = (cache_param->vsl_reclen - 2) / 2; + suff = 1; + } + if (VSL_END(vsl->wlp, len * 2 + 1) >= vsl->wle) + VSL_Flush(vsl, 1); + assert(VSL_END(vsl->wlp, len * 2 + 1) < vsl->wle); + p = VSL_DATA(vsl->wlp); + for (ll = 0; ll < len; ll++) { + (void)sprintf(p, "%02x", *pp++); + p += 2; + } + if (suff) + *p++ = '-'; + *p = '\0'; + vsl->wlp = vsl_hdr(tag, vsl->wlp, len * 2 + 1, vsl->wid); + assert(vsl->wlp < vsl->wle); + vsl->wlr++; +} + /*-------------------------------------------------------------------- * Setup a VSL buffer, allocate space if none provided. */ From phk at FreeBSD.org Tue Aug 30 09:42:11 2016 From: phk at FreeBSD.org (Poul-Henning Kamp) Date: Tue, 30 Aug 2016 11:42:11 +0200 Subject: [master] e9f1b8c Make a workspace debugging output more usable Message-ID: commit e9f1b8ce82e0f575dd069aa73dd8287c33d01cdf Author: Poul-Henning Kamp Date: Tue Aug 30 09:01:16 2016 +0000 Make a workspace debugging output more usable diff --git a/bin/varnishd/cache/cache_ws.c b/bin/varnishd/cache/cache_ws.c index 36989fc..57f334f 100644 --- a/bin/varnishd/cache/cache_ws.c +++ b/bin/varnishd/cache/cache_ws.c @@ -244,7 +244,7 @@ void WS_ReleaseP(struct ws *ws, char *ptr) { WS_Assert(ws); - DSL(DBG_WORKSPACE, 0, "WS_ReleaseP(%p, %p)", ws, ptr); + DSL(DBG_WORKSPACE, 0, "WS_ReleaseP(%p, %p (%zd))", ws, ptr, ptr - ws->f); assert(ws->r != NULL); assert(ptr >= ws->f); assert(ptr <= ws->r); From phk at FreeBSD.org Tue Aug 30 09:42:11 2016 From: phk at FreeBSD.org (Poul-Henning Kamp) Date: Tue, 30 Aug 2016 11:42:11 +0200 Subject: [master] fb58772 Tag the request with the transport up front Message-ID: commit fb58772dde17ce0a3fc0f1b8059ccf3a39c070ca Author: Poul-Henning Kamp Date: Tue Aug 30 09:01:31 2016 +0000 Tag the request with the transport up front diff --git a/bin/varnishd/cache/cache_session.c b/bin/varnishd/cache/cache_session.c index 6c0e45f..2eeec0a 100644 --- a/bin/varnishd/cache/cache_session.c +++ b/bin/varnishd/cache/cache_session.c @@ -62,6 +62,7 @@ SES_SetTransport(struct worker *wrk, struct sess *sp, struct req *req, assert(xp->number > 0); sp->sattr[SA_TRANSPORT] = xp->number; + req->transport = xp; wrk->task.func = xp->new_session; wrk->task.priv = req; } From phk at FreeBSD.org Tue Aug 30 09:42:12 2016 From: phk at FreeBSD.org (Poul-Henning Kamp) Date: Tue, 30 Aug 2016 11:42:12 +0200 Subject: [master] c1bd0b6 Add H2 table includes Message-ID: commit c1bd0b6b794a247c3c3129dd4ae94890c795f51f Author: Poul-Henning Kamp Date: Tue Aug 30 09:40:49 2016 +0000 Add H2 table includes diff --git a/include/Makefile.am b/include/Makefile.am index 145d8b9..5ea662a 100644 --- a/include/Makefile.am +++ b/include/Makefile.am @@ -2,8 +2,8 @@ # API headers nobase_pkginclude_HEADERS = \ - tbl/acct_fields_req.h \ tbl/acct_fields_bereq.h \ + tbl/acct_fields_req.h \ tbl/backend_poll.h \ tbl/ban_vars.h \ tbl/bo_flags.h \ @@ -12,6 +12,10 @@ nobase_pkginclude_HEADERS = \ tbl/cli_cmds.h \ tbl/debug_bits.h \ tbl/feature_bits.h \ + tbl/h2_error.h \ + tbl/h2_frames.h \ + tbl/h2_settings.h \ + tbl/h2_stream.h \ tbl/http_headers.h \ tbl/http_response.h \ tbl/locks.h \ @@ -27,12 +31,17 @@ nobase_pkginclude_HEADERS = \ tbl/symbol_kind.h \ tbl/vcc_types.h \ tbl/vcl_returns.h \ + tbl/vhd_fsm.h \ + tbl/vhd_fsm_funcs.h \ + tbl/vhd_return.h \ + tbl/vhp_huffman.h \ + tbl/vhp_static.h \ tbl/vrt_stv_var.h \ - tbl/vsc_types.h \ tbl/vsc_all.h \ + tbl/vsc_f_main.h \ tbl/vsc_fields.h \ tbl/vsc_levels.h \ - tbl/vsc_f_main.h \ + tbl/vsc_types.h \ tbl/vsl_tags.h \ tbl/vsl_tags_http.h \ vapi/vsm.h \ diff --git a/include/tbl/h2_error.h b/include/tbl/h2_error.h new file mode 100644 index 0000000..c946454 --- /dev/null +++ b/include/tbl/h2_error.h @@ -0,0 +1,49 @@ +/*- + * Copyright (c) 2016 Varnish Software AS + * All rights reserved. + * + * Author: Poul-Henning Kamp + * + * 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. + * + * RFC7540 section 11.4 + */ + +/*lint -save -e525 -e539 */ + +H2_ERROR(NO_ERROR, 0x0, "Graceful shutdown") +H2_ERROR(PROTOCOL_ERROR, 0x1, "Protocol error detected") +H2_ERROR(INTERNAL_ERROR, 0x2, "Implementation fault") +H2_ERROR(FLOW_CONTROL_ERROR, 0x3, "Flow-control limits exceeded") +H2_ERROR(SETTINGS_TIMEOUT, 0x4, "Settings not acknowledged") +H2_ERROR(STREAM_CLOSED, 0x5, "Frame received for closed stream") +H2_ERROR(FRAME_SIZE_ERROR, 0x6, "Frame size incorrect") +H2_ERROR(REFUSED_STREAM, 0x7, "Stream not processed") +H2_ERROR(CANCEL, 0x8, "Stream cancelled") +H2_ERROR(COMPRESSION_ERROR, 0x9, "Compression state not updated") +H2_ERROR(CONNECT_ERROR, 0xa, "TCP connection error for CONNECT method") +H2_ERROR(ENHANCE_YOUR_CALM, 0xb, "Processing capacity exceeded") +H2_ERROR(INADEQUATE_SECURITY, 0xc, "Negotiated TLS parameters not acceptable") +H2_ERROR(HTTP_1_1_REQUIRED, 0xd, "Use HTTP/1.1 for the request") +#undef H2_ERROR + +/*lint -restore */ diff --git a/include/tbl/h2_frames.h b/include/tbl/h2_frames.h new file mode 100644 index 0000000..031b27c --- /dev/null +++ b/include/tbl/h2_frames.h @@ -0,0 +1,66 @@ +/*- + * Copyright (c) 2016 Varnish Software AS + * All rights reserved. + * + * Author: Poul-Henning Kamp + * + * 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. + * + * RFC7540 section 11.2 + */ + +/*lint -save -e525 -e539 */ + +#ifdef H2_FRAME +/* lower, upper, type, valid flags */ +H2_FRAME(data, DATA, 0x0, 0x09) +H2_FRAME(headers, HEADERS, 0x1, 0x2d) +H2_FRAME(priority, PRIORITY, 0x2, 0x00) +H2_FRAME(rst_stream, RST_STREAM, 0x3, 0x00) +H2_FRAME(settings, SETTINGS, 0x4, 0x01) +H2_FRAME(push_promise, PUSH_PROMISE, 0x5, 0x0c) +H2_FRAME(ping, PING, 0x6, 0x01) +H2_FRAME(goaway, GOAWAY, 0x7, 0x00) +H2_FRAME(window_update, WINDOW_UPDATE, 0x8, 0x00) +H2_FRAME(continuation, CONTINUATION, 0x9, 0x04) +#undef H2_FRAME +#endif + + +#ifdef H2_FRAME_FLAGS +H2_FRAME_FLAGS(none, NONE, 0x00) +H2_FRAME_FLAGS(data_end_stream, DATA_END_STREAM, 0x01) +H2_FRAME_FLAGS(data_padded, DATA_PADDED, 0x08) +H2_FRAME_FLAGS(headers_end_stream, HEADERS_END_STREAM, 0x01) +H2_FRAME_FLAGS(headers_end_headers, HEADERS_END_HEADERS, 0x04) +H2_FRAME_FLAGS(headers_padded, HEADERS_PADDED, 0x08) +H2_FRAME_FLAGS(headers_priority, HEADERS_PRIORITY, 0x20) +H2_FRAME_FLAGS(settings_ack, SETTINGS_ACK, 0x01) +H2_FRAME_FLAGS(push_promise_end_headers,PUSH_PROMISE_END_HEADERS, 0x04) +H2_FRAME_FLAGS(push_promise_padded, PUSH_PROMISE_PADDED, 0x08) +H2_FRAME_FLAGS(ping_ack, PING_ACK, 0x01) +H2_FRAME_FLAGS(continuation_end_headers,CONTINUATION_END_HEADERS, 0x04) +#undef H2_FRAME_FLAGS +#endif + + +/*lint -restore */ diff --git a/include/tbl/h2_settings.h b/include/tbl/h2_settings.h new file mode 100644 index 0000000..929094c --- /dev/null +++ b/include/tbl/h2_settings.h @@ -0,0 +1,41 @@ +/*- + * Copyright (c) 2016 Varnish Software AS + * All rights reserved. + * + * Author: Poul-Henning Kamp + * + * 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. + * + * RFC7540 section 11.3 + */ + +/*lint -save -e525 -e539 */ + +H2_SETTINGS(HEADER_TABLE_SIZE, 0x1, 4096) +H2_SETTINGS(ENABLE_PUSH, 0x2, 1) +H2_SETTINGS(MAX_CONCURRENT_STREAMS, 0x3, 0xffffffff) +H2_SETTINGS(INITIAL_WINDOW_SIZE, 0x4, 65535) +H2_SETTINGS(MAX_FRAME_SIZE, 0x5, 16384) +H2_SETTINGS(MAX_HEADER_LIST_SIZE, 0x6, 0xffffffff) +#undef H2_SETTINGS + +/*lint -restore */ diff --git a/include/tbl/h2_stream.h b/include/tbl/h2_stream.h new file mode 100644 index 0000000..21f6b3d --- /dev/null +++ b/include/tbl/h2_stream.h @@ -0,0 +1,42 @@ +/*- + * Copyright (c) 2016 Varnish Software AS + * All rights reserved. + * + * Author: Poul-Henning Kamp + * + * 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. + * + * RFC7540 section 11.4 + */ + +/*lint -save -e525 -e539 */ + +H2_STREAM(IDLE, "idle", "idle") +H2_STREAM(RESV_LOC, "r-loc", "reserved (local)") +H2_STREAM(RESV_REM, "r-rem", "reserved (remote)") +H2_STREAM(OPEN, "open", "open") +H2_STREAM(CLOS_LOC, "c-loc", "half closed (local)") +H2_STREAM(CLOS_REM, "c-rem", "half closed (remote)") +H2_STREAM(CLOSED, "closed", "closed") +#undef H2_STREAM + +/*lint -restore */ diff --git a/include/tbl/vhd_fsm.h b/include/tbl/vhd_fsm.h new file mode 100644 index 0000000..802b2e1 --- /dev/null +++ b/include/tbl/vhd_fsm.h @@ -0,0 +1,267 @@ +/*- + * Copyright (c) 2016 Varnish Software + * All rights reserved. + * + * Author: Martin Blix Grydeland + * + * 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. + * + */ + +/* Decode integer with prefix */ +#define VHD_FSM_INTEGER(STATE, pfx) \ + VHD_FSM(STATE, INTEGER, pfx, 0) +/* Goto */ +#define VHD_FSM_GOTO(STATE, TO) \ + VHD_FSM(STATE, GOTO, VHD_S_##TO, 0) +/* Label */ +#define VHD_FSM_LABEL(STATE) \ + VHD_FSM(STATE, SKIP, 0, 0) +/* Set max table size */ +#define VHD_FSM_SET_MAX(STATE) \ + VHD_FSM(STATE, SET_MAX, 0, 0) +/* Set index */ +#define VHD_FSM_SET_IDX(STATE) \ + VHD_FSM(STATE, SET_IDX, 0, 0) +/* Table lookup from index */ +#define VHD_FSM_LOOKUP(STATE, type) \ + VHD_FSM(STATE, LOOKUP, type, 0) +/* New table entry */ +#define VHD_FSM_NEW(STATE) \ + VHD_FSM(STATE, NEW, 0, 0) +/* New table entry, name from index */ +#define VHD_FSM_NEW_IDX(STATE) \ + VHD_FSM(STATE, NEW_IDX, 0, 0) +/* Branch if index is zero */ +#define VHD_FSM_BRANCH_ZIDX(STATE, BRANCH) \ + VHD_FSM(STATE, BRANCH_ZIDX, VHD_S_##BRANCH, 0) +/* Branch if bit 0 */ +#define VHD_FSM_BRANCH_BIT0(STATE, BRANCH) \ + VHD_FSM(STATE, BRANCH_BIT0, VHD_S_##BRANCH, 0) + +/* Decode a literal: + * 0 1 2 3 4 5 6 7 + * +---+---+-----------------------+ + * | H | Length (7+) | + * +---+---------------------------+ + * | String (Length octets) | + * +-------------------------------+ + */ +#define VHD_FSM_LITERAL(STATE, type, flags) \ + VHD_FSM_BRANCH_BIT0(STATE, STATE##_HUFFMAN_LEN) \ + VHD_FSM_INTEGER(STATE##_RAW_LEN, 7) \ + VHD_FSM(STATE##_RAW, RAW, type, flags) \ + VHD_FSM_GOTO(STATE##_RAW_FINISH, STATE##_EXIT) \ + VHD_FSM_INTEGER(STATE##_HUFFMAN_LEN, 7) \ + VHD_FSM(STATE##_HUFFMAN, HUFFMAN, type, flags) \ + VHD_FSM_LABEL(STATE##_EXIT) + +/* The idle state */ +VHD_FSM(IDLE, IDLE, 0, 0) + +/* Indexed header field + * (RFC 7541 section 6.1) + * + * 0 1 2 3 4 5 6 7 + * +---+---+---+---+---+---+---+---+ + * | 1 | Index(7+) | + * +---+---------------------------+ + */ +VHD_FSM_LABEL(HP61_START) +VHD_FSM_INTEGER(HP61_IDX, 7) +VHD_FSM_SET_IDX(HP61_SET_IDX) +VHD_FSM_LOOKUP(HP61_NAME, VHD_NAME) +VHD_FSM_LOOKUP(HP61_VAL, VHD_VALUE) +VHD_FSM_GOTO(HP61_FINISH, IDLE) + +/* Literal header field with incremental indexing + * (RFC 7541 section 6.2.1) + * + * HP621_IN - Indexed name + * + * 0 1 2 3 4 5 6 7 + * +---+---+---+---+---+---+---+---+ + * | 0 | 1 | Index (6+) | + * +---+---+-----------------------+ + * | H | Value Length (7+) | + * +---+---------------------------+ + * | Value String (Length octets) | + * +-------------------------------+ + * + * HP621_NN - New name + * + * 0 1 2 3 4 5 6 7 + * +---+---+---+---+---+---+---+---+ + * | 0 | 1 | 0 | + * +---+---+-----------------------+ + * | H | Name Length (7+) | + * +---+---------------------------+ + * | Name String (Length octets) | + * +---+---------------------------+ + * | H | Value Length (7+) | + * +---+---------------------------+ + * | Value String (Length octets) | + * +-------------------------------+ + */ +VHD_FSM_LABEL(HP621_START) +VHD_FSM_INTEGER(HP621_IDX, 6) +VHD_FSM_SET_IDX(HP621_SET_IDX) +VHD_FSM_BRANCH_ZIDX(HP621_BRANCH_ZIDX, HP621_NN_NEW) +/* HP621_IN - Indexed name */ +VHD_FSM_LOOKUP(HP621_IN_NAME, VHD_NAME) +VHD_FSM_NEW_IDX(HP621_IN_NEW) +VHD_FSM_LITERAL(HP621_IN_VAL, VHD_VALUE, VHD_INCREMENTAL) +VHD_FSM_GOTO(HP621_IN_FINISH, IDLE) +/* HP621_NN - New name */ +VHD_FSM_NEW(HP621_NN_NEW) +VHD_FSM_LITERAL(HP621_NN_NAME, VHD_NAME, VHD_INCREMENTAL) +VHD_FSM_LITERAL(HP621_NN_VAL, VHD_VALUE, VHD_INCREMENTAL) +VHD_FSM_GOTO(HP621_NN_FINISH, IDLE) + +/* Literal header field without indexing + * (RFC 7541 section 622) + * + * HP622_IN - Indexed name + * + * 0 1 2 3 4 5 6 7 + * +---+---+---+---+---+---+---+---+ + * | 0 | 0 | 0 | 0 | Index (4+) | + * +---+---+-----------------------+ + * | H | Value Length (7+) | + * +---+---------------------------+ + * | Value String (Length octets) | + * +-------------------------------+ + * + * HP622_NN - New name + * + * 0 1 2 3 4 5 6 7 + * +---+---+---+---+---+---+---+---+ + * | 0 | 0 | 0 | 0 | 0 | + * +---+---+-----------------------+ + * | H | Name Length (7+) | + * +---+---------------------------+ + * | Name String (Length octets) | + * +---+---------------------------+ + * | H | Value Length (7+) | + * +---+---------------------------+ + * | Value String (Length octets) | + * +-------------------------------+ + */ +VHD_FSM_LABEL(HP622_START) +VHD_FSM_INTEGER(HP622_IDX, 4) +VHD_FSM_SET_IDX(HP622_SET_IDX) +VHD_FSM_BRANCH_ZIDX(HP622_BR_ZIDX, HP622_NN_NAME) +/* HP622_IN - Indexed name */ +VHD_FSM_LOOKUP(HP622_IN_NAME, VHD_NAME) +VHD_FSM_LITERAL(HP622_IN_VAL, VHD_VALUE, 0) +VHD_FSM_GOTO(HP622_IN_FINISH, IDLE) +/* HP622_NN - New name */ +VHD_FSM_LITERAL(HP622_NN_NAME, VHD_NAME, 0) +VHD_FSM_LITERAL(HP622_NN_VAL, VHD_VALUE, 0) +VHD_FSM_GOTO(HP622_NN_FINISH, IDLE) + +/* Literal header field never indexed + * (RFC 7541 section 6.2.3) + * + * HP623_IN - Indexed name + * + * 0 1 2 3 4 5 6 7 + * +---+---+---+---+---+---+---+---+ + * | 0 | 0 | 0 | 1 | Index (4+) | + * +---+---+-----------------------+ + * | H | Value Length (7+) | + * +---+---------------------------+ + * | Value String (Length octets) | + * +-------------------------------+ + * + * HP623_NN - New name + * + * 0 1 2 3 4 5 6 7 + * +---+---+---+---+---+---+---+---+ + * | 0 | 0 | 0 | 1 | 0 | + * +---+---+-----------------------+ + * | H | Name Length (7+) | + * +---+---------------------------+ + * | Name String (Length octets) | + * +---+---------------------------+ + * | H | Value Length (7+) | + * +---+---------------------------+ + * | Value String (Length octets) | + * +-------------------------------+ + */ +VHD_FSM_LABEL(HP623_START) +VHD_FSM_INTEGER(HP623_IDX, 4) +VHD_FSM_SET_IDX(HP623_SET_IDX) +VHD_FSM_BRANCH_ZIDX(HP623_BR_ZIDX, HP623_NN_NAME) +/* HP623_IN - Indexed name */ +VHD_FSM_LOOKUP(HP623_IN_NAME, VHD_NAME_SEC) +VHD_FSM_LITERAL(HP623_IN_VAL, VHD_VALUE_SEC, 0) +VHD_FSM_GOTO(HP623_IN_FINISH, IDLE) +/* HP623_NN - New name */ +VHD_FSM_LITERAL(HP623_NN_NAME, VHD_NAME_SEC, 0) +VHD_FSM_LITERAL(HP623_NN_VAL, VHD_VALUE_SEC, 0) +VHD_FSM_GOTO(HP623_NN_FINISH, IDLE) + +/* Dynamic table size update + * (RFC 7541 section 6.3) + * + * 0 1 2 3 4 5 6 7 + * +---+---+---+---+---+---+---+---+ + * | 0 | 0 | 1 | Max size (5+) | + * +---+---------------------------+ + */ +VHD_FSM_LABEL(HP63_START) +VHD_FSM_INTEGER(HP63_SIZE, 5) +VHD_FSM_SET_MAX(HP63_SET_MAX) +VHD_FSM_GOTO(HP63_FINISH, IDLE) + +/*---------------------------------------------------------------------*/ +/* States used for unit testing */ + +#ifdef DECODE_TEST_DRIVER + +/* Test integer prefix 5 */ +VHD_FSM_LABEL(TEST_INT5) +VHD_FSM_INTEGER(TEST_INT5_INT, 5) +VHD_FSM_GOTO(TEST_INT5_FINISH, IDLE) + +/* Test literal decoding */ +VHD_FSM_LABEL(TEST_LITERAL) +VHD_FSM_LITERAL(TEST_LITERAL_NAME, VHD_NAME, 0) +VHD_FSM_LITERAL(TEST_LITERAL_VALUE, VHD_VALUE, 0) +VHD_FSM_GOTO(TEST_LITERAL_FINISH, IDLE) + +#endif /* DECODE_TEST_DRIVER */ + +/*---------------------------------------------------------------------*/ +/* Clean up macro namespace */ +#undef VHD_FSM_INTEGER +#undef VHD_FSM_GOTO +#undef VHD_FSM_LABEL +#undef VHD_FSM_SET_MAX +#undef VHD_FSM_SET_IDX +#undef VHD_FSM_LOOKUP +#undef VHD_FSM_NEW +#undef VHD_FSM_NEW_IDX +#undef VHD_FSM_BRANCH_ZIDX +#undef VHD_FSM_BRANCH_BIT0 +#undef VHD_FSM_LITERAL diff --git a/include/tbl/vhd_fsm_funcs.h b/include/tbl/vhd_fsm_funcs.h new file mode 100644 index 0000000..92cd082 --- /dev/null +++ b/include/tbl/vhd_fsm_funcs.h @@ -0,0 +1,42 @@ +/*- + * Copyright (c) 2016 Varnish Software + * All rights reserved. + * + * Author: Martin Blix Grydeland + * + * 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. + * + */ + +VHD_FSM_FUNC(SKIP, vhd_skip) +VHD_FSM_FUNC(GOTO, vhd_goto) +VHD_FSM_FUNC(IDLE, vhd_idle) +VHD_FSM_FUNC(INTEGER, vhd_integer) +VHD_FSM_FUNC(SET_MAX, vhd_set_max) +VHD_FSM_FUNC(SET_IDX, vhd_set_idx) +VHD_FSM_FUNC(LOOKUP, vhd_lookup) +VHD_FSM_FUNC(NEW, vhd_new) +VHD_FSM_FUNC(NEW_IDX, vhd_new_idx) +VHD_FSM_FUNC(BRANCH_ZIDX, vhd_branch_zidx) +VHD_FSM_FUNC(BRANCH_BIT0, vhd_branch_bit0) +VHD_FSM_FUNC(RAW, vhd_raw) +VHD_FSM_FUNC(HUFFMAN, vhd_huffman) diff --git a/include/tbl/vhd_return.h b/include/tbl/vhd_return.h new file mode 100644 index 0000000..31f6cca --- /dev/null +++ b/include/tbl/vhd_return.h @@ -0,0 +1,44 @@ +/*- + * Copyright (c) 2016 Varnish Software + * All rights reserved. + * + * Author: Martin Blix Grydeland + * + * 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. + * + */ + +VHD_RET(ERR_ARG, -1, "Invalid HPACK instruction") +VHD_RET(ERR_INT, -2, "Integer overflow") +VHD_RET(ERR_IDX, -3, "Invalid table index") +VHD_RET(ERR_LEN, -4, "Invalid length") +VHD_RET(ERR_HUF, -5, "Invalid huffman code") +VHD_RET(ERR_UPD, -6, "Spurious update") + +VHD_RET(OK, 0, "OK") +VHD_RET(MORE, 1, "Feed me") +VHD_RET(NAME, 2, "Name") +VHD_RET(VALUE, 3, "Value") +VHD_RET(NAME_SEC, 4, "Name never index") +VHD_RET(VALUE_SEC, 5, "Value never index") +VHD_RET(BUF, 6, "Stuffed") +VHD_RET(AGAIN, 7, "Call again") diff --git a/include/tbl/vhp_huffman.h b/include/tbl/vhp_huffman.h new file mode 100644 index 0000000..ad9ebe2 --- /dev/null +++ b/include/tbl/vhp_huffman.h @@ -0,0 +1,264 @@ +/*- + * Written by Dridi Boukelmoune + * + * This file is in the public domain. + * + * HPACK Huffman table, but sorted by codes (RFC 7541 Appendix B) + */ + +HPH(0x30, 0x00000000, 5) /* '0' */ +HPH(0x31, 0x00000001, 5) /* '1' */ +HPH(0x32, 0x00000002, 5) /* '2' */ +HPH(0x61, 0x00000003, 5) /* 'a' */ +HPH(0x63, 0x00000004, 5) /* 'c' */ +HPH(0x65, 0x00000005, 5) /* 'e' */ +HPH(0x69, 0x00000006, 5) /* 'i' */ +HPH(0x6f, 0x00000007, 5) /* 'o' */ +HPH(0x73, 0x00000008, 5) /* 's' */ +HPH(0x74, 0x00000009, 5) /* 't' */ +HPH(0x20, 0x00000014, 6) /* ' ' */ +HPH(0x25, 0x00000015, 6) /* '%' */ +HPH(0x2d, 0x00000016, 6) /* '-' */ +HPH(0x2e, 0x00000017, 6) /* '.' */ +HPH(0x2f, 0x00000018, 6) /* '/' */ +HPH(0x33, 0x00000019, 6) /* '3' */ +HPH(0x34, 0x0000001a, 6) /* '4' */ +HPH(0x35, 0x0000001b, 6) /* '5' */ +HPH(0x36, 0x0000001c, 6) /* '6' */ +HPH(0x37, 0x0000001d, 6) /* '7' */ +HPH(0x38, 0x0000001e, 6) /* '8' */ +HPH(0x39, 0x0000001f, 6) /* '9' */ +HPH(0x3d, 0x00000020, 6) /* '=' */ +HPH(0x41, 0x00000021, 6) /* 'A' */ +HPH(0x5f, 0x00000022, 6) /* '_' */ +HPH(0x62, 0x00000023, 6) /* 'b' */ +HPH(0x64, 0x00000024, 6) /* 'd' */ +HPH(0x66, 0x00000025, 6) /* 'f' */ +HPH(0x67, 0x00000026, 6) /* 'g' */ +HPH(0x68, 0x00000027, 6) /* 'h' */ +HPH(0x6c, 0x00000028, 6) /* 'l' */ +HPH(0x6d, 0x00000029, 6) /* 'm' */ +HPH(0x6e, 0x0000002a, 6) /* 'n' */ +HPH(0x70, 0x0000002b, 6) /* 'p' */ +HPH(0x72, 0x0000002c, 6) /* 'r' */ +HPH(0x75, 0x0000002d, 6) /* 'u' */ +HPH(0x3a, 0x0000005c, 7) /* ':' */ +HPH(0x42, 0x0000005d, 7) /* 'B' */ +HPH(0x43, 0x0000005e, 7) /* 'C' */ +HPH(0x44, 0x0000005f, 7) /* 'D' */ +HPH(0x45, 0x00000060, 7) /* 'E' */ +HPH(0x46, 0x00000061, 7) /* 'F' */ +HPH(0x47, 0x00000062, 7) /* 'G' */ +HPH(0x48, 0x00000063, 7) /* 'H' */ +HPH(0x49, 0x00000064, 7) /* 'I' */ +HPH(0x4a, 0x00000065, 7) /* 'J' */ +HPH(0x4b, 0x00000066, 7) /* 'K' */ +HPH(0x4c, 0x00000067, 7) /* 'L' */ +HPH(0x4d, 0x00000068, 7) /* 'M' */ +HPH(0x4e, 0x00000069, 7) /* 'N' */ +HPH(0x4f, 0x0000006a, 7) /* 'O' */ +HPH(0x50, 0x0000006b, 7) /* 'P' */ +HPH(0x51, 0x0000006c, 7) /* 'Q' */ +HPH(0x52, 0x0000006d, 7) /* 'R' */ +HPH(0x53, 0x0000006e, 7) /* 'S' */ +HPH(0x54, 0x0000006f, 7) /* 'T' */ +HPH(0x55, 0x00000070, 7) /* 'U' */ +HPH(0x56, 0x00000071, 7) /* 'V' */ +HPH(0x57, 0x00000072, 7) /* 'W' */ +HPH(0x59, 0x00000073, 7) /* 'Y' */ +HPH(0x6a, 0x00000074, 7) /* 'j' */ +HPH(0x6b, 0x00000075, 7) /* 'k' */ +HPH(0x71, 0x00000076, 7) /* 'q' */ +HPH(0x76, 0x00000077, 7) /* 'v' */ +HPH(0x77, 0x00000078, 7) /* 'w' */ +HPH(0x78, 0x00000079, 7) /* 'x' */ +HPH(0x79, 0x0000007a, 7) /* 'y' */ +HPH(0x7a, 0x0000007b, 7) /* 'z' */ +HPH(0x26, 0x000000f8, 8) /* '&' */ +HPH(0x2a, 0x000000f9, 8) /* '*' */ +HPH(0x2c, 0x000000fa, 8) /* ',' */ +HPH(0x3b, 0x000000fb, 8) /* ';' */ +HPH(0x58, 0x000000fc, 8) /* 'X' */ +HPH(0x5a, 0x000000fd, 8) /* 'Z' */ +HPH(0x21, 0x000003f8, 10) /* '!' */ +HPH(0x22, 0x000003f9, 10) /* '"' */ +HPH(0x28, 0x000003fa, 10) /* '(' */ +HPH(0x29, 0x000003fb, 10) /* ')' */ +HPH(0x3f, 0x000003fc, 10) /* '?' */ +HPH(0x27, 0x000007fa, 11) /* ''' */ +HPH(0x2b, 0x000007fb, 11) /* '+' */ +HPH(0x7c, 0x000007fc, 11) /* '|' */ +HPH(0x23, 0x00000ffa, 12) /* '#' */ +HPH(0x3e, 0x00000ffb, 12) /* '>' */ +HPH(0x00, 0x00001ff8, 13) +HPH(0x24, 0x00001ff9, 13) /* '$' */ +HPH(0x40, 0x00001ffa, 13) /* '@' */ +HPH(0x5b, 0x00001ffb, 13) /* '[' */ +HPH(0x5d, 0x00001ffc, 13) /* ']' */ +HPH(0x7e, 0x00001ffd, 13) /* '~' */ +HPH(0x5e, 0x00003ffc, 14) /* '^' */ +HPH(0x7d, 0x00003ffd, 14) /* '}' */ +HPH(0x3c, 0x00007ffc, 15) /* '<' */ +HPH(0x60, 0x00007ffd, 15) /* '`' */ +HPH(0x7b, 0x00007ffe, 15) /* '{' */ +HPH(0x5c, 0x0007fff0, 19) /* '\' */ +HPH(0xc3, 0x0007fff1, 19) +HPH(0xd0, 0x0007fff2, 19) +HPH(0x80, 0x000fffe6, 20) +HPH(0x82, 0x000fffe7, 20) +HPH(0x83, 0x000fffe8, 20) +HPH(0xa2, 0x000fffe9, 20) +HPH(0xb8, 0x000fffea, 20) +HPH(0xc2, 0x000fffeb, 20) +HPH(0xe0, 0x000fffec, 20) +HPH(0xe2, 0x000fffed, 20) +HPH(0x99, 0x001fffdc, 21) +HPH(0xa1, 0x001fffdd, 21) +HPH(0xa7, 0x001fffde, 21) +HPH(0xac, 0x001fffdf, 21) +HPH(0xb0, 0x001fffe0, 21) +HPH(0xb1, 0x001fffe1, 21) +HPH(0xb3, 0x001fffe2, 21) +HPH(0xd1, 0x001fffe3, 21) +HPH(0xd8, 0x001fffe4, 21) +HPH(0xd9, 0x001fffe5, 21) +HPH(0xe3, 0x001fffe6, 21) +HPH(0xe5, 0x001fffe7, 21) +HPH(0xe6, 0x001fffe8, 21) +HPH(0x81, 0x003fffd2, 22) +HPH(0x84, 0x003fffd3, 22) +HPH(0x85, 0x003fffd4, 22) +HPH(0x86, 0x003fffd5, 22) +HPH(0x88, 0x003fffd6, 22) +HPH(0x92, 0x003fffd7, 22) +HPH(0x9a, 0x003fffd8, 22) +HPH(0x9c, 0x003fffd9, 22) +HPH(0xa0, 0x003fffda, 22) +HPH(0xa3, 0x003fffdb, 22) +HPH(0xa4, 0x003fffdc, 22) +HPH(0xa9, 0x003fffdd, 22) +HPH(0xaa, 0x003fffde, 22) +HPH(0xad, 0x003fffdf, 22) +HPH(0xb2, 0x003fffe0, 22) +HPH(0xb5, 0x003fffe1, 22) +HPH(0xb9, 0x003fffe2, 22) +HPH(0xba, 0x003fffe3, 22) +HPH(0xbb, 0x003fffe4, 22) +HPH(0xbd, 0x003fffe5, 22) +HPH(0xbe, 0x003fffe6, 22) +HPH(0xc4, 0x003fffe7, 22) +HPH(0xc6, 0x003fffe8, 22) +HPH(0xe4, 0x003fffe9, 22) +HPH(0xe8, 0x003fffea, 22) +HPH(0xe9, 0x003fffeb, 22) +HPH(0x01, 0x007fffd8, 23) +HPH(0x87, 0x007fffd9, 23) +HPH(0x89, 0x007fffda, 23) +HPH(0x8a, 0x007fffdb, 23) +HPH(0x8b, 0x007fffdc, 23) +HPH(0x8c, 0x007fffdd, 23) +HPH(0x8d, 0x007fffde, 23) +HPH(0x8f, 0x007fffdf, 23) +HPH(0x93, 0x007fffe0, 23) +HPH(0x95, 0x007fffe1, 23) +HPH(0x96, 0x007fffe2, 23) +HPH(0x97, 0x007fffe3, 23) +HPH(0x98, 0x007fffe4, 23) +HPH(0x9b, 0x007fffe5, 23) +HPH(0x9d, 0x007fffe6, 23) +HPH(0x9e, 0x007fffe7, 23) +HPH(0xa5, 0x007fffe8, 23) +HPH(0xa6, 0x007fffe9, 23) +HPH(0xa8, 0x007fffea, 23) +HPH(0xae, 0x007fffeb, 23) +HPH(0xaf, 0x007fffec, 23) +HPH(0xb4, 0x007fffed, 23) +HPH(0xb6, 0x007fffee, 23) +HPH(0xb7, 0x007fffef, 23) +HPH(0xbc, 0x007ffff0, 23) +HPH(0xbf, 0x007ffff1, 23) +HPH(0xc5, 0x007ffff2, 23) +HPH(0xe7, 0x007ffff3, 23) +HPH(0xef, 0x007ffff4, 23) +HPH(0x09, 0x00ffffea, 24) +HPH(0x8e, 0x00ffffeb, 24) +HPH(0x90, 0x00ffffec, 24) +HPH(0x91, 0x00ffffed, 24) +HPH(0x94, 0x00ffffee, 24) +HPH(0x9f, 0x00ffffef, 24) +HPH(0xab, 0x00fffff0, 24) +HPH(0xce, 0x00fffff1, 24) +HPH(0xd7, 0x00fffff2, 24) +HPH(0xe1, 0x00fffff3, 24) +HPH(0xec, 0x00fffff4, 24) +HPH(0xed, 0x00fffff5, 24) +HPH(0xc7, 0x01ffffec, 25) +HPH(0xcf, 0x01ffffed, 25) +HPH(0xea, 0x01ffffee, 25) +HPH(0xeb, 0x01ffffef, 25) +HPH(0xc0, 0x03ffffe0, 26) +HPH(0xc1, 0x03ffffe1, 26) +HPH(0xc8, 0x03ffffe2, 26) +HPH(0xc9, 0x03ffffe3, 26) +HPH(0xca, 0x03ffffe4, 26) +HPH(0xcd, 0x03ffffe5, 26) +HPH(0xd2, 0x03ffffe6, 26) +HPH(0xd5, 0x03ffffe7, 26) +HPH(0xda, 0x03ffffe8, 26) +HPH(0xdb, 0x03ffffe9, 26) +HPH(0xee, 0x03ffffea, 26) +HPH(0xf0, 0x03ffffeb, 26) +HPH(0xf2, 0x03ffffec, 26) +HPH(0xf3, 0x03ffffed, 26) +HPH(0xff, 0x03ffffee, 26) +HPH(0xcb, 0x07ffffde, 27) +HPH(0xcc, 0x07ffffdf, 27) +HPH(0xd3, 0x07ffffe0, 27) +HPH(0xd4, 0x07ffffe1, 27) +HPH(0xd6, 0x07ffffe2, 27) +HPH(0xdd, 0x07ffffe3, 27) +HPH(0xde, 0x07ffffe4, 27) +HPH(0xdf, 0x07ffffe5, 27) +HPH(0xf1, 0x07ffffe6, 27) +HPH(0xf4, 0x07ffffe7, 27) +HPH(0xf5, 0x07ffffe8, 27) +HPH(0xf6, 0x07ffffe9, 27) +HPH(0xf7, 0x07ffffea, 27) +HPH(0xf8, 0x07ffffeb, 27) +HPH(0xfa, 0x07ffffec, 27) +HPH(0xfb, 0x07ffffed, 27) +HPH(0xfc, 0x07ffffee, 27) +HPH(0xfd, 0x07ffffef, 27) +HPH(0xfe, 0x07fffff0, 27) +HPH(0x02, 0x0fffffe2, 28) +HPH(0x03, 0x0fffffe3, 28) +HPH(0x04, 0x0fffffe4, 28) +HPH(0x05, 0x0fffffe5, 28) +HPH(0x06, 0x0fffffe6, 28) +HPH(0x07, 0x0fffffe7, 28) +HPH(0x08, 0x0fffffe8, 28) +HPH(0x0b, 0x0fffffe9, 28) +HPH(0x0c, 0x0fffffea, 28) +HPH(0x0e, 0x0fffffeb, 28) +HPH(0x0f, 0x0fffffec, 28) +HPH(0x10, 0x0fffffed, 28) +HPH(0x11, 0x0fffffee, 28) +HPH(0x12, 0x0fffffef, 28) +HPH(0x13, 0x0ffffff0, 28) +HPH(0x14, 0x0ffffff1, 28) +HPH(0x15, 0x0ffffff2, 28) +HPH(0x17, 0x0ffffff3, 28) +HPH(0x18, 0x0ffffff4, 28) +HPH(0x19, 0x0ffffff5, 28) +HPH(0x1a, 0x0ffffff6, 28) +HPH(0x1b, 0x0ffffff7, 28) +HPH(0x1c, 0x0ffffff8, 28) +HPH(0x1d, 0x0ffffff9, 28) +HPH(0x1e, 0x0ffffffa, 28) +HPH(0x1f, 0x0ffffffb, 28) +HPH(0x7f, 0x0ffffffc, 28) +HPH(0xdc, 0x0ffffffd, 28) +HPH(0xf9, 0x0ffffffe, 28) +HPH(0x0a, 0x3ffffffc, 30) +HPH(0x0d, 0x3ffffffd, 30) +HPH(0x16, 0x3ffffffe, 30) diff --git a/include/tbl/vhp_static.h b/include/tbl/vhp_static.h new file mode 100644 index 0000000..576c180 --- /dev/null +++ b/include/tbl/vhp_static.h @@ -0,0 +1,69 @@ +/*- + * Written by Dridi Boukelmoune + * + * This file is in the public domain. + * + * HPACK: Static Table Definition (RFC 7540 Appendix A) + */ + +HPS( 1, ":authority", "") +HPS( 2, ":method", "GET") +HPS( 3, ":method", "POST") +HPS( 4, ":path", "/") +HPS( 5, ":path", "/index.html") +HPS( 6, ":scheme", "http") +HPS( 7, ":scheme", "https") +HPS( 8, ":status", "200") +HPS( 9, ":status", "204") +HPS(10, ":status", "206") +HPS(11, ":status", "304") +HPS(12, ":status", "400") +HPS(13, ":status", "404") +HPS(14, ":status", "500") +HPS(15, "accept-charset", "") +HPS(16, "accept-encoding", "gzip, deflate") +HPS(17, "accept-language", "") +HPS(18, "accept-ranges", "") +HPS(19, "accept", "") +HPS(20, "access-control-allow-origin", "") +HPS(21, "age", "") +HPS(22, "allow", "") +HPS(23, "authorization", "") +HPS(24, "cache-control", "") +HPS(25, "content-disposition", "") +HPS(26, "content-encoding", "") +HPS(27, "content-language", "") +HPS(28, "content-length", "") +HPS(29, "content-location", "") +HPS(30, "content-range", "") +HPS(31, "content-type", "") +HPS(32, "cookie", "") +HPS(33, "date", "") +HPS(34, "etag", "") +HPS(35, "expect", "") +HPS(36, "expires", "") +HPS(37, "from", "") +HPS(38, "host", "") +HPS(39, "if-match", "") +HPS(40, "if-modified-since", "") +HPS(41, "if-none-match", "") +HPS(42, "if-range", "") +HPS(43, "if-unmodified-since", "") +HPS(44, "last-modified", "") +HPS(45, "link", "") +HPS(46, "location", "") +HPS(47, "max-forwards", "") +HPS(48, "proxy-authenticate", "") +HPS(49, "proxy-authorization", "") +HPS(50, "range", "") +HPS(51, "referer", "") +HPS(52, "refresh", "") +HPS(53, "retry-after", "") +HPS(54, "server", "") +HPS(55, "set-cookie", "") +HPS(56, "strict-transport-security", "") +HPS(57, "transfer-encoding", "") +HPS(58, "user-agent", "") +HPS(59, "vary", "") +HPS(60, "via", "") +HPS(61, "www-authenticate", "") From phk at FreeBSD.org Tue Aug 30 10:41:11 2016 From: phk at FreeBSD.org (Poul-Henning Kamp) Date: Tue, 30 Aug 2016 12:41:11 +0200 Subject: [master] 59c4ad8 Add gquintar's HTTP2 support for varnishtest. Message-ID: commit 59c4ad8e8e231c4ad7acbd36d40e966c7038ee2a Author: Poul-Henning Kamp Date: Tue Aug 30 10:39:40 2016 +0000 Add gquintar's HTTP2 support for varnishtest. diff --git a/bin/varnishtest/Makefile.am b/bin/varnishtest/Makefile.am index d6dbc10..1c519e1 100644 --- a/bin/varnishtest/Makefile.am +++ b/bin/varnishtest/Makefile.am @@ -23,6 +23,7 @@ AM_CPPFLAGS = \ bin_PROGRAMS = varnishtest varnishtest_SOURCES = \ + hpack.h \ programs.h \ vmods.h \ vtc.c \ @@ -30,7 +31,15 @@ varnishtest_SOURCES = \ vtc_barrier.c \ vtc_client.c \ vtc_http.c \ + vtc_http.h \ vtc_log.c \ + vtc_http2.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_logexp.c \ vtc_main.c \ vtc_process.c \ diff --git a/bin/varnishtest/flint.lnt b/bin/varnishtest/flint.lnt index b7306f1..a02b3be 100644 --- a/bin/varnishtest/flint.lnt +++ b/bin/varnishtest/flint.lnt @@ -32,3 +32,4 @@ -efile(451, vmods.h) -efile(451, programs.h) +-efile(451, vtc_h2_stattbl.h) diff --git a/bin/varnishtest/flint.sh b/bin/varnishtest/flint.sh index 45bbfd8..7f9c759 100755 --- a/bin/varnishtest/flint.sh +++ b/bin/varnishtest/flint.sh @@ -1,5 +1,11 @@ #!/bin/sh +if [ "x$1" = "x-ok" -a -f _.fl ] ; then + echo "Saved as reference" + mv _.fl _.fl.old + exit 0 +fi + flexelint \ -DTOP_BUILDDIR='"foo"' \ -I/usr/include \ @@ -10,4 +16,14 @@ flexelint \ ../../flint.lnt \ ../flint.lnt \ flint.lnt \ - *.c + *.c \ + 2>&1 | tee _.fl + +if [ -f _.fl.old ] ; then + diff -u _.fl.old _.fl +fi + +if [ "x$1" = "x-ok" ] ; then + echo "Saved as reference" + mv _.fl _.fl.old +fi diff --git a/bin/varnishtest/hpack.h b/bin/varnishtest/hpack.h new file mode 100644 index 0000000..feca984 --- /dev/null +++ b/bin/varnishtest/hpack.h @@ -0,0 +1,53 @@ +#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 txt { + char *ptr; + int len; + int huff; +}; + +struct hpk_hdr { + struct txt key; + struct txt value; + enum hpk_indexed t; + int 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, char *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); + +/* DEBUG */ +void dump_dyn_tbl(const struct hpk_ctx *ctx); diff --git a/bin/varnishtest/tests/README b/bin/varnishtest/tests/README index 80fd68a..971ee3d 100644 --- a/bin/varnishtest/tests/README +++ b/bin/varnishtest/tests/README @@ -29,15 +29,3 @@ Naming scheme id ~ [t] --> sTreaming tests id ~ [u] --> Unusual background processes id ~ [v] --> VCL tests: execute VRT functions - - -Private test scope ------------------- - - Test cases matching - - [id]1%04d.vtc - - are reserved for private tests. - - diff --git a/bin/varnishtest/vtc.c b/bin/varnishtest/vtc.c index 11ab41d..f3bf538 100644 --- a/bin/varnishtest/vtc.c +++ b/bin/varnishtest/vtc.c @@ -262,7 +262,7 @@ parse_string(const char *spec, const struct cmds *cmd, void *priv, { char *token_s[MAX_TOKENS], *token_e[MAX_TOKENS]; struct vsb *token_exp[MAX_TOKENS]; - char *p, *q, *f, *buf; + char *e, *p, *q, *f, *buf; int nest_brace; int tn; const struct cmds *cp; @@ -270,7 +270,9 @@ parse_string(const char *spec, const struct cmds *cmd, void *priv, AN(spec); buf = strdup(spec); AN(buf); - for (p = buf; *p != '\0'; p++) { + e = strchr(buf, '\0'); + AN(e); + for (p = buf; p < e; p++) { if (vtc_error || vtc_stop) break; /* Start of line */ @@ -298,8 +300,9 @@ parse_string(const char *spec, const struct cmds *cmd, void *priv, /* First content on line, collect tokens */ tn = 0; f = p; - while (*p != '\0') { + while (p < e) { assert(tn < MAX_TOKENS); + assert(p < e); if (*p == '\n') { /* End on NL */ break; } @@ -315,6 +318,7 @@ parse_string(const char *spec, const struct cmds *cmd, void *priv, token_s[tn] = ++p; q = p; for (; *p != '\0'; p++) { + assert(p < e); if (*p == '"') break; if (*p == '\\') { @@ -334,7 +338,7 @@ parse_string(const char *spec, const struct cmds *cmd, void *priv, } else if (*p == '{') { /* Braces */ nest_brace = 0; token_s[tn] = p + 1; - for (; *p != '\0'; p++) { + for (; p < e; p++) { if (*p == '{') nest_brace++; else if (*p == '}') { @@ -346,25 +350,26 @@ parse_string(const char *spec, const struct cmds *cmd, void *priv, token_e[tn++] = p++; } else { /* other tokens */ token_s[tn] = p; - for (; *p != '\0' && !isspace(*p); 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++) { token_exp[tn] = NULL; AN(token_e[tn]); /*lint !e771 */ *token_e[tn] = '\0'; /*lint !e771 */ - if (NULL == strstr(token_s[tn], "${")) - continue; - token_exp[tn] = macro_expand(vl, token_s[tn]); - if (vtc_error) { - return; + if (NULL != strstr(token_s[tn], "${")) { + token_exp[tn] = macro_expand(vl, token_s[tn]); + if (vtc_error) + return; + token_s[tn] = VSB_data(token_exp[tn]); + token_e[tn] = strchr(token_s[tn], '\0'); } - token_s[tn] = VSB_data(token_exp[tn]); - token_e[tn] = strchr(token_s[tn], '\0'); } for (cp = cmd; cp->name != NULL; cp++) @@ -507,13 +512,14 @@ cmd_err_shell(CMD_ARGS) VSB_destroy(&vsb); } -/* SECTION: delay delay +/* SECTION: client-server.spec.delay delay * - * This is the equivalent of ``sleep`` in shell: the command takes one argument - * that is the number of seconds (can be a float) to wait before continuing the - * test. + * Take a float as argument and sleep for that number of seconds. + */ +/* SECTION: stream.spec.delay delay + * + * Take a float as argument and sleep for that number of seconds. */ - void cmd_delay(CMD_ARGS) { diff --git a/bin/varnishtest/vtc.h b/bin/varnishtest/vtc.h index b872e89..a614cd0 100644 --- a/bin/varnishtest/vtc.h +++ b/bin/varnishtest/vtc.h @@ -80,6 +80,10 @@ void init_barrier(void); void init_server(void); int http_process(struct vtclog *vl, const char *spec, int sock, int *sfd); +int http2_process(struct vtclog *vl, const char *spec, int sock, int *sfd, + unsigned nosettings); + +char * synth_body(const char *len, int rnd); void cmd_server_genvcl(struct vsb *vsb); @@ -107,3 +111,10 @@ struct vsb *macro_expand(struct vtclog *vl, const char *text); void extmacro_def(const char *name, const char *fmt, ...) __v_printflike(2, 3); + +struct http; +extern const struct cmds http_cmds[]; +void cmd_stream(CMD_ARGS); +void start_h2(struct http *hp); +void stop_h2(struct http *hp); +void b64_settings(const struct http *hp, const char *s); diff --git a/bin/varnishtest/vtc_doc_hdr.rst b/bin/varnishtest/vtc_doc_hdr.rst new file mode 100644 index 0000000..de8541f --- /dev/null +++ b/bin/varnishtest/vtc_doc_hdr.rst @@ -0,0 +1,33 @@ +Syntax RFP for HTTP2 in varnishtest +=================================== + +This document tries to document how varnishtest would work this H/2, adapting to +the introduction of stream. The main idea is the introduction of a new command +(stream) inside client and server specification that naively maps to RFC7540. + +It provides little abstraction, allowwing great control over test scenario, +while still retaining a familiar logic. + +Here's an example of test file:: + + 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 + +.. contents:: + diff --git a/bin/varnishtest/vtc_h2_dectbl.h b/bin/varnishtest/vtc_h2_dectbl.h new file mode 100644 index 0000000..03a2d73 --- /dev/null +++ b/bin/varnishtest/vtc_h2_dectbl.h @@ -0,0 +1,1028 @@ +struct stbl; + +struct ssym { + uint8_t csm; /* bits consumed */ + uint8_t chr; /* character */ + struct stbl *nxt; /* next table */ +}; + +struct stbl { + int msk; + struct ssym syms[]; +}; + +static struct stbl byte3_pref0110 = { + 1, + { + /* idx 0 */ {1, 199, NULL}, + /* idx 1 */ {1, 207, NULL}, + } +}; + +static struct stbl byte3_pref0111 = { + 1, + { + /* idx 0 */ {1, 234, NULL}, + /* idx 1 */ {1, 235, NULL}, + } +}; + +static struct stbl byte3_pref1000 = { + 2, + { + /* idx 0 */ {2, 192, NULL}, + /* idx 1 */ {2, 193, NULL}, + /* idx 2 */ {2, 200, NULL}, + /* idx 3 */ {2, 201, NULL}, + } +}; + +static struct stbl byte3_pref1001 = { + 2, + { + /* idx 0 */ {2, 202, NULL}, + /* idx 1 */ {2, 205, NULL}, + /* idx 2 */ {2, 210, NULL}, + /* idx 3 */ {2, 213, NULL}, + } +}; + +static struct stbl byte3_pref1010 = { + 2, + { + /* idx 0 */ {2, 218, NULL}, + /* idx 1 */ {2, 219, NULL}, + /* idx 2 */ {2, 238, NULL}, + /* idx 3 */ {2, 240, NULL}, + } +}; + +static struct stbl byte3_pref1011 = { + 3, + { + /* idx 0 */ {2, 242, NULL}, + /* idx 1 */ {2, 242, NULL}, + /* idx 2 */ {2, 243, NULL}, + /* idx 3 */ {2, 243, NULL}, + /* idx 4 */ {2, 255, NULL}, + /* idx 5 */ {2, 255, NULL}, + /* idx 6 */ {3, 203, NULL}, + /* idx 7 */ {3, 204, NULL}, + } +}; + +static struct stbl byte3_pref1100 = { + 3, + { + /* idx 0 */ {3, 211, NULL}, + /* idx 1 */ {3, 212, NULL}, + /* idx 2 */ {3, 214, NULL}, + /* idx 3 */ {3, 221, NULL}, + /* idx 4 */ {3, 222, NULL}, + /* idx 5 */ {3, 223, NULL}, + /* idx 6 */ {3, 241, NULL}, + /* idx 7 */ {3, 244, NULL}, + } +}; + +static struct stbl byte3_pref1101 = { + 3, + { + /* idx 0 */ {3, 245, NULL}, + /* idx 1 */ {3, 246, NULL}, + /* idx 2 */ {3, 247, NULL}, + /* idx 3 */ {3, 248, NULL}, + /* idx 4 */ {3, 250, NULL}, + /* idx 5 */ {3, 251, NULL}, + /* idx 6 */ {3, 252, NULL}, + /* idx 7 */ {3, 253, NULL}, + } +}; + +static struct stbl byte3_pref1110 = { + 4, + { + /* idx 0 */ {3, 254, NULL}, + /* idx 1 */ {3, 254, NULL}, + /* idx 2 */ {4, 2, NULL}, + /* idx 3 */ {4, 3, NULL}, + /* idx 4 */ {4, 4, NULL}, + /* idx 5 */ {4, 5, NULL}, + /* idx 6 */ {4, 6, NULL}, + /* idx 7 */ {4, 7, NULL}, + /* idx 8 */ {4, 8, NULL}, + /* idx 9 */ {4, 11, NULL}, + /* idx 10 */ {4, 12, NULL}, + /* idx 11 */ {4, 14, NULL}, + /* idx 12 */ {4, 15, NULL}, + /* idx 13 */ {4, 16, NULL}, + /* idx 14 */ {4, 17, NULL}, + /* idx 15 */ {4, 18, NULL}, + } +}; + +static struct stbl byte3_pref1111 = { + 6, + { + /* idx 0 */ {4, 19, NULL}, + /* idx 1 */ {4, 19, NULL}, + /* idx 2 */ {4, 19, NULL}, + /* idx 3 */ {4, 19, NULL}, + /* idx 4 */ {4, 20, NULL}, + /* idx 5 */ {4, 20, NULL}, + /* idx 6 */ {4, 20, NULL}, + /* idx 7 */ {4, 20, NULL}, + /* idx 8 */ {4, 21, NULL}, + /* idx 9 */ {4, 21, NULL}, + /* idx 10 */ {4, 21, NULL}, + /* idx 11 */ {4, 21, NULL}, + /* idx 12 */ {4, 23, NULL}, + /* idx 13 */ {4, 23, NULL}, + /* idx 14 */ {4, 23, NULL}, + /* idx 15 */ {4, 23, NULL}, + /* idx 16 */ {4, 24, NULL}, + /* idx 17 */ {4, 24, NULL}, + /* idx 18 */ {4, 24, NULL}, + /* idx 19 */ {4, 24, NULL}, + /* idx 20 */ {4, 25, NULL}, + /* idx 21 */ {4, 25, NULL}, + /* idx 22 */ {4, 25, NULL}, + /* idx 23 */ {4, 25, NULL}, + /* idx 24 */ {4, 26, NULL}, + /* idx 25 */ {4, 26, NULL}, + /* idx 26 */ {4, 26, NULL}, + /* idx 27 */ {4, 26, NULL}, + /* idx 28 */ {4, 27, NULL}, + /* idx 29 */ {4, 27, NULL}, + /* idx 30 */ {4, 27, NULL}, + /* idx 31 */ {4, 27, NULL}, + /* idx 32 */ {4, 28, NULL}, + /* idx 33 */ {4, 28, NULL}, + /* idx 34 */ {4, 28, NULL}, + /* idx 35 */ {4, 28, NULL}, + /* idx 36 */ {4, 29, NULL}, + /* idx 37 */ {4, 29, NULL}, + /* idx 38 */ {4, 29, NULL}, + /* idx 39 */ {4, 29, NULL}, + /* idx 40 */ {4, 30, NULL}, + /* idx 41 */ {4, 30, NULL}, + /* idx 42 */ {4, 30, NULL}, + /* idx 43 */ {4, 30, NULL}, + /* idx 44 */ {4, 31, NULL}, + /* idx 45 */ {4, 31, NULL}, + /* idx 46 */ {4, 31, NULL}, + /* idx 47 */ {4, 31, NULL}, + /* idx 48 */ {4, 127, NULL}, + /* idx 49 */ {4, 127, NULL}, + /* idx 50 */ {4, 127, NULL}, + /* idx 51 */ {4, 127, NULL}, + /* idx 52 */ {4, 220, NULL}, + /* idx 53 */ {4, 220, NULL}, + /* idx 54 */ {4, 220, NULL}, + /* idx 55 */ {4, 220, NULL}, + /* idx 56 */ {4, 249, NULL}, + /* idx 57 */ {4, 249, NULL}, + /* idx 58 */ {4, 249, NULL}, + /* idx 59 */ {4, 249, NULL}, + /* idx 60 */ {6, 10, NULL}, + /* idx 61 */ {6, 13, NULL}, + /* idx 62 */ {6, 22, NULL}, + /* idx 63 */ {0, 0, NULL} + } +}; + +static struct stbl byte2_pref0 = { + 5, + { + /* idx 0 */ {3, 92, NULL}, /* '\' */ + /* idx 1 */ {3, 92, NULL}, /* '\' */ + /* idx 2 */ {3, 92, NULL}, /* '\' */ + /* idx 3 */ {3, 92, NULL}, /* '\' */ + /* idx 4 */ {3, 195, NULL}, /* . */ + /* idx 5 */ {3, 195, NULL}, /* . */ + /* idx 6 */ {3, 195, NULL}, /* . */ + /* idx 7 */ {3, 195, NULL}, /* . */ + /* idx 8 */ {3, 208, NULL}, /* . */ + /* idx 9 */ {3, 208, NULL}, /* . */ + /* idx 10 */ {3, 208, NULL}, /* . */ + /* idx 11 */ {3, 208, NULL}, /* . */ + /* idx 12 */ {4, 128, NULL}, /* . */ + /* idx 13 */ {4, 128, NULL}, /* . */ + /* idx 14 */ {4, 130, NULL}, /* . */ + /* idx 15 */ {4, 130, NULL}, /* . */ + /* idx 16 */ {4, 131, NULL}, /* . */ + /* idx 17 */ {4, 131, NULL}, /* . */ + /* idx 18 */ {4, 162, NULL}, /* . */ + /* idx 19 */ {4, 162, NULL}, /* . */ + /* idx 20 */ {4, 184, NULL}, /* . */ + /* idx 21 */ {4, 184, NULL}, /* . */ + /* idx 22 */ {4, 194, NULL}, /* . */ + /* idx 23 */ {4, 194, NULL}, /* . */ + /* idx 24 */ {4, 224, NULL}, /* . */ + /* idx 25 */ {4, 224, NULL}, /* . */ + /* idx 26 */ {4, 226, NULL}, /* . */ + /* idx 27 */ {4, 226, NULL}, /* . */ + /* idx 28 */ {5, 153, NULL}, /* . */ + /* idx 29 */ {5, 161, NULL}, /* . */ + /* idx 30 */ {5, 167, NULL}, /* . */ + /* idx 31 */ {5, 172, NULL} /* . */ + } +}; + +static struct stbl byte2_pref1 = { + 8, + { + /* idx 0 */ {5, 176, NULL}, /* . */ + /* idx 1 */ {5, 176, NULL}, /* . */ + /* idx 2 */ {5, 176, NULL}, /* . */ + /* idx 3 */ {5, 176, NULL}, /* . */ + /* idx 4 */ {5, 176, NULL}, /* . */ + /* idx 5 */ {5, 176, NULL}, /* . */ + /* idx 6 */ {5, 176, NULL}, /* . */ + /* idx 7 */ {5, 176, NULL}, /* . */ + /* idx 8 */ {5, 177, NULL}, /* . */ + /* idx 9 */ {5, 177, NULL}, /* . */ + /* idx 10 */ {5, 177, NULL}, /* . */ + /* idx 11 */ {5, 177, NULL}, /* . */ + /* idx 12 */ {5, 177, NULL}, /* . */ + /* idx 13 */ {5, 177, NULL}, /* . */ + /* idx 14 */ {5, 177, NULL}, /* . */ + /* idx 15 */ {5, 177, NULL}, /* . */ + /* idx 16 */ {5, 179, NULL}, /* */ + /* idx 17 */ {5, 179, NULL}, /* */ + /* idx 18 */ {5, 179, NULL}, /* */ + /* idx 19 */ {5, 179, NULL}, /* */ + /* idx 20 */ {5, 179, NULL}, /* */ + /* idx 21 */ {5, 179, NULL}, /* */ + /* idx 22 */ {5, 179, NULL}, /* */ + /* idx 23 */ {5, 179, NULL}, /* */ + /* idx 24 */ {5, 209, NULL}, /* . */ + /* idx 25 */ {5, 209, NULL}, /* . */ + /* idx 26 */ {5, 209, NULL}, /* . */ + /* idx 27 */ {5, 209, NULL}, /* . */ + /* idx 28 */ {5, 209, NULL}, /* . */ + /* idx 29 */ {5, 209, NULL}, /* . */ + /* idx 30 */ {5, 209, NULL}, /* . */ + /* idx 31 */ {5, 209, NULL}, /* . */ + /* idx 32 */ {5, 216, NULL}, /* . */ + /* idx 33 */ {5, 216, NULL}, /* . */ + /* idx 34 */ {5, 216, NULL}, /* . */ + /* idx 35 */ {5, 216, NULL}, /* . */ + /* idx 36 */ {5, 216, NULL}, /* . */ + /* idx 37 */ {5, 216, NULL}, /* . */ + /* idx 38 */ {5, 216, NULL}, /* . */ + /* idx 39 */ {5, 216, NULL}, /* . */ + /* idx 40 */ {5, 217, NULL}, /* . */ + /* idx 41 */ {5, 217, NULL}, /* . */ + /* idx 42 */ {5, 217, NULL}, /* . */ + /* idx 43 */ {5, 217, NULL}, /* . */ + /* idx 44 */ {5, 217, NULL}, /* . */ + /* idx 45 */ {5, 217, NULL}, /* . */ + /* idx 46 */ {5, 217, NULL}, /* . */ + /* idx 47 */ {5, 217, NULL}, /* . */ + /* idx 48 */ {5, 227, NULL}, /* . */ + /* idx 49 */ {5, 227, NULL}, /* . */ + /* idx 50 */ {5, 227, NULL}, /* . */ + /* idx 51 */ {5, 227, NULL}, /* . */ + /* idx 52 */ {5, 227, NULL}, /* . */ + /* idx 53 */ {5, 227, NULL}, /* . */ + /* idx 54 */ {5, 227, NULL}, /* . */ + /* idx 55 */ {5, 227, NULL}, /* . */ + /* idx 56 */ {5, 229, NULL}, /* . */ + /* idx 57 */ {5, 229, NULL}, /* . */ + /* idx 58 */ {5, 229, NULL}, /* . */ + /* idx 59 */ {5, 229, NULL}, /* . */ + /* idx 60 */ {5, 229, NULL}, /* . */ + /* idx 61 */ {5, 229, NULL}, /* . */ + /* idx 62 */ {5, 229, NULL}, /* . */ + /* idx 63 */ {5, 229, NULL}, /* . */ + /* idx 64 */ {5, 230, NULL}, /* . */ + /* idx 65 */ {5, 230, NULL}, /* . */ + /* idx 66 */ {5, 230, NULL}, /* . */ + /* idx 67 */ {5, 230, NULL}, /* . */ + /* idx 68 */ {5, 230, NULL}, /* . */ + /* idx 69 */ {5, 230, NULL}, /* . */ + /* idx 70 */ {5, 230, NULL}, /* . */ + /* idx 71 */ {5, 230, NULL}, /* . */ + /* idx 72 */ {6, 129, NULL}, /* . */ + /* idx 73 */ {6, 129, NULL}, /* . */ + /* idx 74 */ {6, 129, NULL}, /* . */ + /* idx 75 */ {6, 129, NULL}, /* . */ + /* idx 76 */ {6, 132, NULL}, /* . */ + /* idx 77 */ {6, 132, NULL}, /* . */ + /* idx 78 */ {6, 132, NULL}, /* . */ + /* idx 79 */ {6, 132, NULL}, /* . */ + /* idx 80 */ {6, 133, NULL}, /* . */ + /* idx 81 */ {6, 133, NULL}, /* . */ + /* idx 82 */ {6, 133, NULL}, /* . */ + /* idx 83 */ {6, 133, NULL}, /* . */ + /* idx 84 */ {6, 134, NULL}, /* . */ + /* idx 85 */ {6, 134, NULL}, /* . */ + /* idx 86 */ {6, 134, NULL}, /* . */ + /* idx 87 */ {6, 134, NULL}, /* . */ + /* idx 88 */ {6, 136, NULL}, /* . */ + /* idx 89 */ {6, 136, NULL}, /* . */ + /* idx 90 */ {6, 136, NULL}, /* . */ + /* idx 91 */ {6, 136, NULL}, /* . */ + /* idx 92 */ {6, 146, NULL}, /* . */ + /* idx 93 */ {6, 146, NULL}, /* . */ + /* idx 94 */ {6, 146, NULL}, /* . */ + /* idx 95 */ {6, 146, NULL}, /* . */ + /* idx 96 */ {6, 154, NULL}, /* . */ + /* idx 97 */ {6, 154, NULL}, /* . */ + /* idx 98 */ {6, 154, NULL}, /* . */ + /* idx 99 */ {6, 154, NULL}, /* . */ + /* idx 100 */ {6, 156, NULL}, /* . */ + /* idx 101 */ {6, 156, NULL}, /* . */ + /* idx 102 */ {6, 156, NULL}, /* . */ + /* idx 103 */ {6, 156, NULL}, /* . */ + /* idx 104 */ {6, 160, NULL}, /* . */ + /* idx 105 */ {6, 160, NULL}, /* . */ + /* idx 106 */ {6, 160, NULL}, /* . */ + /* idx 107 */ {6, 160, NULL}, /* . */ + /* idx 108 */ {6, 163, NULL}, /* . */ + /* idx 109 */ {6, 163, NULL}, /* . */ + /* idx 110 */ {6, 163, NULL}, /* . */ + /* idx 111 */ {6, 163, NULL}, /* . */ + /* idx 112 */ {6, 164, NULL}, /* . */ + /* idx 113 */ {6, 164, NULL}, /* . */ + /* idx 114 */ {6, 164, NULL}, /* . */ + /* idx 115 */ {6, 164, NULL}, /* . */ + /* idx 116 */ {6, 169, NULL}, /* . */ + /* idx 117 */ {6, 169, NULL}, /* . */ + /* idx 118 */ {6, 169, NULL}, /* . */ + /* idx 119 */ {6, 169, NULL}, /* . */ + /* idx 120 */ {6, 170, NULL}, /* . */ + /* idx 121 */ {6, 170, NULL}, /* . */ + /* idx 122 */ {6, 170, NULL}, /* . */ + /* idx 123 */ {6, 170, NULL}, /* . */ + /* idx 124 */ {6, 173, NULL}, /* . */ + /* idx 125 */ {6, 173, NULL}, /* . */ + /* idx 126 */ {6, 173, NULL}, /* . */ + /* idx 127 */ {6, 173, NULL}, /* . */ + /* idx 128 */ {6, 176, NULL}, /* . */ + /* idx 129 */ {6, 176, NULL}, /* . */ + /* idx 130 */ {6, 176, NULL}, /* . */ + /* idx 131 */ {6, 176, NULL}, /* . */ + /* idx 132 */ {6, 181, NULL}, /* . */ + /* idx 133 */ {6, 181, NULL}, /* . */ + /* idx 134 */ {6, 181, NULL}, /* . */ + /* idx 135 */ {6, 181, NULL}, /* . */ + /* idx 136 */ {6, 185, NULL}, /* . */ + /* idx 137 */ {6, 185, NULL}, /* . */ + /* idx 138 */ {6, 185, NULL}, /* . */ + /* idx 139 */ {6, 185, NULL}, /* . */ + /* idx 140 */ {6, 186, NULL}, /* . */ + /* idx 141 */ {6, 186, NULL}, /* . */ + /* idx 142 */ {6, 186, NULL}, /* . */ + /* idx 143 */ {6, 186, NULL}, /* . */ + /* idx 144 */ {6, 187, NULL}, /* . */ + /* idx 145 */ {6, 187, NULL}, /* . */ + /* idx 146 */ {6, 187, NULL}, /* . */ + /* idx 147 */ {6, 187, NULL}, /* . */ + /* idx 148 */ {6, 189, NULL}, /* . */ + /* idx 149 */ {6, 189, NULL}, /* . */ + /* idx 150 */ {6, 189, NULL}, /* . */ + /* idx 151 */ {6, 189, NULL}, /* . */ + /* idx 152 */ {6, 190, NULL}, /* . */ + /* idx 153 */ {6, 190, NULL}, /* . */ + /* idx 154 */ {6, 190, NULL}, /* . */ + /* idx 155 */ {6, 190, NULL}, /* . */ + /* idx 156 */ {6, 196, NULL}, /* . */ + /* idx 157 */ {6, 196, NULL}, /* . */ + /* idx 158 */ {6, 196, NULL}, /* . */ + /* idx 159 */ {6, 196, NULL}, /* . */ + /* idx 160 */ {6, 198, NULL}, /* . */ + /* idx 161 */ {6, 198, NULL}, /* . */ + /* idx 162 */ {6, 198, NULL}, /* . */ + /* idx 163 */ {6, 198, NULL}, /* . */ + /* idx 164 */ {6, 228, NULL}, /* . */ + /* idx 165 */ {6, 228, NULL}, /* . */ + /* idx 166 */ {6, 228, NULL}, /* . */ + /* idx 167 */ {6, 228, NULL}, /* . */ + /* idx 168 */ {6, 232, NULL}, /* . */ + /* idx 169 */ {6, 232, NULL}, /* . */ + /* idx 170 */ {6, 232, NULL}, /* . */ + /* idx 171 */ {6, 232, NULL}, /* . */ + /* idx 172 */ {6, 233, NULL}, /* . */ + /* idx 173 */ {6, 233, NULL}, /* . */ + /* idx 174 */ {6, 233, NULL}, /* . */ + /* idx 175 */ {6, 233, NULL}, /* . */ + /* idx 176 */ {7, 1, NULL}, + /* idx 177 */ {7, 1, NULL}, + /* idx 178 */ {7, 135, NULL}, + /* idx 179 */ {7, 135, NULL}, + /* idx 180 */ {7, 137, NULL}, + /* idx 181 */ {7, 137, NULL}, + /* idx 182 */ {7, 138, NULL}, + /* idx 183 */ {7, 138, NULL}, + /* idx 184 */ {7, 139, NULL}, + /* idx 185 */ {7, 139, NULL}, + /* idx 186 */ {7, 140, NULL}, + /* idx 187 */ {7, 140, NULL}, + /* idx 188 */ {7, 141, NULL}, + /* idx 189 */ {7, 141, NULL}, + /* idx 190 */ {7, 143, NULL}, + /* idx 191 */ {7, 143, NULL}, + /* idx 192 */ {7, 147, NULL}, + /* idx 193 */ {7, 147, NULL}, + /* idx 194 */ {7, 149, NULL}, + /* idx 195 */ {7, 149, NULL}, + /* idx 196 */ {7, 150, NULL}, + /* idx 197 */ {7, 150, NULL}, + /* idx 198 */ {7, 151, NULL}, + /* idx 199 */ {7, 151, NULL}, + /* idx 200 */ {7, 152, NULL}, + /* idx 201 */ {7, 152, NULL}, + /* idx 202 */ {7, 155, NULL}, + /* idx 203 */ {7, 155, NULL}, + /* idx 204 */ {7, 157, NULL}, + /* idx 205 */ {7, 157, NULL}, + /* idx 206 */ {7, 158, NULL}, + /* idx 207 */ {7, 158, NULL}, + /* idx 208 */ {7, 165, NULL}, + /* idx 209 */ {7, 165, NULL}, + /* idx 210 */ {7, 166, NULL}, + /* idx 211 */ {7, 166, NULL}, + /* idx 212 */ {7, 168, NULL}, + /* idx 213 */ {7, 168, NULL}, + /* idx 214 */ {7, 174, NULL}, + /* idx 215 */ {7, 174, NULL}, + /* idx 216 */ {7, 175, NULL}, + /* idx 217 */ {7, 175, NULL}, + /* idx 218 */ {7, 180, NULL}, + /* idx 219 */ {7, 180, NULL}, + /* idx 220 */ {7, 182, NULL}, + /* idx 221 */ {7, 182, NULL}, + /* idx 222 */ {7, 183, NULL}, + /* idx 223 */ {7, 183, NULL}, + /* idx 224 */ {7, 188, NULL}, + /* idx 225 */ {7, 188, NULL}, + /* idx 226 */ {7, 191, NULL}, + /* idx 227 */ {7, 191, NULL}, + /* idx 228 */ {7, 197, NULL}, + /* idx 229 */ {7, 197, NULL}, + /* idx 230 */ {7, 231, NULL}, + /* idx 231 */ {7, 231, NULL}, + /* idx 232 */ {7, 239, NULL}, + /* idx 233 */ {7, 239, NULL}, + /* idx 234 */ {8, 9, NULL}, + /* idx 235 */ {8, 142, NULL}, + /* idx 236 */ {8, 144, NULL}, + /* idx 237 */ {8, 145, NULL}, + /* idx 238 */ {8, 148, NULL}, + /* idx 239 */ {8, 159, NULL}, + /* idx 240 */ {8, 171, NULL}, + /* idx 241 */ {8, 206, NULL}, + /* idx 242 */ {8, 215, NULL}, + /* idx 243 */ {8, 225, NULL}, + /* idx 244 */ {8, 236, NULL}, + /* idx 245 */ {8, 237, NULL}, + /* idx 246 */ {8, 0, &byte3_pref0110 }, + /* idx 247 */ {8, 0, &byte3_pref0111 }, + /* idx 248 */ {8, 0, &byte3_pref1000 }, + /* idx 249 */ {8, 0, &byte3_pref1001 }, + /* idx 250 */ {8, 0, &byte3_pref1010 }, + /* idx 251 */ {8, 0, &byte3_pref1011 }, + /* idx 252 */ {8, 0, &byte3_pref1100 }, + /* idx 253 */ {8, 0, &byte3_pref1101 }, + /* idx 254 */ {8, 0, &byte3_pref1110 }, + /* idx 255 */ {8, 0, &byte3_pref1111 } + } +}; + + +static struct stbl byte1_pref0 = { + 2, + { + /* idx 0 */ {2, 33, NULL}, /* '!' */ + /* idx 1 */ {2, 34, NULL}, /* '"' */ + /* idx 2 */ {2, 40, NULL}, /* '(' */ + /* idx 3 */ {2, 41, NULL}, /* ')' */ + } +}; + +static struct stbl byte1_pref1 = { + 8, + { + /* idx 0 */ {2, 63, NULL}, /* '?' */ + /* idx 1 */ {2, 63, NULL}, /* '?' */ + /* idx 2 */ {2, 63, NULL}, /* '?' */ + /* idx 3 */ {2, 63, NULL}, /* '?' */ + /* idx 4 */ {2, 63, NULL}, /* '?' */ + /* idx 5 */ {2, 63, NULL}, /* '?' */ + /* idx 6 */ {2, 63, NULL}, /* '?' */ + /* idx 7 */ {2, 63, NULL}, /* '?' */ + /* idx 8 */ {2, 63, NULL}, /* '?' */ + /* idx 9 */ {2, 63, NULL}, /* '?' */ + /* idx 10 */ {2, 63, NULL}, /* '?' */ + /* idx 11 */ {2, 63, NULL}, /* '?' */ + /* idx 12 */ {2, 63, NULL}, /* '?' */ + /* idx 13 */ {2, 63, NULL}, /* '?' */ + /* idx 14 */ {2, 63, NULL}, /* '?' */ + /* idx 15 */ {2, 63, NULL}, /* '?' */ + /* idx 16 */ {2, 63, NULL}, /* '?' */ + /* idx 17 */ {2, 63, NULL}, /* '?' */ + /* idx 18 */ {2, 63, NULL}, /* '?' */ + /* idx 19 */ {2, 63, NULL}, /* '?' */ + /* idx 20 */ {2, 63, NULL}, /* '?' */ + /* idx 21 */ {2, 63, NULL}, /* '?' */ + /* idx 22 */ {2, 63, NULL}, /* '?' */ + /* idx 23 */ {2, 63, NULL}, /* '?' */ + /* idx 24 */ {2, 63, NULL}, /* '?' */ + /* idx 25 */ {2, 63, NULL}, /* '?' */ + /* idx 26 */ {2, 63, NULL}, /* '?' */ + /* idx 27 */ {2, 63, NULL}, /* '?' */ + /* idx 28 */ {2, 63, NULL}, /* '?' */ + /* idx 29 */ {2, 63, NULL}, /* '?' */ + /* idx 30 */ {2, 63, NULL}, /* '?' */ + /* idx 31 */ {2, 63, NULL}, /* '?' */ + /* idx 32 */ {2, 63, NULL}, /* '?' */ + /* idx 33 */ {2, 63, NULL}, /* '?' */ + /* idx 34 */ {2, 63, NULL}, /* '?' */ + /* idx 35 */ {2, 63, NULL}, /* '?' */ + /* idx 36 */ {2, 63, NULL}, /* '?' */ + /* idx 37 */ {2, 63, NULL}, /* '?' */ + /* idx 38 */ {2, 63, NULL}, /* '?' */ + /* idx 39 */ {2, 63, NULL}, /* '?' */ + /* idx 40 */ {2, 63, NULL}, /* '?' */ + /* idx 41 */ {2, 63, NULL}, /* '?' */ + /* idx 42 */ {2, 63, NULL}, /* '?' */ + /* idx 43 */ {2, 63, NULL}, /* '?' */ + /* idx 44 */ {2, 63, NULL}, /* '?' */ + /* idx 45 */ {2, 63, NULL}, /* '?' */ + /* idx 46 */ {2, 63, NULL}, /* '?' */ + /* idx 47 */ {2, 63, NULL}, /* '?' */ + /* idx 48 */ {2, 63, NULL}, /* '?' */ + /* idx 49 */ {2, 63, NULL}, /* '?' */ + /* idx 50 */ {2, 63, NULL}, /* '?' */ + /* idx 51 */ {2, 63, NULL}, /* '?' */ + /* idx 52 */ {2, 63, NULL}, /* '?' */ + /* idx 53 */ {2, 63, NULL}, /* '?' */ + /* idx 54 */ {2, 63, NULL}, /* '?' */ + /* idx 55 */ {2, 63, NULL}, /* '?' */ + /* idx 56 */ {2, 63, NULL}, /* '?' */ + /* idx 57 */ {2, 63, NULL}, /* '?' */ + /* idx 58 */ {2, 63, NULL}, /* '?' */ + /* idx 59 */ {2, 63, NULL}, /* '?' */ + /* idx 60 */ {2, 63, NULL}, /* '?' */ + /* idx 61 */ {2, 63, NULL}, /* '?' */ + /* idx 62 */ {2, 63, NULL}, /* '?' */ + /* idx 63 */ {2, 63, NULL}, /* '?' */ + /* idx 64 */ {3, 39, NULL}, /* ''' */ + /* idx 65 */ {3, 39, NULL}, /* ''' */ + /* idx 66 */ {3, 39, NULL}, /* ''' */ + /* idx 67 */ {3, 39, NULL}, /* ''' */ + /* idx 68 */ {3, 39, NULL}, /* ''' */ + /* idx 69 */ {3, 39, NULL}, /* ''' */ + /* idx 70 */ {3, 39, NULL}, /* ''' */ + /* idx 71 */ {3, 39, NULL}, /* ''' */ + /* idx 72 */ {3, 39, NULL}, /* ''' */ + /* idx 73 */ {3, 39, NULL}, /* ''' */ + /* idx 74 */ {3, 39, NULL}, /* ''' */ + /* idx 75 */ {3, 39, NULL}, /* ''' */ + /* idx 76 */ {3, 39, NULL}, /* ''' */ + /* idx 77 */ {3, 39, NULL}, /* ''' */ + /* idx 78 */ {3, 39, NULL}, /* ''' */ + /* idx 79 */ {3, 39, NULL}, /* ''' */ + /* idx 80 */ {3, 39, NULL}, /* ''' */ + /* idx 81 */ {3, 39, NULL}, /* ''' */ + /* idx 82 */ {3, 39, NULL}, /* ''' */ + /* idx 83 */ {3, 39, NULL}, /* ''' */ + /* idx 84 */ {3, 39, NULL}, /* ''' */ + /* idx 85 */ {3, 39, NULL}, /* ''' */ + /* idx 86 */ {3, 39, NULL}, /* ''' */ + /* idx 87 */ {3, 39, NULL}, /* ''' */ + /* idx 88 */ {3, 39, NULL}, /* ''' */ + /* idx 89 */ {3, 39, NULL}, /* ''' */ + /* idx 90 */ {3, 39, NULL}, /* ''' */ + /* idx 91 */ {3, 39, NULL}, /* ''' */ + /* idx 92 */ {3, 39, NULL}, /* ''' */ + /* idx 93 */ {3, 39, NULL}, /* ''' */ + /* idx 94 */ {3, 39, NULL}, /* ''' */ + /* idx 95 */ {3, 39, NULL}, /* ''' */ + /* idx 96 */ {3, 43, NULL}, /* '+' */ + /* idx 97 */ {3, 43, NULL}, /* '+' */ + /* idx 98 */ {3, 43, NULL}, /* '+' */ + /* idx 99 */ {3, 43, NULL}, /* '+' */ + /* idx 100 */ {3, 43, NULL}, /* '+' */ + /* idx 101 */ {3, 43, NULL}, /* '+' */ + /* idx 102 */ {3, 43, NULL}, /* '+' */ + /* idx 103 */ {3, 43, NULL}, /* '+' */ + /* idx 104 */ {3, 43, NULL}, /* '+' */ + /* idx 105 */ {3, 43, NULL}, /* '+' */ + /* idx 106 */ {3, 43, NULL}, /* '+' */ + /* idx 107 */ {3, 43, NULL}, /* '+' */ + /* idx 108 */ {3, 43, NULL}, /* '+' */ + /* idx 109 */ {3, 43, NULL}, /* '+' */ + /* idx 110 */ {3, 43, NULL}, /* '+' */ + /* idx 111 */ {3, 43, NULL}, /* '+' */ + /* idx 112 */ {3, 43, NULL}, /* '+' */ + /* idx 113 */ {3, 43, NULL}, /* '+' */ + /* idx 114 */ {3, 43, NULL}, /* '+' */ + /* idx 115 */ {3, 43, NULL}, /* '+' */ + /* idx 116 */ {3, 43, NULL}, /* '+' */ + /* idx 117 */ {3, 43, NULL}, /* '+' */ + /* idx 118 */ {3, 43, NULL}, /* '+' */ + /* idx 119 */ {3, 43, NULL}, /* '+' */ + /* idx 120 */ {3, 43, NULL}, /* '+' */ + /* idx 121 */ {3, 43, NULL}, /* '+' */ + /* idx 122 */ {3, 43, NULL}, /* '+' */ + /* idx 123 */ {3, 43, NULL}, /* '+' */ + /* idx 124 */ {3, 43, NULL}, /* '+' */ + /* idx 125 */ {3, 43, NULL}, /* '+' */ + /* idx 126 */ {3, 43, NULL}, /* '+' */ + /* idx 127 */ {3, 43, NULL}, /* '+' */ + /* idx 128 */ {3, 124, NULL}, /* '|' */ + /* idx 129 */ {3, 124, NULL}, /* '|' */ + /* idx 130 */ {3, 124, NULL}, /* '|' */ + /* idx 131 */ {3, 124, NULL}, /* '|' */ + /* idx 132 */ {3, 124, NULL}, /* '|' */ + /* idx 133 */ {3, 124, NULL}, /* '|' */ + /* idx 134 */ {3, 124, NULL}, /* '|' */ + /* idx 135 */ {3, 124, NULL}, /* '|' */ + /* idx 136 */ {3, 124, NULL}, /* '|' */ + /* idx 137 */ {3, 124, NULL}, /* '|' */ + /* idx 138 */ {3, 124, NULL}, /* '|' */ + /* idx 139 */ {3, 124, NULL}, /* '|' */ + /* idx 140 */ {3, 124, NULL}, /* '|' */ + /* idx 141 */ {3, 124, NULL}, /* '|' */ + /* idx 142 */ {3, 124, NULL}, /* '|' */ + /* idx 143 */ {3, 124, NULL}, /* '|' */ + /* idx 144 */ {3, 124, NULL}, /* '|' */ + /* idx 145 */ {3, 124, NULL}, /* '|' */ + /* idx 146 */ {3, 124, NULL}, /* '|' */ + /* idx 147 */ {3, 124, NULL}, /* '|' */ + /* idx 148 */ {3, 124, NULL}, /* '|' */ + /* idx 149 */ {3, 124, NULL}, /* '|' */ + /* idx 150 */ {3, 124, NULL}, /* '|' */ + /* idx 151 */ {3, 124, NULL}, /* '|' */ + /* idx 152 */ {3, 124, NULL}, /* '|' */ + /* idx 153 */ {3, 124, NULL}, /* '|' */ + /* idx 154 */ {3, 124, NULL}, /* '|' */ + /* idx 155 */ {3, 124, NULL}, /* '|' */ + /* idx 156 */ {3, 124, NULL}, /* '|' */ + /* idx 157 */ {3, 124, NULL}, /* '|' */ + /* idx 158 */ {3, 124, NULL}, /* '|' */ + /* idx 159 */ {3, 124, NULL}, /* '|' */ + /* idx 160 */ {4, 35, NULL}, /* '#' */ + /* idx 161 */ {4, 35, NULL}, /* '#' */ + /* idx 162 */ {4, 35, NULL}, /* '#' */ + /* idx 163 */ {4, 35, NULL}, /* '#' */ + /* idx 164 */ {4, 35, NULL}, /* '#' */ + /* idx 165 */ {4, 35, NULL}, /* '#' */ + /* idx 166 */ {4, 35, NULL}, /* '#' */ + /* idx 167 */ {4, 35, NULL}, /* '#' */ + /* idx 168 */ {4, 35, NULL}, /* '#' */ + /* idx 169 */ {4, 35, NULL}, /* '#' */ + /* idx 170 */ {4, 35, NULL}, /* '#' */ + /* idx 171 */ {4, 35, NULL}, /* '#' */ + /* idx 172 */ {4, 35, NULL}, /* '#' */ + /* idx 173 */ {4, 35, NULL}, /* '#' */ + /* idx 174 */ {4, 35, NULL}, /* '#' */ + /* idx 175 */ {4, 35, NULL}, /* '#' */ + /* idx 176 */ {4, 62, NULL}, /* '>' */ + /* idx 177 */ {4, 62, NULL}, /* '>' */ + /* idx 178 */ {4, 62, NULL}, /* '>' */ + /* idx 179 */ {4, 62, NULL}, /* '>' */ + /* idx 180 */ {4, 62, NULL}, /* '>' */ + /* idx 181 */ {4, 62, NULL}, /* '>' */ + /* idx 182 */ {4, 62, NULL}, /* '>' */ + /* idx 183 */ {4, 62, NULL}, /* '>' */ + /* idx 184 */ {4, 62, NULL}, /* '>' */ + /* idx 185 */ {4, 62, NULL}, /* '>' */ + /* idx 186 */ {4, 62, NULL}, /* '>' */ + /* idx 187 */ {4, 62, NULL}, /* '>' */ + /* idx 188 */ {4, 62, NULL}, /* '>' */ + /* idx 189 */ {4, 62, NULL}, /* '>' */ + /* idx 190 */ {4, 62, NULL}, /* '>' */ + /* idx 191 */ {4, 62, NULL}, /* '>' */ + /* idx 192 */ {5, 0, NULL}, /* . */ + /* idx 193 */ {5, 0, NULL}, /* . */ + /* idx 194 */ {5, 0, NULL}, /* . */ + /* idx 195 */ {5, 0, NULL}, /* . */ + /* idx 196 */ {5, 0, NULL}, /* . */ + /* idx 197 */ {5, 0, NULL}, /* . */ + /* idx 198 */ {5, 0, NULL}, /* . */ + /* idx 199 */ {5, 0, NULL}, /* . */ + /* idx 200 */ {5, 36, NULL}, /* $ */ + /* idx 201 */ {5, 36, NULL}, /* $ */ + /* idx 202 */ {5, 36, NULL}, /* $ */ + /* idx 203 */ {5, 36, NULL}, /* $ */ + /* idx 204 */ {5, 36, NULL}, /* $ */ + /* idx 205 */ {5, 36, NULL}, /* $ */ + /* idx 206 */ {5, 36, NULL}, /* $ */ + /* idx 207 */ {5, 36, NULL}, /* $ */ + /* idx 208 */ {5, 64, NULL}, /* '@' */ + /* idx 209 */ {5, 64, NULL}, /* '@' */ + /* idx 210 */ {5, 64, NULL}, /* '@' */ + /* idx 211 */ {5, 64, NULL}, /* '@' */ + /* idx 212 */ {5, 64, NULL}, /* '@' */ + /* idx 213 */ {5, 64, NULL}, /* '@' */ + /* idx 214 */ {5, 64, NULL}, /* '@' */ + /* idx 215 */ {5, 64, NULL}, /* '@' */ + /* idx 216 */ {5, 91, NULL}, /* '[' */ + /* idx 217 */ {5, 91, NULL}, /* '[' */ + /* idx 218 */ {5, 91, NULL}, /* '[' */ + /* idx 219 */ {5, 91, NULL}, /* '[' */ + /* idx 220 */ {5, 91, NULL}, /* '[' */ + /* idx 221 */ {5, 91, NULL}, /* '[' */ + /* idx 222 */ {5, 91, NULL}, /* '[' */ + /* idx 223 */ {5, 91, NULL}, /* '[' */ + /* idx 224 */ {5, 93, NULL}, /* ']' */ + /* idx 225 */ {5, 93, NULL}, /* ']' */ + /* idx 226 */ {5, 93, NULL}, /* ']' */ + /* idx 227 */ {5, 93, NULL}, /* ']' */ + /* idx 228 */ {5, 93, NULL}, /* ']' */ + /* idx 229 */ {5, 93, NULL}, /* ']' */ + /* idx 230 */ {5, 93, NULL}, /* ']' */ + /* idx 231 */ {5, 93, NULL}, /* ']' */ + /* idx 232 */ {5, 126, NULL}, /* '~' */ + /* idx 233 */ {5, 126, NULL}, /* '~' */ + /* idx 234 */ {5, 126, NULL}, /* '~' */ + /* idx 235 */ {5, 126, NULL}, /* '~' */ + /* idx 236 */ {5, 126, NULL}, /* '~' */ + /* idx 237 */ {5, 126, NULL}, /* '~' */ + /* idx 238 */ {5, 126, NULL}, /* '~' */ + /* idx 239 */ {5, 126, NULL}, /* '~' */ + /* idx 240 */ {6, 94, NULL}, /* '^' */ + /* idx 241 */ {6, 94, NULL}, /* '^' */ + /* idx 242 */ {6, 94, NULL}, /* '^' */ + /* idx 243 */ {6, 94, NULL}, /* '^' */ + /* idx 244 */ {6, 125, NULL}, /* '}' */ + /* idx 245 */ {6, 125, NULL}, /* '}' */ + /* idx 246 */ {6, 125, NULL}, /* '}' */ + /* idx 247 */ {6, 125, NULL}, /* '}' */ + /* idx 248 */ {7, 60, NULL}, /* '<' */ + /* idx 249 */ {7, 60, NULL}, /* '<' */ + /* idx 250 */ {7, 96, NULL}, /* '`' */ + /* idx 251 */ {7, 96, NULL}, /* '`' */ + /* idx 252 */ {7, 123, NULL}, /* '{' */ + /* idx 253 */ {7, 123, NULL}, /* '{' */ + /* idx 254 */ {8, 0, &byte2_pref0}, /* escape */ + /* idx 255 */ {8, 0, &byte2_pref1} /* escape */ + } +}; + + +static struct stbl byte0 = { + 8, + { + /* idx 0 */ {5, 48, NULL}, /* '0' */ + /* idx 1 */ {5, 48, NULL}, /* '0' */ + /* idx 2 */ {5, 48, NULL}, /* '0' */ + /* idx 3 */ {5, 48, NULL}, /* '0' */ + /* idx 4 */ {5, 48, NULL}, /* '0' */ + /* idx 5 */ {5, 48, NULL}, /* '0' */ + /* idx 6 */ {5, 48, NULL}, /* '0' */ + /* idx 7 */ {5, 48, NULL}, /* '0' */ + /* idx 8 */ {5, 49, NULL}, /* '1' */ + /* idx 9 */ {5, 49, NULL}, /* '1' */ + /* idx 10 */ {5, 49, NULL}, /* '1' */ + /* idx 11 */ {5, 49, NULL}, /* '1' */ + /* idx 12 */ {5, 49, NULL}, /* '1' */ + /* idx 13 */ {5, 49, NULL}, /* '1' */ + /* idx 14 */ {5, 49, NULL}, /* '1' */ + /* idx 15 */ {5, 49, NULL}, /* '1' */ + /* idx 16 */ {5, 50, NULL}, /* '2' */ + /* idx 17 */ {5, 50, NULL}, /* '2' */ + /* idx 18 */ {5, 50, NULL}, /* '2' */ + /* idx 19 */ {5, 50, NULL}, /* '2' */ + /* idx 20 */ {5, 50, NULL}, /* '2' */ + /* idx 21 */ {5, 50, NULL}, /* '2' */ + /* idx 22 */ {5, 50, NULL}, /* '2' */ + /* idx 23 */ {5, 50, NULL}, /* '2' */ + /* idx 24 */ {5, 97, NULL}, /* 'a' */ + /* idx 25 */ {5, 97, NULL}, /* 'a' */ + /* idx 26 */ {5, 97, NULL}, /* 'a' */ + /* idx 27 */ {5, 97, NULL}, /* 'a' */ + /* idx 28 */ {5, 97, NULL}, /* 'a' */ + /* idx 29 */ {5, 97, NULL}, /* 'a' */ + /* idx 30 */ {5, 97, NULL}, /* 'a' */ + /* idx 31 */ {5, 97, NULL}, /* 'a' */ + /* idx 32 */ {5, 99, NULL}, /* 'c' */ + /* idx 33 */ {5, 99, NULL}, /* 'c' */ + /* idx 34 */ {5, 99, NULL}, /* 'c' */ + /* idx 35 */ {5, 99, NULL}, /* 'c' */ + /* idx 36 */ {5, 99, NULL}, /* 'c' */ + /* idx 37 */ {5, 99, NULL}, /* 'c' */ + /* idx 38 */ {5, 99, NULL}, /* 'c' */ + /* idx 39 */ {5, 99, NULL}, /* 'c' */ + /* idx 40 */ {5, 101, NULL}, /* 'e' */ + /* idx 41 */ {5, 101, NULL}, /* 'e' */ + /* idx 42 */ {5, 101, NULL}, /* 'e' */ + /* idx 43 */ {5, 101, NULL}, /* 'e' */ + /* idx 44 */ {5, 101, NULL}, /* 'e' */ + /* idx 45 */ {5, 101, NULL}, /* 'e' */ + /* idx 46 */ {5, 101, NULL}, /* 'e' */ + /* idx 47 */ {5, 101, NULL}, /* 'e' */ + /* idx 48 */ {5, 105, NULL}, /* 'i' */ + /* idx 49 */ {5, 105, NULL}, /* 'i' */ + /* idx 50 */ {5, 105, NULL}, /* 'i' */ + /* idx 51 */ {5, 105, NULL}, /* 'i' */ + /* idx 52 */ {5, 105, NULL}, /* 'i' */ + /* idx 53 */ {5, 105, NULL}, /* 'i' */ + /* idx 54 */ {5, 105, NULL}, /* 'i' */ + /* idx 55 */ {5, 105, NULL}, /* 'i' */ + /* idx 56 */ {5, 111, NULL}, /* 'o' */ + /* idx 57 */ {5, 111, NULL}, /* 'o' */ + /* idx 58 */ {5, 111, NULL}, /* 'o' */ + /* idx 59 */ {5, 111, NULL}, /* 'o' */ + /* idx 60 */ {5, 111, NULL}, /* 'o' */ + /* idx 61 */ {5, 111, NULL}, /* 'o' */ + /* idx 62 */ {5, 111, NULL}, /* 'o' */ + /* idx 63 */ {5, 111, NULL}, /* 'o' */ + /* idx 64 */ {5, 115, NULL}, /* 's' */ + /* idx 65 */ {5, 115, NULL}, /* 's' */ + /* idx 66 */ {5, 115, NULL}, /* 's' */ + /* idx 67 */ {5, 115, NULL}, /* 's' */ + /* idx 68 */ {5, 115, NULL}, /* 's' */ + /* idx 69 */ {5, 115, NULL}, /* 's' */ + /* idx 70 */ {5, 115, NULL}, /* 's' */ + /* idx 71 */ {5, 115, NULL}, /* 's' */ + /* idx 72 */ {5, 116, NULL}, /* 't' */ + /* idx 73 */ {5, 116, NULL}, /* 't' */ + /* idx 74 */ {5, 116, NULL}, /* 't' */ + /* idx 75 */ {5, 116, NULL}, /* 't' */ + /* idx 76 */ {5, 116, NULL}, /* 't' */ + /* idx 77 */ {5, 116, NULL}, /* 't' */ + /* idx 78 */ {5, 116, NULL}, /* 't' */ + /* idx 79 */ {5, 116, NULL}, /* 't' */ + /* idx 80 */ {6, 32, NULL}, /* tab */ + /* idx 81 */ {6, 32, NULL}, /* tab */ + /* idx 82 */ {6, 32, NULL}, /* tab */ + /* idx 83 */ {6, 32, NULL}, /* tab */ + /* idx 84 */ {6, 37, NULL}, /* '%' */ + /* idx 85 */ {6, 37, NULL}, /* '%' */ + /* idx 86 */ {6, 37, NULL}, /* '%' */ + /* idx 87 */ {6, 37, NULL}, /* '%' */ + /* idx 88 */ {6, 45, NULL}, /* '-' */ + /* idx 89 */ {6, 45, NULL}, /* '-' */ + /* idx 90 */ {6, 45, NULL}, /* '-' */ + /* idx 91 */ {6, 45, NULL}, /* '-' */ + /* idx 92 */ {6, 46, NULL}, /* '.' */ + /* idx 93 */ {6, 46, NULL}, /* '.' */ + /* idx 94 */ {6, 46, NULL}, /* '.' */ + /* idx 95 */ {6, 46, NULL}, /* '.' */ + /* idx 96 */ {6, 47, NULL}, /* '/' */ + /* idx 97 */ {6, 47, NULL}, /* '/' */ + /* idx 98 */ {6, 47, NULL}, /* '/' */ + /* idx 99 */ {6, 47, NULL}, /* '/' */ + /* idx 100 */ {6, 51, NULL}, /* '3' */ + /* idx 101 */ {6, 51, NULL}, /* '3' */ + /* idx 102 */ {6, 51, NULL}, /* '3' */ + /* idx 103 */ {6, 51, NULL}, /* '3' */ + /* idx 104 */ {6, 52, NULL}, /* '4' */ + /* idx 105 */ {6, 52, NULL}, /* '4' */ + /* idx 106 */ {6, 52, NULL}, /* '4' */ + /* idx 107 */ {6, 52, NULL}, /* '4' */ + /* idx 108 */ {6, 53, NULL}, /* '5' */ + /* idx 109 */ {6, 53, NULL}, /* '5' */ + /* idx 110 */ {6, 53, NULL}, /* '5' */ + /* idx 111 */ {6, 53, NULL}, /* '5' */ + /* idx 112 */ {6, 54, NULL}, /* '6' */ + /* idx 113 */ {6, 54, NULL}, /* '6' */ + /* idx 114 */ {6, 54, NULL}, /* '6' */ + /* idx 115 */ {6, 54, NULL}, /* '6' */ + /* idx 116 */ {6, 55, NULL}, /* '7' */ + /* idx 117 */ {6, 55, NULL}, /* '7' */ + /* idx 118 */ {6, 55, NULL}, /* '7' */ + /* idx 119 */ {6, 55, NULL}, /* '7' */ + /* idx 120 */ {6, 56, NULL}, /* '8' */ + /* idx 121 */ {6, 56, NULL}, /* '8' */ + /* idx 122 */ {6, 56, NULL}, /* '8' */ + /* idx 123 */ {6, 56, NULL}, /* '8' */ + /* idx 124 */ {6, 57, NULL}, /* '9' */ + /* idx 125 */ {6, 57, NULL}, /* '9' */ + /* idx 126 */ {6, 57, NULL}, /* '9' */ + /* idx 127 */ {6, 57, NULL}, /* '9' */ + /* idx 128 */ {6, 61, NULL}, /* '=' */ + /* idx 129 */ {6, 61, NULL}, /* '=' */ + /* idx 130 */ {6, 61, NULL}, /* '=' */ + /* idx 131 */ {6, 61, NULL}, /* '=' */ + /* idx 132 */ {6, 65, NULL}, /* 'A' */ + /* idx 133 */ {6, 65, NULL}, /* 'A' */ + /* idx 134 */ {6, 65, NULL}, /* 'A' */ + /* idx 135 */ {6, 65, NULL}, /* 'A' */ + /* idx 136 */ {6, 95, NULL}, /* '_' */ + /* idx 137 */ {6, 95, NULL}, /* '_' */ + /* idx 138 */ {6, 95, NULL}, /* '_' */ + /* idx 139 */ {6, 95, NULL}, /* '_' */ + /* idx 140 */ {6, 98, NULL}, /* 'b' */ + /* idx 141 */ {6, 98, NULL}, /* 'b' */ + /* idx 142 */ {6, 98, NULL}, /* 'b' */ + /* idx 143 */ {6, 98, NULL}, /* 'b' */ + /* idx 144 */ {6, 100, NULL}, /* 'd' */ + /* idx 145 */ {6, 100, NULL}, /* 'd' */ + /* idx 146 */ {6, 100, NULL}, /* 'd' */ + /* idx 147 */ {6, 100, NULL}, /* 'd' */ + /* idx 148 */ {6, 102, NULL}, /* 'f' */ + /* idx 149 */ {6, 102, NULL}, /* 'f' */ + /* idx 150 */ {6, 102, NULL}, /* 'f' */ + /* idx 151 */ {6, 102, NULL}, /* 'f' */ + /* idx 152 */ {6, 103, NULL}, /* 'g' */ + /* idx 153 */ {6, 103, NULL}, /* 'g' */ + /* idx 154 */ {6, 103, NULL}, /* 'g' */ + /* idx 155 */ {6, 103, NULL}, /* 'g' */ + /* idx 156 */ {6, 104, NULL}, /* 'h' */ + /* idx 157 */ {6, 104, NULL}, /* 'h' */ + /* idx 158 */ {6, 104, NULL}, /* 'h' */ + /* idx 159 */ {6, 104, NULL}, /* 'h' */ + /* idx 160 */ {6, 108, NULL}, /* 'l' */ + /* idx 161 */ {6, 108, NULL}, /* 'l' */ + /* idx 162 */ {6, 108, NULL}, /* 'l' */ + /* idx 163 */ {6, 108, NULL}, /* 'l' */ + /* idx 164 */ {6, 109, NULL}, /* 'm' */ + /* idx 165 */ {6, 109, NULL}, /* 'm' */ + /* idx 166 */ {6, 109, NULL}, /* 'm' */ + /* idx 167 */ {6, 109, NULL}, /* 'm' */ + /* idx 168 */ {6, 110, NULL}, /* 'n' */ + /* idx 169 */ {6, 110, NULL}, /* 'n' */ + /* idx 170 */ {6, 110, NULL}, /* 'n' */ + /* idx 171 */ {6, 110, NULL}, /* 'n' */ + /* idx 172 */ {6, 112, NULL}, /* 'p' */ + /* idx 173 */ {6, 112, NULL}, /* 'p' */ + /* idx 174 */ {6, 112, NULL}, /* 'p' */ + /* idx 175 */ {6, 112, NULL}, /* 'p' */ + /* idx 176 */ {6, 114, NULL}, /* 'r' */ + /* idx 177 */ {6, 114, NULL}, /* 'r' */ + /* idx 178 */ {6, 114, NULL}, /* 'r' */ + /* idx 179 */ {6, 114, NULL}, /* 'r' */ + /* idx 180 */ {6, 117, NULL}, /* 'u' */ + /* idx 181 */ {6, 117, NULL}, /* 'u' */ + /* idx 182 */ {6, 117, NULL}, /* 'u' */ + /* idx 183 */ {6, 117, NULL}, /* 'u' */ + /* idx 184 */ {7, 58, NULL}, /* ':' */ + /* idx 185 */ {7, 58, NULL}, /* ':' */ + /* idx 186 */ {7, 66, NULL}, /* 'B' */ + /* idx 187 */ {7, 66, NULL}, /* 'B' */ + /* idx 188 */ {7, 67, NULL}, /* 'C' */ + /* idx 189 */ {7, 67, NULL}, /* 'C' */ + /* idx 190 */ {7, 68, NULL}, /* 'D' */ + /* idx 191 */ {7, 68, NULL}, /* 'D' */ + /* idx 192 */ {7, 69, NULL}, /* 'E' */ + /* idx 193 */ {7, 69, NULL}, /* 'E' */ + /* idx 194 */ {7, 70, NULL}, /* 'F' */ + /* idx 195 */ {7, 70, NULL}, /* 'F' */ + /* idx 196 */ {7, 71, NULL}, /* 'G' */ + /* idx 197 */ {7, 71, NULL}, /* 'G' */ + /* idx 198 */ {7, 72, NULL}, /* 'H' */ + /* idx 199 */ {7, 72, NULL}, /* 'H' */ + /* idx 200 */ {7, 73, NULL}, /* 'I' */ + /* idx 201 */ {7, 73, NULL}, /* 'I' */ + /* idx 202 */ {7, 74, NULL}, /* 'J' */ + /* idx 203 */ {7, 74, NULL}, /* 'J' */ + /* idx 204 */ {7, 75, NULL}, /* 'K' */ + /* idx 205 */ {7, 75, NULL}, /* 'K' */ + /* idx 206 */ {7, 76, NULL}, /* 'L' */ + /* idx 207 */ {7, 76, NULL}, /* 'L' */ + /* idx 208 */ {7, 77, NULL}, /* 'M' */ + /* idx 209 */ {7, 77, NULL}, /* 'M' */ + /* idx 210 */ {7, 78, NULL}, /* 'N' */ + /* idx 211 */ {7, 78, NULL}, /* 'N' */ + /* idx 212 */ {7, 79, NULL}, /* 'O' */ + /* idx 213 */ {7, 79, NULL}, /* 'O' */ + /* idx 214 */ {7, 80, NULL}, /* 'P' */ + /* idx 215 */ {7, 80, NULL}, /* 'P' */ + /* idx 216 */ {7, 81, NULL}, /* 'Q' */ + /* idx 217 */ {7, 81, NULL}, /* 'Q' */ + /* idx 218 */ {7, 82, NULL}, /* 'R' */ + /* idx 219 */ {7, 82, NULL}, /* 'R' */ + /* idx 220 */ {7, 83, NULL}, /* 'S' */ + /* idx 221 */ {7, 83, NULL}, /* 'S' */ + /* idx 222 */ {7, 84, NULL}, /* 'T' */ + /* idx 223 */ {7, 84, NULL}, /* 'T' */ + /* idx 224 */ {7, 85, NULL}, /* 'U' */ + /* idx 225 */ {7, 85, NULL}, /* 'U' */ + /* idx 226 */ {7, 86, NULL}, /* 'V' */ + /* idx 227 */ {7, 86, NULL}, /* 'V' */ + /* idx 228 */ {7, 87, NULL}, /* 'W' */ + /* idx 229 */ {7, 87, NULL}, /* 'W' */ + /* idx 230 */ {7, 89, NULL}, /* 'Y' */ + /* idx 231 */ {7, 89, NULL}, /* 'Y' */ + /* idx 232 */ {7, 106, NULL}, /* 'j' */ + /* idx 233 */ {7, 106, NULL}, /* 'j' */ + /* idx 234 */ {7, 107, NULL}, /* 'k' */ + /* idx 235 */ {7, 107, NULL}, /* 'k' */ + /* idx 236 */ {7, 113, NULL}, /* 'q' */ + /* idx 237 */ {7, 113, NULL}, /* 'q' */ + /* idx 238 */ {7, 118, NULL}, /* 'v' */ + /* idx 239 */ {7, 118, NULL}, /* 'v' */ + /* idx 240 */ {7, 119, NULL}, /* 'w' */ + /* idx 241 */ {7, 119, NULL}, /* 'w' */ + /* idx 242 */ {7, 120, NULL}, /* 'x' */ + /* idx 243 */ {7, 120, NULL}, /* 'x' */ + /* idx 244 */ {7, 121, NULL}, /* 'y' */ + /* idx 245 */ {7, 121, NULL}, /* 'y' */ + /* idx 246 */ {7, 122, NULL}, /* 'z' */ + /* idx 247 */ {7, 122, NULL}, /* 'z' */ + /* idx 248 */ {8, 38, NULL}, /* '&' */ + /* idx 249 */ {8, 42, NULL}, /* '*' */ + /* idx 250 */ {8, 44, NULL}, /* ',' */ + /* idx 251 */ {8, 59, NULL}, /* ';' */ + /* idx 252 */ {8, 88, NULL}, /* 'X' */ + /* idx 253 */ {8, 90, NULL}, /* 'Z' */ + /* idx 254 */ {8, 0, &byte1_pref0}, /* escape */ + /* idx 255 */ {8, 0, &byte1_pref1} /* escape */ + } +}; diff --git a/bin/varnishtest/vtc_h2_enctbl.h b/bin/varnishtest/vtc_h2_enctbl.h new file mode 100644 index 0000000..5e69f33 --- /dev/null +++ b/bin/varnishtest/vtc_h2_enctbl.h @@ -0,0 +1,257 @@ +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 new file mode 100644 index 0000000..63034fd --- /dev/null +++ b/bin/varnishtest/vtc_h2_hpack.c @@ -0,0 +1,430 @@ +/*- + * Copyright (c) 2008-2016 Varnish Software AS + * All rights reserved. + * + * Author: Guillaume Quintard + * + * 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 "hpack.h" +#include "vtc_h2_priv.h" + +struct symbol { + uint32_t val; + uint8_t size; +}; + +static 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" + +struct symbol *EOS = &coding_table[256]; + +#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; + int pl = 0; /* pack length*/ + struct stbl *tbl = &byte0; + struct ssym *sym; + + (void)nm; + while (ilen > 0 || pl != 0) { + assert(pl >= 0); + /* make sure we have enough data*/ + if (pl < tbl->msk) { + if (ilen == 0) { + if (pl == 0 || (MASK(pack, pl) == + (unsigned)((1 << pl) - 1))) { + assert(tbl == &byte0); + 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); + + pack <<= sym->csm; + pl -= sym->csm; + if (sym->nxt) { + tbl = sym->nxt; + continue; + } + str[l++] = sym->chr; + tbl = &byte0; + } + return (l); +} + +/* inspired from Dridi Boukelmoune's cashpack. */ +static int +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 (1); + *iter->buf = (char)(pack >> 56); + iter->buf++; + pack <<= 8; + pl -= 8; + } + str++; + } + + /* padding */ + if (pl) { + assert(pl < 8); + if (iter->buf == iter->end) + return (1); + pl += 8; + pack |= (uint64_t)0xff << (64 - pl); + *iter->buf = (char)(pack >> 56); + iter->buf++; + } + + return (0); +} + +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 < (1 << prefix) - 1) { + 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 = (1 << prefix) - 1; + + *iter->buf &= 0xff << 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 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 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); + if (num > iter->end - iter->buf) + return (hpk_err); + if (huff) { /*Huffman encoding */ + t->ptr = malloc((num * 8) / 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); + memcpy(t->ptr, t->ptr, num); + } 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 txt *to, const struct 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 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)) + return (hpk_err); + } + + if (hpk_err == str_decode(iter, &header->value)) + 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; + num_encode(iter, 7, h->i); + 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 new file mode 100644 index 0000000..706ba0c --- /dev/null +++ b/bin/varnishtest/vtc_h2_priv.h @@ -0,0 +1,22 @@ +#include "vqueue.h" + +#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; + char *orig; + char *buf; + char *end; +}; + +const struct txt * tbl_get_key(const struct hpk_ctx *ctx, uint32_t index); + +const struct 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 new file mode 100644 index 0000000..1e54862 --- /dev/null +++ b/bin/varnishtest/vtc_h2_stattbl.h @@ -0,0 +1,61 @@ +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 new file mode 100644 index 0000000..0a50d1c --- /dev/null +++ b/bin/varnishtest/vtc_h2_tbl.c @@ -0,0 +1,291 @@ +/*- + * Copyright (c) 2008-2016 Varnish Software AS + * All rights reserved. + * + * Author: Guillaume Quintard + * + * 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 + +#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 */ +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, char *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 = 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 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 txt * +tbl_get_key(const struct hpk_ctx *ctx, uint32_t idx) +{ + return (tbl_get_field(ctx, idx, 1)); +} + +const struct 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); +} + +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"); +} + +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_http.c b/bin/varnishtest/vtc_http.c index f7e993f..3c85f50 100644 --- a/bin/varnishtest/vtc_http.c +++ b/bin/varnishtest/vtc_http.c @@ -40,14 +40,14 @@ #include #include "vtc.h" +#include "vtc_http.h" #include "vct.h" #include "vgz.h" #include "vnum.h" #include "vre.h" #include "vtcp.h" - -#define MAX_HDR 50 +#include "hpack.h" /* SECTION: client-server client/server * @@ -120,39 +120,26 @@ * is of the form "CLIENTIP:PORT SERVERIP:PORT". * * 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 */ -struct http { - unsigned magic; -#define HTTP_MAGIC 0x2f02169c - int fd; - int *sfd; - int timeout; - struct vtclog *vl; - - struct vsb *vsb; - - int nrxbuf; - char *rxbuf; - char *rem_ip; - char *rem_port; - int prxbuf; - char *body; - unsigned bodyl; - char bodylen[20]; - char chunklen[20]; - - char *req[MAX_HDR]; - char *resp[MAX_HDR]; - - int gziplevel; - int gzipresidual; - - int fatal; -}; - #define ONLY_CLIENT(hp, av) \ do { \ + if (hp->h2) \ + vtc_log(hp->vl, 0, \ + "\"%s\" only possible before H/2 upgrade", \ + av[0]); \ if (hp->sfd != NULL) \ vtc_log(hp->vl, 0, \ "\"%s\" only possible in client", av[0]); \ @@ -160,6 +147,10 @@ struct http { #define ONLY_SERVER(hp, av) \ do { \ + if (hp->h2) \ + vtc_log(hp->vl, 0, \ + "\"%s\" only possible before H/2 upgrade", \ + av[0]); \ if (hp->sfd == NULL) \ vtc_log(hp->vl, 0, \ "\"%s\" only possible in server", av[0]); \ @@ -173,7 +164,7 @@ static const char * const nl = "\r\n"; * Generate a synthetic body */ -static char * +char * synth_body(const char *len, int rnd) { int i, j, k, l; @@ -336,6 +327,11 @@ cmd_var_resolve(struct http *hp, char *spec) } 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); @@ -544,8 +540,8 @@ http_rxchar(struct http *hp, int n, int eof) return (i); if (i == 0) vtc_log(hp->vl, hp->fatal, - "HTTP rx EOF (fd:%d read: %s)", - hp->fd, strerror(errno)); + "HTTP rx EOF (fd:%d read: %s) %d", + hp->fd, strerror(errno), n); if (i < 0) vtc_log(hp->vl, hp->fatal, "HTTP rx failed (fd:%d read: %s)", @@ -729,7 +725,7 @@ cmd_http_rxresphdrs(CMD_ARGS) av++; for(; *av != NULL; av++) - vtc_log(hp->vl, 0, "Unknown http rxreq spec: %s\n", *av); + vtc_log(hp->vl, 0, "Unknown http rxresp spec: %s\n", *av); http_rxhdr(hp); http_splitheader(hp, 0); if (http_count_header(hp->resp, "Content-Length") > 1) @@ -1035,33 +1031,75 @@ cmd_http_txresp(CMD_ARGS) 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_log(vl, 0, "Req misses \"Upgrade: h2c\" header"); + + h = http_find_header(hp->req, "Connection"); + if (!h || strcmp(h, "Upgrade, HTTP2-Settings")) + vtc_log(vl, 0, "Req misses \"Connection: " + "Upgrade, HTTP2-Settings\" header"); + + h = http_find_header(hp->req, "HTTP2-Settings"); + if (!h) + vtc_log(vl, 0, "Req misses \"HTTP2-Settings\" header"); + + + parse_string("txresp -status 101 " + "-hdr \"Connection: Upgrade\" " + "-hdr \"Upgrade: h2c\"\n", cmd, hp, vl); + + b64_settings(hp, h); + + parse_string("rxpri\n" + "stream 0 {\n" + "txsettings\n" + "rxsettings\n" + "txsettings -ack\n" + "rxsettings\n" + "expect settings.ack == true\n" + "} -start\n", cmd, hp, vl); +} + +/********************************************************************** + * 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; (void)cmd; - (void)vl; CAST_OBJ_NOTNULL(hp, priv, HTTP_MAGIC); ONLY_SERVER(hp, av); AZ(strcmp(av[0], "rxreq")); av++; for(; *av != NULL; av++) - vtc_log(hp->vl, 0, "Unknown http rxreq spec: %s\n", *av); + vtc_log(vl, 0, "Unknown http rxreq spec: %s\n", *av); http_rxhdr(hp); http_splitheader(hp, 1); if (http_count_header(hp->req, "Content-Length") > 1) - vtc_log(hp->vl, 0, + vtc_log(vl, 0, "Multiple Content-Length headers.\n"); http_swallow_body(hp, hp->req, 0); - vtc_log(hp->vl, 4, "bodylen = %s", hp->bodylen); + vtc_log(vl, 4, "bodylen = %s", hp->bodylen); } /* SECTION: client-server.spec.rxreqhdrs @@ -1133,7 +1171,7 @@ cmd_http_rxrespbody(CMD_ARGS) av++; for(; *av != NULL; av++) - vtc_log(hp->vl, 0, "Unknown http rxreq spec: %s\n", *av); + vtc_log(hp->vl, 0, "Unknown http rxrespbody spec: %s\n", *av); http_swallow_body(hp, hp->resp, 0); vtc_log(hp->vl, 4, "bodylen = %s", hp->bodylen); } @@ -1175,6 +1213,7 @@ cmd_http_txreq(CMD_ARGS) const char *req = "GET"; const char *url = "/"; const char *proto = "HTTP/1.1"; + const char *up = NULL; (void)cmd; (void)vl; @@ -1195,15 +1234,39 @@ cmd_http_txreq(CMD_ARGS) } else if (!strcmp(*av, "-req")) { req = av[1]; av++; + } else if (!hp->sfd && !strcmp(*av, "-up")) { + up = av[1]; + av++; } 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); + av = http_tx_parse_args(av, vl, hp, NULL); if (*av != NULL) vtc_log(hp->vl, 0, "Unknown http txreq spec: %s\n", *av); http_write(hp, 4, "txreq"); + + if (up) { + parse_string("rxresp\n" + "expect resp.status == 101\n" + "expect resp.http.connection == Upgrade\n" + "expect resp.http.upgrade == h2c\n" + "txpri\n", http_cmds, hp, vl); + b64_settings(hp, up); + parse_string("stream 0 {\n" + "txsettings\n" + "rxsettings\n" + "txsettings -ack\n" + "rxsettings\n" + "expect settings.ack == true" + "} -start\n", http_cmds, hp, vl); + } } /* SECTION: client-server.spec.recv @@ -1453,6 +1516,11 @@ cmd_http_timeout(CMD_ARGS) * Wait for the connected client to close the connection. */ +/* SECTION: client-server.spec.expect_close expect_close (server) + * + * Reads from the connection, expecting nothing to read but an EOF. + * + */ static void cmd_http_expect_close(CMD_ARGS) { @@ -1467,6 +1535,8 @@ cmd_http_expect_close(CMD_ARGS) AZ(av[1]); vtc_log(vl, 4, "Expecting close (fd = %d)", hp->fd); + if (hp->h2) + stop_h2(hp); while (1) { fds[0].fd = hp->fd; fds[0].events = POLLIN | POLLERR; @@ -1497,6 +1567,12 @@ cmd_http_expect_close(CMD_ARGS) * Close the active TCP connection */ +/* SECTION: client-server.spec.close close (server) + * + * Close the connection. Not that if operating in H/2 mode, no extra (GOAWAY) + * frame is sent, it's simply a TCP close. + * + */ static void cmd_http_close(CMD_ARGS) { @@ -1508,6 +1584,8 @@ cmd_http_close(CMD_ARGS) AZ(av[1]); assert(hp->sfd != NULL); assert(*hp->sfd >= 0); + if (hp->h2) + stop_h2(hp); VTCP_close(&hp->fd); vtc_log(vl, 4, "Closed"); } @@ -1518,6 +1596,11 @@ cmd_http_close(CMD_ARGS) * Close the active connection (if any) and accept a new one. */ +/* SECTION: client-server.spec.accept accept (server) + * + * Close the potential current connection, and accept a new one. Note that this + * new connection is H/1. + */ static void cmd_http_accept(CMD_ARGS) { @@ -1529,6 +1612,8 @@ cmd_http_accept(CMD_ARGS) AZ(av[1]); assert(hp->sfd != NULL); assert(*hp->sfd >= 0); + if (hp->h2) + stop_h2(hp); if (hp->fd >= 0) VTCP_close(&hp->fd); vtc_log(vl, 4, "Accepting"); @@ -1594,11 +1679,113 @@ cmd_http_fatal(CMD_ARGS) * Same as for the top-level barrier */ +char PREFACE[] = "PRI * HTTP/2.0\r\n\r\nSM\r\n\r\n"; + +/* SECTION: client-server.spec.txpri txpri (client) + * + * Send an H/2 preface ("PRI * HTTP/2.0\\r\\n\\r\\nSM\\r\\n\\r\\n") and set + * client to H/2. + */ +static void +cmd_http_txpri(CMD_ARGS) +{ + size_t l; + struct http *hp; + (void)cmd; + CAST_OBJ_NOTNULL(hp, priv, HTTP_MAGIC); + ONLY_CLIENT(hp, av); + + vtc_dump(hp->vl, 4, "txpri", PREFACE, sizeof(PREFACE) - 1); + l = write(hp->fd, PREFACE, sizeof(PREFACE) - 1); + if (l != sizeof(PREFACE) - 1) + vtc_log(vl, hp->fatal, "Write failed: (%zd vs %zd) %s", + l, sizeof(PREFACE) - 1, strerror(errno)); + start_h2(hp); + AN(hp->h2); +} + +/* SECTION: client-server.spec.rxpri rxpri (server) + * + * Receive a preface, and if it matches, sets the server to H/2, aborts + * otherwise. + */ +static void +cmd_http_rxpri(CMD_ARGS) +{ + struct http *hp; + (void)cmd; + CAST_OBJ_NOTNULL(hp, priv, HTTP_MAGIC); + ONLY_SERVER(hp, av); + + hp->prxbuf = 0; + if (!http_rxchar(hp, sizeof(PREFACE) - 1, 0)) + vtc_log(vl, 0, "Couldn't retrieve connection preface"); + if (strncmp(hp->rxbuf, PREFACE, sizeof(PREFACE) - 1)) + vtc_log(vl, 0, "Received invalid preface\n"); + start_h2(hp); + AN(hp->h2); +} + +/* SECTION: client-server.spec.settings + * + * settings -dectbl INT + * Force internal H/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); + (void)cmd; + + if (!hp->h2) + vtc_log(hp->vl, 0, "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_log(hp->vl, 0, "-dectbl takes an integer as" + " argument (found %s)", av[1]); + HPK_ResizeTbl(hp->decctx, n); + av++; + } else + vtc_log(vl, 0, "Unknown settings spec: %s\n", *av); + } +} + +static void +cmd_http_stream(CMD_ARGS) +{ + struct http *hp = (struct http *)priv; + 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("rxpri", http_cmds, hp, vl); + else + parse_string("txpri", http_cmds, hp, vl); + parse_string("stream 0 {\n" + "txsettings\n" + "rxsettings\n" + "txsettings -ack\n" + "rxsettings\n" + "expect settings.ack == true" + "} -run\n", http_cmds, hp, vl); + } + cmd_stream(av, hp, cmd, vl); +} + /********************************************************************** * Execute HTTP specifications */ -static const struct cmds http_cmds[] = { +const struct cmds http_cmds[] = { { "timeout", cmd_http_timeout }, { "txreq", cmd_http_txreq }, @@ -1629,6 +1816,12 @@ static const struct cmds http_cmds[] = { { "loop", cmd_http_loop }, { "fatal", cmd_http_fatal }, { "non-fatal", cmd_http_fatal }, + + { "rxpri", cmd_http_rxpri }, + { "txpri", cmd_http_txpri }, + { "stream", cmd_http_stream }, + { "settings", cmd_http_settings }, + { "upgrade", cmd_http_upgrade }, { NULL, NULL } }; @@ -1665,6 +1858,8 @@ http_process(struct vtclog *vl, const char *spec, int sock, int *sfd) VTCP_hisname(sock, hp->rem_ip, VTCP_ADDRBUFSIZE, hp->rem_port, VTCP_PORTBUFSIZE); parse_string(spec, http_cmds, hp, vl); + if (hp->h2) + stop_h2(hp); retval = hp->fd; VSB_destroy(&hp->vsb); free(hp->rxbuf); diff --git a/bin/varnishtest/vtc_http.h b/bin/varnishtest/vtc_http.h new file mode 100644 index 0000000..f944f36 --- /dev/null +++ b/bin/varnishtest/vtc_http.h @@ -0,0 +1,45 @@ +#define MAX_HDR 50 + +struct http { + unsigned magic; +#define HTTP_MAGIC 0x2f02169c + int fd; + int *sfd; + int timeout; + struct vtclog *vl; + + struct vsb *vsb; + + int nrxbuf; + char *rxbuf; + char *rem_ip; + char *rem_port; + int prxbuf; + char *body; + unsigned bodyl; + char bodylen[20]; + char chunklen[20]; + + char *req[MAX_HDR]; + char *resp[MAX_HDR]; + + int gziplevel; + int gzipresidual; + + int fatal; + + /* H/2 */ + unsigned h2; + int wf; + + pthread_t tp; + VTAILQ_HEAD(, stream) streams; + pthread_mutex_t mtx; + pthread_cond_t cond; + struct hpk_ctx *encctx; + struct hpk_ctx *decctx; + uint64_t iws; + int64_t ws; +}; + + diff --git a/bin/varnishtest/vtc_http2.c b/bin/varnishtest/vtc_http2.c new file mode 100644 index 0000000..4a90d24 --- /dev/null +++ b/bin/varnishtest/vtc_http2.c @@ -0,0 +1,2785 @@ +/*- + * Copyright (c) 2008-2016 Varnish Software AS + * All rights reserved. + * + * Author: Guillaume Quintard + * + * 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 "vtc_http.h" + +#include "vct.h" +#include "vgz.h" +#include "vnum.h" +#include "vre.h" +#include "hpack.h" +#include "vend.h" + +#define MAX_HDR 50 +#define ERR_MAX 13 +#define BUF_SIZE (1024*2048) + +static const char *const h2_errs[] = { +#define H2_ERROR(n,v,t) [v] = #n, +#include + NULL +}; + +static const char *const h2_types[] = { +#define H2_FRAME(l,u,t,f) [t] = #u, +#include + NULL +}; + +#define SETTINGS_MAX 0x06 + +static const char * const h2_settings[] = { + [0] = "unknown", +#define H2_SETTINGS(U,v,d) [v] = #U, +#include + NULL +}; + +enum h2_type { +#define H2_FRAME(l,u,t,f) TYPE_##u = t, +#include + TYPE_MAX +}; + +enum { + ACK = 0x1, + END_STREAM = 0x1, + PADDED = 0x8, + END_HEADERS = 0x4, + PRIORITY = 0x20, +}; + +struct stream { + unsigned magic; +#define STREAM_MAGIC 0x63f1fac2 + uint32_t id; + char *spec; + char *name; + VTAILQ_ENTRY(stream) list; + unsigned running; + pthread_cond_t cond; + struct frame *frame; + pthread_t tp; + unsigned reading; + struct http *hp; + int64_t ws; + + VTAILQ_HEAD(, frame) fq; + + char *body; + int 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) +{ + while (h->t) { + if (h->key.len) + free(h->key.ptr); + if (h->value.len) + free(h->value.ptr); + memset(h, 0, sizeof(*h)); + h++; + } +} + +#define ONLY_CLIENT(hp, av) \ + do { \ + if (hp->sfd != NULL) \ + vtc_log(hp->vl, 0, \ + "\"%s\" only possible in client", av[0]); \ + } while (0) + +#define ONLY_SERVER(hp, av) \ + do { \ + if (hp->sfd == NULL) \ + vtc_log(hp->vl, 0, \ + "\"%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->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, int n) +{ + int i; + struct pollfd pfd[1]; + + CHECK_OBJ_NOTNULL(hp, HTTP_MAGIC); + AN(buf); + + while (n > 0) { + pfd[0].fd = hp->fd; + pfd[0].events = POLLIN; + pfd[0].revents = 0; + i = poll(pfd, 1, hp->timeout); + if (i < 0 && errno == EINTR) + continue; + if (i == 0) + vtc_log(hp->vl, hp->fatal, + "HTTP2 rx timeout (fd:%d %u ms)", + hp->fd, hp->timeout); + if (i < 0) + vtc_log(hp->vl, hp->fatal, + "HTTP2 rx failed (fd:%d poll: %s)", + hp->fd, strerror(errno)); + assert(i > 0); + i = read(hp->fd, buf, n); + if (!(pfd[0].revents & POLLIN)) + vtc_log(hp->vl, 4, + "HTTP2 rx poll (fd:%d revents: %x n=%d, i=%d)", + hp->fd, pfd[0].revents, n, i); + if (i == 0) + vtc_log(hp->vl, hp->fatal, + "HTTP2 rx EOF (fd:%d read: %s)", + hp->fd, strerror(errno)); + if (i < 0) + vtc_log(hp->vl, hp->fatal, + "HTTP2 rx failed (fd:%d read: %s)", + hp->fd, strerror(errno)); + 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 = (0xff & (unsigned char)buf[5]) << 24; + f->stid += (0xff & (unsigned char)buf[6]) << 16; + f->stid += (0xff & (unsigned char)buf[7]) << 8; + f->stid += (0xff & (unsigned char)buf[8]); +}; + +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; + + buf[5] = (f->stid >> 24) & 0xff; + buf[6] = (f->stid >> 16) & 0xff; + buf[7] = (f->stid >> 8) & 0xff; + buf[8] = (f->stid ) & 0xff; +} + +#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 +clean_frame(struct frame **f) +{ + AN(f); + if (!*f) + return; + + CHECK_OBJ_NOTNULL(*f, FRAME_MAGIC); + + if ((*f)->type == TYPE_GOAWAY) + free((*f)->md.goaway.debug); + free((*f)->data); + free(*f); + *f = NULL; +} + +static void +write_frame(struct http *hp, const struct frame *f, const unsigned lock) +{ + ssize_t l; + char hdr[9]; + + CHECK_OBJ_NOTNULL(hp, HTTP_MAGIC); + CHECK_OBJ_NOTNULL(f, FRAME_MAGIC); + + writeFrameHeader(hdr, f); + + vtc_log(hp->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 (lock) + AZ(pthread_mutex_lock(&hp->mtx)); + l = write(hp->fd, hdr, sizeof(hdr)); + if (l != sizeof(hdr)) + vtc_log(hp->vl, hp->fatal, "Write failed: (%zd vs %zd) %s", + l, sizeof(hdr), strerror(errno)); + + if (f->size) { + AN(f->data); + l = write(hp->fd, f->data, f->size); + if (l != f->size) + vtc_log(hp->vl, hp->fatal, + "Write failed: (%zd vs %d) %s", + l, f->size, strerror(errno)); + } + if (lock) + AZ(pthread_mutex_unlock(&hp->mtx)); +} + +static void +exclusive_stream_dependency(const struct stream *s) +{ + struct stream *target = NULL; + 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"); + flags &= ~ACK; + } else if (flags & END_STREAM && + (type == TYPE_HEADERS || + type == TYPE_PUSH_PROMISE || + type == TYPE_DATA)) { + vtc_log(vl, 3, "flag: END_STREAM"); + flags &= ~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"); + flags &= ~END_HEADERS; + } else if (flags & PRIORITY && + (type == TYPE_HEADERS || + type == TYPE_PUSH_PROMISE)) { + vtc_log(vl, 3, "flag: END_PRIORITY"); + flags &= ~PRIORITY; + } else if (flags & PADDED && + (type == TYPE_DATA || + type == TYPE_HEADERS || + type == TYPE_PUSH_PROMISE)) { + vtc_log(vl, 3, "flag: PADDED"); + flags &= ~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(hp->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(hp->vl, 4, "padding: %3d", f->md.padded); + } + + if (!size) + vtc_log(hp->vl, 4, "s%u - no data", s->id); + + if (s->id) + s->ws -= size; + s->hp->ws -= size; + + if (s->body) { + s->body = realloc(s->body, s->bodylen + size + 1L); + } else { + AZ(s->bodylen); + s->body = malloc(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(hp->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(hp->vl, 4, "padding: %3d", f->md.padded); + } + + if (f->type == TYPE_HEADERS && f->flags & PRIORITY){ + shift += 5; + n = vbe32dec(f->data); + s->dependency = n & ~(1 << 31); + exclusive = n >> 31; + + s->weight = f->data[4]; + if (exclusive) + exclusive_stream_dependency(s); + + vtc_log(hp->vl, 4, "stream->dependency: %u", s->dependency); + vtc_log(hp->vl, 4, "stream->weight: %u", s->weight); + } else if (f->type == TYPE_PUSH_PROMISE){ + shift += 4; + n = vbe32dec(f->data); + f->md.promised = n & ~(1 << 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_log(hp->vl, 0, "Size should be 5, but isn't (%d)", f->size); + + buf = f->data; + AN(buf); + + n = vbe32dec(f->data); + f->md.prio.stream = n & ~(1 << 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(hp->vl, 3, "prio->stream: %u", f->md.prio.stream); + vtc_log(hp->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_log(hp->vl, 0, "Size should be 4, but isn't (%d)", f->size); + + err = vbe32dec(f->data); + f->md.rst_err = err; + + vtc_log(hp->vl, 2, "ouch"); + if (err <= ERR_MAX) + buf = h2_errs[err]; + else + buf = "unknown"; + vtc_log(hp->vl, 4, "rst->err: %s (%d)", buf, err); + +} + +static void +parse_settings(const struct stream *s, struct frame *f) +{ + struct http *hp; + int i, t, v; + 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 % 6) + vtc_log(hp->vl, 0, "Size should be a multiple of 6, but isn't (%d)", f->size); + + for (i = 0; i <= SETTINGS_MAX; i++) + f->md.settings[i] = NAN; + + for (i = 0; i < f->size;) { + t = vbe16dec(f->data + i); + i += 2; + v = vbe32dec(f->data + i); + if (t <= SETTINGS_MAX) { + buf = h2_settings[t]; + f->md.settings[t] = v; + } else + buf = "unknown"; + i += 4; + + if (t == 1 ) + HPK_ResizeTbl(s->hp->encctx, v); + + vtc_log(hp->vl, 4, "settings->%s (%d): %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_log(hp->vl, 0, "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(hp->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_log(hp->vl, 0, "Size should be at least 8, but isn't (%d)", f->size); + if (f->data[0] & (1<<7)) + vtc_log(hp->vl, 0, "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(hp->vl, 3, "goaway->laststream: %d", stid); + vtc_log(hp->vl, 3, "goaway->err: %s (%d)", err_buf, err); + if (f->md.goaway.debug) + vtc_log(hp->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_log(hp->vl, 0, "Size should be 4, but isn't (%d)", f->size); + if (f->data[0] & (1<<7)) + vtc_log(hp->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(hp->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); + + AZ(pthread_mutex_lock(&hp->mtx)); + while (hp->h2) { + /*no wanted frames? */ + if (hp->wf == 0) { + AZ(pthread_cond_wait(&hp->cond, &hp->mtx)); + continue; + } + AZ(pthread_mutex_unlock(&hp->mtx)); + + if (!get_bytes(hp, hdr, 9)) { + vtc_log(hp->vl, 1, "could not get 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'; + get_bytes(hp, f->data, f->size); + } + + /* is the corresponding stream waiting? */ + AZ(pthread_mutex_lock(&hp->mtx)); + s = NULL; + while (!s) { + VTAILQ_FOREACH(s, &hp->streams, list) { + if (s->id == f->stid) + break; + } + if (!s) + AZ(pthread_cond_wait(&hp->cond, &hp->mtx)); + if (!hp->h2) { + clean_frame(&f); + AZ(pthread_mutex_unlock(&hp->mtx)); + return (NULL); + } + } + AZ(pthread_mutex_unlock(&hp->mtx)); + + if (expect_cont && (f->type != TYPE_CONTINUATION || + expect_cont != s->id)) + vtc_log(hp->vl, 0, "Expected CONTINUATION frame for" + " stream %u", expect_cont); + + /* parse the frame according to it type, and fill the metada */ + 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; + } + 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; + 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"); + } + + AZ(pthread_mutex_lock(&hp->mtx)); + VTAILQ_INSERT_HEAD(&s->fq, f, list); + hp->wf--; + AZ(pthread_cond_signal(&s->cond)); + continue; + } + AZ(pthread_mutex_unlock(&hp->mtx)); + AZ(vsb); + return (NULL); +} + +#define STRTOU32(n, s, p, v, c) \ + do { \ + n = strtoul(s, &p, 0); \ + if (*p != '\0') { \ + vtc_log(v, 0, "%s takes an integer as argument" \ + "(found %s)", c, s); \ + WRONG("Couldn't convert to integer");\ + } \ + } while (0) + +#define STRTOU32_CHECK(n, sp, p, v, c, l) \ +do { \ + sp++; \ + AN(*sp); \ + STRTOU32(n, *sp, p, v, c); \ + if (l && n >= (1 << l)) \ + vtc_log(v, 0, 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_log(s->hp->vl, 0, "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, "%d", 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 && !memcmp(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); + } + else 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. + */ + else 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. + */ + else if (!strcmp(spec, "prio.stream")) { + CHECK_LAST_FRAME(PRIORITY); + RETURN_BUFFED(f->md.prio.stream); + } + else if (!strcmp(spec, "prio.exclusive")) { + CHECK_LAST_FRAME(PRIORITY); + snprintf(buf, 20, f->md.prio.exclusive ? "true" : "false"); + return (buf); + } + else 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. + */ + else 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. + */ + else 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); + } + else if (!strcmp(spec, "push")) { + if (isnan(f->md.settings[2])) + return (NULL); + else if (f->md.settings[2] == 1) + snprintf(buf, 20, "true"); + else + snprintf(buf, 20, "false"); + return (buf); + } + else if (!strcmp(spec, "hdrtbl")) { RETURN_SETTINGS(1); } + else if (!strcmp(spec, "maxstreams")) { RETURN_SETTINGS(3); } + else if (!strcmp(spec, "winsize")) { RETURN_SETTINGS(4); } + else if (!strcmp(spec, "framesize")) { RETURN_SETTINGS(5); } + else if (!strcmp(spec, "hdrsize")) { RETURN_SETTINGS(6); } + } + /* SECTION: stream.spec.zexpect.push PUSH_PROMISE specific + * push.id + * The id of the promised stream. + */ + else 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. + */ + else 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. + */ + else if (!strncmp(spec, "frame.", 6)) { + spec += 6; + if (!f) + vtc_log(s->hp->vl, 0, "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_log(s->hp->vl, 0, + "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 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. + */ + else if (!strcmp(spec, "stream.window")) { + snprintf(buf, 20, "%ld", s->id ? s->ws : s->hp->ws); + return (buf); + } + else if (!strcmp(spec, "stream.weight")) { + if (s->id) { + snprintf(buf, 20, "%d", s->weight); + return (buf); + } else { + return NULL; + } + } + else 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. + */ + else 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. + */ + else 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); + } + else + return (spec); + return(NULL); +} + +/* 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, will 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; + char *q; + char *buf; + char tmp[3]; + int i; + unsigned size = 0; + (void)cmd; + + CAST_OBJ_NOTNULL(s, priv, STREAM_MAGIC); + CAST_OBJ_NOTNULL(hp, s->hp, HTTP_MAGIC); + AN(av[1]); + AZ(av[2]); + + q = av[1]; + size = strlen(q)/2; + buf = malloc(size); + AN(buf); + for (i = 0; i < size; i++) { + while (vct_issp(*q)) + q++; + if (*q == '\0') + break; + memcpy(tmp, q, 2); + q += 2; + tmp[2] = '\0'; + if (!vct_ishex(tmp[0]) || !vct_ishex(tmp[1])) + vtc_log(vl, 0, "Illegal Hex char \"%c%c\"", + tmp[0], tmp[1]); + buf[i] = (uint8_t)strtoul(tmp, NULL, 16); + } + AZ(pthread_mutex_lock(&hp->mtx)); + http_write(hp, 4, buf, i, "sendhex"); + + AZ(pthread_mutex_unlock(&hp->mtx)); + vtc_hexdump(vl, 4, "sendhex", (void *)buf, size); + free(buf); +} + +#define ENC(hdr, k, v) \ +{ \ + AN(k); \ + hdr.key.ptr = strdup(k); \ + AN(hdr.key.ptr); \ + hdr.key.len = strlen(k); \ + AN(v); \ + hdr.value.ptr = strdup(v); \ + AN(hdr.value.ptr); \ + hdr.value.len = strlen(v); \ + HPK_EncHdr(iter, &hdr); \ + free(hdr.key.ptr); \ + free(hdr.value.ptr); \ +} + +#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_log(vl, 0, 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, txresp will send + * HEADER frames, txcont will send CONTINUATION frames, and 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. + * + * \-req STRING (txreq, txpush) + * Set the :method pseudo-header. + * + * \-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 idex 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. + * + * \-bodylen INT (txreq, txresp) + * Do the same thing as ``-body`` but generate an string of INT length + * for you. + * + * \-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. + */ +static void +cmd_tx11obj(CMD_ARGS) +{ + struct stream *s; + int status_done = 1; + int method_done = 1; + int path_done = 1; + int scheme_done = 1; + uint32_t stid = 0, pstid; + uint32_t weight = 16; + int 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; + (void)cmd; + + 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_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_SERVER(s->hp, av); + f.type = TYPE_HEADERS; + f.flags |= END_STREAM; + status_done = 0; + } else if (!strcmp(cmd_str, "txpush")) { + ONLY_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("-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")) { + 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); + HPK_EncHdr(iter, &hdr); + } + 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_log(vl, 0, "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"); + HPK_EncHdr(iter, &hdr); + } + 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_log(vl, 0, "first -litHdr arg can be inc, " + "not, never (got: %s)", *av); + + STR_ENC(av, key, "second -litHdr"); + STR_ENC(av, value, "fourth -litHdr"); + HPK_EncHdr(iter, &hdr); + } + 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]); + f.flags &= ~END_STREAM; + av++; + } + else if (AV_IS("-bodylen")) { + AZ(body); + body = synth_body(av[1], 0); + f.flags &= ~END_STREAM; + av++; + }else if (AV_IS("-dep")) { + STRTOU32_CHECK(stid, av, p, vl, "-dep", 0); + f.flags |= PRIORITY; + } + else if (AV_IS("-ex")) { + exclusive = 1 << 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_log(vl, 0, "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) >= 128) + vtc_log(vl, 0, "Padding is limited to 128 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->hp, &f, 1); + free(buf); + + if (!body) + return; + + INIT_FRAME(f, DATA, strlen(body), s->id, END_STREAM); + f.data = body; + + write_frame(s->hp, &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; + (void)cmd; + + 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(data); + 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_log(vl, 0, "Unknown txdata spec: %s\n", *av); + + if (!body) + body = strdup(""); + + if (pad) { + f.flags |= PADDED; + if (strlen(pad) >= 128) + vtc_log(vl, 0, "Padding is limited to 128 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->hp, &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; + struct frame f; + (void)cmd; + 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_log(vl, 0, "Unknown txrst spec: %s\n", *av); + + err = htonl(err); + f.data = (void *)&err; + write_frame(s->hp, &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]; + + (void)cmd; + 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_log(vl, 0, "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->hp, &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 + * + * \-ack + * set the ack bit + */ +static void +cmd_txsettings(CMD_ARGS) +{ + struct stream *s, *_s; + struct http *hp; + char *p; + uint32_t val = 0; + struct frame f; + //TODO dynamic alloc + char buf[512]; + char *cursor = buf; + + (void)cmd; + 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; + + AZ(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_log(vl, 0, "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); + HPK_ResizeTbl(s->hp->decctx, val); + } + 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(_s, &hp->streams, list) + _s->ws += (val - hp->iws); + hp->iws = 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 (!strcmp(*av, "-ack")) { + f.flags |= 1; + } else + break; + } + if (*av != NULL) + vtc_log(vl, 0, "Unknown txsettings spec: %s\n", *av); + + write_frame(hp, &f, 0); + AZ(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]; + + (void)cmd; + 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_log(vl, 0, "this frame already has data"); + if (strlen(*av) != 8) + vtc_log(vl, 0, "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_log(vl, 0, "Unknown txping spec: %s\n", *av); + if (!f.data) + f.data = buf; + write_frame(s->hp, &f, 1); +} + +/* + * SECTION: stream.spec.goaway_txgoaway rxgoaway + * + * Possible options include: + * + * \-err STRING|INT + * set the error code to eplain 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; + char buf[8]; + + (void)cmd; + CAST_OBJ_NOTNULL(s, priv, STREAM_MAGIC); + memset(buf, 0, 8); + + 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_log(vl, 0, "this frame already has debug data"); + f.size = 8 + strlen(*av); + f.data = malloc(f.size); + memcpy(f.data + 8, *av, f.size - 8); + } else + break; + } + if (*av != NULL) + vtc_log(vl, 0, "Unknown txgoaway spec: %s\n", *av); + + if (!f.data) + f.data = malloc(2); + vbe32enc(f.data, ls); + vbe32enc(f.data + 4, err); + write_frame(s->hp, &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; + + (void)cmd; + 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_log(vl, 0, "Unknown txwinup spec: %s\n", *av); + + AZ(pthread_mutex_lock(&hp->mtx)); + if (s->id == 0) + hp->ws += size; + s->ws += size; + AZ(pthread_mutex_unlock(&hp->mtx)); + + size = htonl(size); + f.data = (void *)&size; + write_frame(hp, &f, 1); +} + +static struct frame * +rxstuff(struct stream *s) { + struct frame *f; + + CHECK_OBJ_NOTNULL(s, STREAM_MAGIC); + + AZ(pthread_mutex_lock(&s->hp->mtx)); + s->hp->wf++; + if (VTAILQ_EMPTY(&s->fq)) { + AZ(pthread_cond_signal(&s->hp->cond)); + AZ(pthread_cond_wait(&s->cond, &s->hp->mtx)); + } + if (VTAILQ_EMPTY(&s->fq)) { + AZ(pthread_mutex_unlock(&s->hp->mtx)); + return (NULL); + } + clean_frame(&s->frame); + f = VTAILQ_LAST(&s->fq, fq_head); + VTAILQ_REMOVE(&s->fq, f, list); + AZ(pthread_mutex_unlock(&s->hp->mtx)); + + CHECK_OBJ_NOTNULL(f, FRAME_MAGIC); + return (f); +} + +#define CHKFRAME(rt, wt, rcv, func) \ + do { \ + if (rt != wt) \ + vtc_log(vl, 0, "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_12 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; + int times = 1; + int rcv = 0; + enum h2_type expect = TYPE_HEADERS; + + (void)cmd; + CAST_OBJ_NOTNULL(s, priv, STREAM_MAGIC); + + while (*++av) { + if (!strcmp(*av, "-some")) { + STRTOU32_CHECK(times, av, p, vl, "-some", 0); + AN(times); + } else if (!strcmp(*av, "-all")) { + loop = 1; + } else + break; + } + if (*av != NULL) + vtc_log(vl, 0, "Unknown rxhdrs spec: %s\n", *av); + + while (rcv++ < times || (loop && !(f->flags & END_HEADERS))) { + f = rxstuff(s); + if (!f) + return; + CHKFRAME(f->type, expect, rcv, "rxhdrs"); + expect = TYPE_CONTINUATION; + } + s->frame = f; +} + +static void +cmd_rxcont(CMD_ARGS) +{ + struct stream *s; + struct frame *f = NULL; + char *p; + int loop = 0; + int times = 1; + int rcv = 0; + + (void)cmd; + (void)av; + CAST_OBJ_NOTNULL(s, priv, STREAM_MAGIC); + + while (*++av) { + if (!strcmp(*av, "-some")) { + STRTOU32_CHECK(times, av, p, vl, "-some", 0); + } else if (!strcmp(*av, "-all")) { + loop = 1; + } else + break; + } + if (*av != NULL) + vtc_log(vl, 0, "Unknown rxcont spec: %s\n", *av); + + while (rcv++ < times || (loop && !(f->flags & END_HEADERS))) { + f = rxstuff(s); + if (!f) + return; + CHKFRAME(f->type, TYPE_CONTINUATION, rcv, "rxcont"); + } + 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; + int times = 1; + int rcv = 0; + + (void)cmd; + (void)av; + CAST_OBJ_NOTNULL(s, priv, STREAM_MAGIC); + + while (*++av) { + if (!strcmp(*av, "-some")) { + STRTOU32_CHECK(times, av, p, vl, "-some", 0); + } else if (!strcmp(*av, "-all")) { + loop = 1; + } else + break; + } + if (*av != NULL) + vtc_log(vl, 0, "Unknown rxdata spec: %s\n", *av); + + while (rcv++ < times || (loop && !(f->flags & END_STREAM))) { + f = rxstuff(s); + if (!f) + return; + CHKFRAME(f->type, TYPE_DATA, rcv, "rxhdata"); + } + 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. + * + */ +static void +cmd_rxreqsp(CMD_ARGS) +{ + struct stream *s; + struct frame *f; + int end_stream; + int rcv = 0; + + (void)cmd; + CAST_OBJ_NOTNULL(s, priv, STREAM_MAGIC); + + if (!strcmp(av[0], "rxreq")) + ONLY_SERVER(s->hp, av); + else + ONLY_CLIENT(s->hp, av); + + f = rxstuff(s); + if (!f) + return; + + rcv++; + CHKFRAME(f->type, TYPE_HEADERS, rcv, *av); + + end_stream = f->flags & END_STREAM; + + while (!(f->flags & END_HEADERS)) { + f = rxstuff(s); + if (!f) + return; + rcv++; + CHKFRAME(f->type, TYPE_CONTINUATION, rcv, *av); + } + + while (!end_stream && (f = rxstuff(s))) { + rcv++; + CHKFRAME(f->type, TYPE_DATA, rcv, *av); + end_stream = f->flags & END_STREAM; + } + s->frame = f; +} + +/* SECTION: stream.spec.data_11 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; + int times = 1; + int rcv = 0; + enum h2_type expect = TYPE_PUSH_PROMISE; + + (void)cmd; + CAST_OBJ_NOTNULL(s, priv, STREAM_MAGIC); + + while (*++av) { + if (!strcmp(*av, "-some")) { + STRTOU32_CHECK(times, av, p, vl, "-some", 0); + AN(times); + } else if (!strcmp(*av, "-all")) { + loop = 1; + } else + break; + } + if (*av != NULL) + vtc_log(vl, 0, "Unknown rxpush spec: %s\n", *av); + + while (rcv++ < times || (loop && !(f->flags & END_HEADERS))) { + f = rxstuff(s); + if (!f) + return; + CHKFRAME(f->type, expect, rcv, "rxpush"); + expect = TYPE_CONTINUATION; + } + s->frame = f; +} + +#define RXFUNC(lctype, upctype) \ + static void \ + cmd_rx ## lctype(CMD_ARGS) { \ + struct stream *s; \ + (void)cmd; \ + (void)av; \ + CAST_OBJ_NOTNULL(s, priv, STREAM_MAGIC); \ + if ((s->frame = rxstuff(s))) \ + return; \ + if (s->frame->type != TYPE_ ## upctype) \ + vtc_log(vl, 0, "Received frame of type %d " \ + "is invalid for %s", \ + s->frame->type, "rx ## lctype"); \ + } + +/* SECTION: stream.spec.prio_rxprio rxprio + * + * Receive a PRIORITY frame + */ +RXFUNC(prio, PRIORITY) + +/* SECTION: stream.spec.reset_rxrst rxrst + * + * Receive a RST_STREAM frame + */ +RXFUNC(rst, RST_STREAM) + +/* SECTION: stream.spec.settings_rxsettings rxsettings + * + * Receive a SETTINGS frame + */ +RXFUNC(settings,SETTINGS) + +/* 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.winup_rxwinup rxwinup + * + * Receive a WINDOW_UPDATE frame + */ +RXFUNC(winup, WINDOW_UPDATE) + +/* SECTION: stream.spec.frame_rxframe + * + * Receive a frame, any frame. + */ +static void +cmd_rxframe(CMD_ARGS) { + struct stream *s; + (void)cmd; + (void)vl; + (void)av; + CAST_OBJ_NOTNULL(s, priv, STREAM_MAGIC); + rxstuff(s); +} + + + +static void +cmd_http_expect(CMD_ARGS) +{ + struct http *hp; + struct stream *s; + const char *lhs, *clhs; + char *cmp; + const char *rhs, *crhs; + vre_t *vre; + const char *error; + int erroroffset; + int i, retval = -1; + char buf[20]; + + (void)cmd; + 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]); + AZ(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); + + clhs = lhs ? lhs : ""; + crhs = rhs ? rhs : ""; + + if (!strcmp(cmp, "~") || !strcmp(cmp, "!~")) { + vre = VRE_compile(crhs, 0, &error, &erroroffset); + if (vre == NULL) + vtc_log(vl, 0, "REGEXP error: %s (@%d) (%s)", + error, erroroffset, crhs); + i = VRE_exec(vre, clhs, strlen(clhs), 0, 0, NULL, 0, 0); + retval = (i >= 0 && *cmp == '~') || (i < 0 && *cmp == '!'); + VRE_free(&vre); + } else if (!strcmp(cmp, "==")) { + retval = strcmp(clhs, crhs) == 0; + } else if (!strcmp(cmp, "!=")) { + retval = strcmp(clhs, crhs) != 0; + } else if (lhs == NULL || rhs == NULL) { + // fail inequality comparisons if either side is undef'ed + retval = 0; + } else if (!strcmp(cmp, "<")) { + retval = isless(VNUM(lhs), VNUM(rhs)); + } else if (!strcmp(cmp, ">")) { + retval = isgreater(VNUM(lhs), VNUM(rhs)); + } else if (!strcmp(cmp, "<=")) { + retval = islessequal(VNUM(lhs), VNUM(rhs)); + } else if (!strcmp(cmp, ">=")) { + retval = isgreaterequal(VNUM(lhs), VNUM(rhs)); + } + + if (retval == -1) + vtc_log(vl, 0, + "EXPECT %s (%s) %s %s (%s) test not implemented", + av[0], clhs, av[1], av[2], crhs); + else + vtc_log(vl, retval ? 4 : 0, "(s%d) EXPECT %s (%s) %s \"%s\" %s", + s->id, av[0], clhs, cmp, crhs, retval ? "match" : "failed"); + AZ(pthread_mutex_unlock(&s->hp->mtx)); +} + +/* 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[] = { + { "expect", cmd_http_expect }, + { "sendhex", cmd_sendhex }, + { "rxframe", cmd_rxframe }, + { "txdata", cmd_txdata }, + { "rxdata", cmd_rxdata }, + { "rxhdrs", cmd_rxhdrs }, + { "txreq", cmd_tx11obj }, + { "rxreq", cmd_rxreqsp }, + { "txresp", cmd_tx11obj }, + { "rxresp", cmd_rxreqsp }, + { "txprio", cmd_txprio }, + { "rxprio", cmd_rxprio }, + { "txrst", cmd_txrst }, + { "rxrst", cmd_rxrst }, + { "txsettings", cmd_txsettings }, + { "rxsettings", cmd_rxsettings }, + { "txpush", cmd_tx11obj }, + { "rxpush", cmd_rxpush }, + { "txping", cmd_txping }, + { "rxping", cmd_rxping }, + { "txgoaway", cmd_txgoaway }, + { "rxgoaway", cmd_rxgoaway }, + { "txwinup", cmd_txwinup }, + { "rxwinup", cmd_rxwinup }, + { "txcont", cmd_tx11obj }, + { "rxcont", cmd_rxcont }, + { "delay", cmd_delay }, + { "barrier", cmd_barrier }, + { NULL, NULL } +}; + +static void * +stream_thread(void *priv) +{ + struct stream *s; + + CAST_OBJ_NOTNULL(s, priv, STREAM_MAGIC); + + parse_string(s->spec, stream_cmds, s, s->hp->vl); + + clean_headers(s->req); + clean_headers(s->resp); + vtc_log(s->hp->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; + struct stream *s; + + ALLOC_OBJ(s, STREAM_MAGIC); + AN(s); + pthread_cond_init(&s->cond, NULL); + REPLACE(s->name, name); + AN(name); + VTAILQ_INIT(&s->fq); + s->ws = h->iws; + + s->weight = 16; + s->dependency = 0; + + STRTOU32(s->id, name, p, h->vl, "stream"); + if (s->id & (1 << 31)) + vtc_log(h->vl, 0, "Stream id must be a 31-bits integer " + "(found %s)", name); + + CHECK_OBJ_NOTNULL(h, HTTP_MAGIC); + s->hp = h; + + //bprintf(s->connect, "%s", "${v1_sock}"); + AZ(pthread_mutex_lock(&h->mtx)); + VTAILQ_INSERT_HEAD(&h->streams, s, list); + AZ(pthread_mutex_unlock(&h->mtx)); + return (s); +} + +/********************************************************************** + * Clean up stream + */ + +static void +stream_delete(struct stream *s) +{ + CHECK_OBJ_NOTNULL(s, STREAM_MAGIC); + 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 %p", s); + AZ(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); + AZ(pthread_join(s->tp, &res)); + if (res != NULL) + vtc_log(s->hp->vl, 0, "Stream %u returned \"%s\"", s->id, + (char *)res); + + VTAILQ_FOREACH_SAFE(f, &s->fq, list, f2) + 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.zstream stream + * + * H/2 introduces the concept of streams, and these come with their own + * specification, and as it's quite big, have bee move 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 H/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 H/2 stream number, while SPEC describes what will be done in that + * stream. + * + * Note that, when parsing a stream action, if the entity isn't operating in H/2 + * mode, these spec is ran before:: + * + * txpri/rxpri # client/server + * stream 0 { + * txsettings + * rxsettings + * txsettings -ack + * rxsettings + * expect settings.ack == true + * } -run + * + * And H/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)cmd; + (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_log(vl, 0, "Unknown client argument: %s", *av); + REPLACE(s->spec, *av); + } +} + +void +b64_settings(const struct http *hp, const char *s) +{ + uint16_t i; + uint64_t v; + const char *buf; + int shift; + while (*s) { + v = 0; + for (shift = 42; shift >= 0; shift -= 6) { + if (*s >= 'A' && *s <= 'Z') { + v |= (uint64_t)(*s - 'A') << shift; + } else if (*s >= 'a' && *s <= 'z') { + v |= (uint64_t)(*s - 'a' + 26) << shift; + } else if (*s >= '0' && *s <= '9') { + v |= (uint64_t)(*s - '0' + 52) << shift; + } else if (*s == '-') + v |= (uint64_t)62 << shift; + else if (*s == '_') { + v |= (uint64_t)63 << shift; + } else + vtc_log(hp->vl, 0, "Bad \"HTTP2-Settings\" " + "header"); + s++; + } + i = v >> 32; + v &= 0xffff; + + if (i <= SETTINGS_MAX) { + buf = h2_settings[i]; + } else + buf = "unknown"; + + if (v == 1 ) { + if (hp->sfd) + HPK_ResizeTbl(hp->encctx, v); + else + HPK_ResizeTbl(hp->decctx, v); + } + + vtc_log(hp->vl, 4, "Upgrade: %s (%d): %ld", buf, i, v); + } +} + +void +start_h2(struct http *hp) +{ + CHECK_OBJ_NOTNULL(hp, HTTP_MAGIC); + AZ(pthread_mutex_init(&hp->mtx, NULL)); + AZ(pthread_cond_init(&hp->cond, NULL)); + VTAILQ_INIT(&hp->streams); + hp->iws = 0xffff; + hp->ws = 0xffff; + + hp->h2 = 1; + + hp->decctx = HPK_NewCtx(4096); + hp->encctx = HPK_NewCtx(4096); + AZ(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); + AZ(pthread_mutex_lock(&hp->mtx)); + VTAILQ_REMOVE(&hp->streams, s, list); + AZ(pthread_mutex_unlock(&hp->mtx)); + stream_delete(s); + } + + AZ(pthread_mutex_lock(&hp->mtx)); + hp->h2 = 0; + AZ(pthread_cond_signal(&hp->cond)); + AZ(pthread_mutex_unlock(&hp->mtx)); + AZ(pthread_join(hp->tp, NULL)); + + HPK_FreeCtx(hp->decctx); + HPK_FreeCtx(hp->encctx); + + AZ(pthread_mutex_destroy(&hp->mtx)); + AZ(pthread_cond_destroy(&hp->cond)); +} diff --git a/bin/varnishtest/vtc_server.c b/bin/varnishtest/vtc_server.c index 21fe298..22b9849 100644 --- a/bin/varnishtest/vtc_server.c +++ b/bin/varnishtest/vtc_server.c @@ -38,7 +38,6 @@ #include "vtc.h" -#include "vss.h" #include "vtcp.h" struct server { diff --git a/doc/sphinx/Makefile.am b/doc/sphinx/Makefile.am index f929a6f..c88b46b 100644 --- a/doc/sphinx/Makefile.am +++ b/doc/sphinx/Makefile.am @@ -179,6 +179,7 @@ BUILT_SOURCES += include/vsl-tags.rst VTCSYN_SRC = $(top_srcdir)/bin/varnishtest/vtc.c \ $(top_srcdir)/bin/varnishtest/vtc_barrier.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_varnish.c include/vtc-syntax.rst: vtc-syntax.py $(VTCSYN_SRC) diff --git a/doc/sphinx/vtc-syntax.py b/doc/sphinx/vtc-syntax.py index 45a5102..298f4bb 100644 --- a/doc/sphinx/vtc-syntax.py +++ b/doc/sphinx/vtc-syntax.py @@ -34,13 +34,10 @@ import sys import re -def parse_file(fn): +def parse_file(fn, cl, tl, sl): p = False section = "" resec = re.compile("[ /]\* SECTION: ") - cl = {} - tl = {} - sl = [] f = open(fn, "r") @@ -63,6 +60,12 @@ def parse_file(fn): cl[section].append(re.sub(r"^ \* ?", "", l)) f.close() +if __name__ == "__main__": + cl = {} + tl = {} + sl = [] + for fn in sys.argv[1:]: + parse_file(fn, cl, tl, sl) sl.sort() for section in sl: print(tl[section], end="") @@ -72,11 +75,10 @@ def parse_file(fn): r = "-" elif c == 1: r = "~" - else: + elif c == 2: r = "." + else: + r = "*" print(re.sub(r".", r, tl[section]), end="") print("".join(cl[section])) -if __name__ == "__main__": - for fn in sys.argv[1:]: - parse_file(fn) From phk at FreeBSD.org Tue Aug 30 11:45:12 2016 From: phk at FreeBSD.org (Poul-Henning Kamp) Date: Tue, 30 Aug 2016 13:45:12 +0200 Subject: [master] 66d2682 Add our skeleton HTTP2 implementation, which seems to survive at least trival traffic in real life. Message-ID: commit 66d2682647b8fc00c32f69bb4b1856d7d4dcc30c Author: Poul-Henning Kamp Date: Tue Aug 30 11:43:29 2016 +0000 Add our skeleton HTTP2 implementation, which seems to survive at least trival traffic in real life. diff --git a/bin/varnishd/Makefile.am b/bin/varnishd/Makefile.am index 1a328fb..d26d982 100644 --- a/bin/varnishd/Makefile.am +++ b/bin/varnishd/Makefile.am @@ -58,6 +58,8 @@ varnishd_SOURCES = \ hash/hash_critbit.c \ hash/mgt_hash.c \ hash/hash_simple_list.c \ + hpack/vhp_table.c \ + hpack/vhp_decode.c \ http1/cache_http1_deliver.c \ http1/cache_http1_fetch.c \ http1/cache_http1_fsm.c \ @@ -65,6 +67,11 @@ varnishd_SOURCES = \ http1/cache_http1_pipe.c \ http1/cache_http1_proto.c \ http1/cache_http1_vfp.c \ + http2/cache_http2_deliver.c \ + http2/cache_http2_hpack.c \ + http2/cache_http2_panic.c \ + http2/cache_http2_proto.c \ + http2/cache_http2_send.c \ mgt/mgt_acceptor.c \ mgt/mgt_child.c \ mgt/mgt_cli.c \ @@ -111,7 +118,9 @@ noinst_HEADERS = \ cache/cache_transport.h \ common/heritage.h \ hash/hash_slinger.h \ + hpack/vhp.h \ http1/cache_http1.h \ + http2/cache_http2.h \ mgt/mgt.h \ mgt/mgt_param.h \ storage/storage.h \ @@ -153,6 +162,26 @@ varnishd_LDADD = \ EXTRA_DIST = builtin.vcl DISTCLEANFILES = builtin_vcl.h +noinst_PROGRAMS = vhp_gen_hufdec +vhp_gen_hufdec_SOURCES = hpack/vhp_gen_hufdec.c +vhp_gen_hufdec_CFLAGS = -include config.h +vhp_gen_hufdec_LDADD = \ + $(top_builddir)/lib/libvarnish/libvarnish.la + +noinst_PROGRAMS += vhp_table_test +vhp_table_test_SOURCES = hpack/vhp_table.c +vhp_table_test_CFLAGS = -DTABLE_TEST_DRIVER -include config.h +vhp_table_test_LDADD = \ + $(top_builddir)/lib/libvarnish/libvarnish.la + +noinst_PROGRAMS += vhp_decode_test +vhp_decode_test_SOURCES = hpack/vhp_decode.c hpack/vhp_table.c +vhp_decode_test_CFLAGS = -DDECODE_TEST_DRIVER -include config.h +vhp_decode_test_LDADD = \ + $(top_builddir)/lib/libvarnish/libvarnish.la + +TESTS = vhp_table_test vhp_decode_test + # # Turn the builtin.vcl file into a C-string we can include in the program. # @@ -168,5 +197,12 @@ builtin_vcl.h: builtin.vcl -e 's/$$/\\n"/' \ -e 's/^/ "/' $(srcdir)/builtin.vcl >> $@ +vhp_hufdec.h: vhp_gen_hufdec + $(AM_V_GEN) ./vhp_gen_hufdec > vhp_hufdec.h_ + mv vhp_hufdec.h_ vhp_hufdec.h + +BUILT_SOURCES = vhp_hufdec.h +DISTCLEANFILES = vhp_hufdec.h + # Explicitly record dependency mgt/mgt_vcc.c: builtin_vcl.h diff --git a/bin/varnishd/cache/cache.h b/bin/varnishd/cache/cache.h index 923a8d2..57f260c 100644 --- a/bin/varnishd/cache/cache.h +++ b/bin/varnishd/cache/cache.h @@ -816,6 +816,9 @@ extern const char H__Status[]; extern const char H__Proto[]; extern const char H__Reason[]; +/* cache_http2_deliver.c */ +void V2D_Init(void); + /* cache_main.c */ #define VXID(u) ((u) & VSL_IDENTMASK) uint32_t VXID_Get(struct worker *, uint32_t marker); diff --git a/bin/varnishd/cache/cache_acceptor.c b/bin/varnishd/cache/cache_acceptor.c index 58481d5..d1f001a 100644 --- a/bin/varnishd/cache/cache_acceptor.c +++ b/bin/varnishd/cache/cache_acceptor.c @@ -632,6 +632,7 @@ XPORT_Init(void) VTAILQ_INSERT_TAIL(&transports, &PROXY_transport, list); VTAILQ_INSERT_TAIL(&transports, &HTTP1_transport, list); + VTAILQ_INSERT_TAIL(&transports, &H2_transport, list); n = 0; VTAILQ_FOREACH(xp, &transports, list) diff --git a/bin/varnishd/cache/cache_main.c b/bin/varnishd/cache/cache_main.c index 6e67968..7c076e2 100644 --- a/bin/varnishd/cache/cache_main.c +++ b/bin/varnishd/cache/cache_main.c @@ -241,6 +241,7 @@ child_main(void) VBE_InitCfg(); Pool_Init(); V1P_Init(); + V2D_Init(); EXP_Init(); HSH_Init(heritage.hash); diff --git a/bin/varnishd/cache/cache_transport.h b/bin/varnishd/cache/cache_transport.h index ef57325..2633cfe 100644 --- a/bin/varnishd/cache/cache_transport.h +++ b/bin/varnishd/cache/cache_transport.h @@ -66,6 +66,8 @@ struct transport { extern struct transport PROXY_transport; extern struct transport HTTP1_transport; +extern struct transport H2_transport; +htc_complete_f H2_prism_complete; const struct transport *XPORT_ByNumber(uint16_t no); void VPX_Send_Proxy(int fd, int version, const struct sess *); diff --git a/bin/varnishd/flint.sh b/bin/varnishd/flint.sh index f96d0fc..ba15504 100755 --- a/bin/varnishd/flint.sh +++ b/bin/varnishd/flint.sh @@ -23,6 +23,7 @@ flexelint \ common/*.c \ hash/*.c \ http1/*.c \ + http2/*.c \ mgt/*.c \ proxy/*.c \ storage/*.c \ diff --git a/bin/varnishd/hpack/vhp.h b/bin/varnishd/hpack/vhp.h new file mode 100644 index 0000000..0f9852d --- /dev/null +++ b/bin/varnishd/hpack/vhp.h @@ -0,0 +1,129 @@ +/*- + * Copyright (c) 2016 Varnish Software + * All rights reserved. + * + * Author: Martin Blix Grydeland + * + * 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 + +/* VHT - Varnish HPACK Table */ + +#define VHT_ENTRY_SIZE 32U + +struct vht_entry { + unsigned magic; +#define VHT_ENTRY_MAGIC 0xc06dd892 + unsigned offset; + unsigned namelen; + unsigned valuelen; +}; + +struct vht_table { + unsigned magic; +#define VHT_TABLE_MAGIC 0x6bbdc683 + unsigned n; + unsigned size; + unsigned maxsize; /* n * 32 + size <= maxsize */ + unsigned protomax; + unsigned bufsize; + char *buf; +}; + +void VHT_NewEntry(struct vht_table *); +int VHT_NewEntry_Indexed(struct vht_table *, unsigned); +void VHT_AppendName(struct vht_table *, const char *, ssize_t); +void VHT_AppendValue(struct vht_table *, const char *, ssize_t); +int VHT_SetMaxTableSize(struct vht_table *, size_t); +int VHT_SetProtoMax(struct vht_table *, size_t); +const char *VHT_LookupName(const struct vht_table *, unsigned, size_t *); +const char *VHT_LookupValue(const struct vht_table *, unsigned, size_t *); +int VHT_Init(struct vht_table *, size_t); +void VHT_Fini(struct vht_table *); + +/* VHD - Varnish HPACK Decoder */ + +enum vhd_ret_e { +#define VHD_RET(NAME, VAL, DESC) \ + VHD_##NAME = VAL, +#include "tbl/vhd_return.h" +#undef VHD_RET +}; + +struct vhd_int { + uint8_t magic; +#define VHD_INT_MAGIC 0x05 + + uint8_t pfx; + uint8_t m; + unsigned v; +}; + +struct vhd_raw { + uint8_t magic; +#define VHD_RAW_MAGIC 0xa0 + + unsigned l; +}; + +struct vhd_huffman { + uint8_t magic; +#define VHD_HUFFMAN_MAGIC 0x56 + + uint8_t blen; + uint16_t bits; + uint16_t pos; + unsigned len; +}; + +struct vhd_lookup { + uint8_t magic; +#define VHD_LOOKUP_MAGIC 0x65 + + unsigned l; +}; + +struct vhd_decode { + unsigned magic; +#define VHD_DECODE_MAGIC 0x9cbc72b2 + + unsigned index; + uint16_t state; + int8_t error; + uint8_t first; + + union { + struct vhd_int integer[1]; + struct vhd_lookup lookup[1]; + struct vhd_raw raw[1]; + struct vhd_huffman huffman[1]; + }; +}; + +void VHD_Init(struct vhd_decode *); +enum vhd_ret_e VHD_Decode(struct vhd_decode *, struct vht_table *, + const uint8_t *in, size_t inlen, size_t *p_inused, + char *out, size_t outlen, size_t *p_outused); +const char *VHD_Error(enum vhd_ret_e); diff --git a/bin/varnishd/hpack/vhp_decode.c b/bin/varnishd/hpack/vhp_decode.c new file mode 100644 index 0000000..334db2d --- /dev/null +++ b/bin/varnishd/hpack/vhp_decode.c @@ -0,0 +1,1218 @@ +/*- + * Copyright (c) 2016 Varnish Software + * All rights reserved. + * + * Author: Martin Blix Grydeland + * Author: Dridi Boukelmoune + * + * 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 "miniobj.h" + +#include "hpack/vhp.h" + +#include "vhp_hufdec.h" + +struct vhd_ctx { + struct vhd_decode *d; + struct vht_table *tbl; + const uint8_t *in; + const uint8_t *in_e; + char *out; + char *out_e; +}; + +typedef enum vhd_ret_e vhd_state_f(struct vhd_ctx *ctx, unsigned first); + +/* Function flags */ +#define VHD_INCREMENTAL (1U << 0) + +/* Functions */ +enum vhd_func_e { +#define VHD_FSM_FUNC(NAME, func) \ + VHD_F_##NAME, +#include "tbl/vhd_fsm_funcs.h" +#undef VHD_FSM_FUNC + VHD_F__MAX, +}; +#define VHD_FSM_FUNC(NAME, func) \ + static vhd_state_f func; +#include "tbl/vhd_fsm_funcs.h" +#undef VHD_FSM_FUNC + +/* States */ +enum vhd_state_e { +#define VHD_FSM(STATE, FUNC, arg1, arg2) \ + VHD_S_##STATE, +#include "tbl/vhd_fsm.h" +#undef VHD_FSM + VHD_S__MAX, +}; +static const struct vhd_state { + const char *name; + enum vhd_func_e func; + unsigned arg1; + unsigned arg2; +} vhd_states[VHD_S__MAX] = { +#define VHD_FSM(STATE, FUNC, arg1, arg2) \ + [VHD_S_##STATE] = { #STATE, VHD_F_##FUNC, arg1, arg2 }, +#include "tbl/vhd_fsm.h" +#undef VHD_FSM +}; + +/* Utility functions */ +static void +vhd_set_state(struct vhd_decode *d, enum vhd_state_e state) +{ + AN(d); + assert(state >= 0 && state < VHD_S__MAX); + d->state = state; + d->first = 1; +} + +static void +vhd_next_state(struct vhd_decode *d) +{ + AN(d); + assert(d->state + 1 < VHD_S__MAX); + vhd_set_state(d, d->state + 1); +} + +/* State functions */ +static enum vhd_ret_e __match_proto__(vhd_state_f) +vhd_skip(struct vhd_ctx *ctx, unsigned first) +{ + AN(ctx); + AN(first); + vhd_next_state(ctx->d); + return (VHD_AGAIN); +} + +static enum vhd_ret_e __match_proto__(vhd_state_f) +vhd_goto(struct vhd_ctx *ctx, unsigned first) +{ + const struct vhd_state *s; + + AN(ctx); + AN(first); + assert(ctx->d->state < VHD_S__MAX); + s = &vhd_states[ctx->d->state]; + assert(s->arg1 < VHD_S__MAX); + vhd_set_state(ctx->d, s->arg1); + return (VHD_AGAIN); +} + +static enum vhd_ret_e __match_proto__(vhd_state_f) +vhd_idle(struct vhd_ctx *ctx, unsigned first) +{ + uint8_t c; + + AN(ctx); + (void)first; + + while (ctx->in < ctx->in_e) { + c = *ctx->in; + if ((c & 0x80) == 0x80) + vhd_set_state(ctx->d, VHD_S_HP61_START); + else if ((c & 0xc0) == 0x40) + vhd_set_state(ctx->d, VHD_S_HP621_START); + else if ((c & 0xf0) == 0x00) + vhd_set_state(ctx->d, VHD_S_HP622_START); + else if ((c & 0xf0) == 0x10) + vhd_set_state(ctx->d, VHD_S_HP623_START); + else if ((c & 0xe0) == 0x20) + vhd_set_state(ctx->d, VHD_S_HP63_START); + else + return (VHD_ERR_ARG); + return (VHD_AGAIN); + } + + return (VHD_OK); +} + +static enum vhd_ret_e __match_proto__(vhd_state_f) +vhd_integer(struct vhd_ctx *ctx, unsigned first) +{ + const struct vhd_state *s; + struct vhd_int *i; + uint8_t c; + unsigned mask; + + assert(UINT_MAX >= UINT32_MAX); + + AN(ctx); + assert(ctx->d->state < VHD_S__MAX); + s = &vhd_states[ctx->d->state]; + i = ctx->d->integer; + + if (first) { + INIT_OBJ(i, VHD_INT_MAGIC); + i->pfx = s->arg1; + assert(i->pfx >= 4 && i->pfx <= 7); + } + CHECK_OBJ_NOTNULL(i, VHD_INT_MAGIC); + + while (ctx->in < ctx->in_e) { + c = *ctx->in; + ctx->in++; + if (i->pfx) { + mask = (1U << i->pfx) - 1; + i->pfx = 0; + i->v = c & mask; + if (i->v < mask) { + vhd_next_state(ctx->d); + return (VHD_AGAIN); + } + } else { + if ((i->m == 28 && (c & 0x78)) || i->m > 28) + return (VHD_ERR_INT); + i->v += (c & 0x7f) * ((uint32_t)1 << i->m); + i->m += 7; + if (!(c & 0x80)) { + vhd_next_state(ctx->d); + return (VHD_AGAIN); + } + } + } + return (VHD_MORE); +} + +static enum vhd_ret_e __match_proto__(vhd_state_f) +vhd_set_max(struct vhd_ctx *ctx, unsigned first) +{ + AN(ctx); + AN(first); + CHECK_OBJ_NOTNULL(ctx->d->integer, VHD_INT_MAGIC); + if (ctx->tbl == NULL) + return (VHD_ERR_UPD); + if (VHT_SetMaxTableSize(ctx->tbl, ctx->d->integer->v)) + return (VHD_ERR_UPD); + vhd_next_state(ctx->d); + return (VHD_AGAIN); +} + +static enum vhd_ret_e __match_proto__(vhd_state_f) +vhd_set_idx(struct vhd_ctx *ctx, unsigned first) +{ + AN(ctx); + AN(first); + CHECK_OBJ_NOTNULL(ctx->d->integer, VHD_INT_MAGIC); + ctx->d->index = ctx->d->integer->v; + vhd_next_state(ctx->d); + return (VHD_AGAIN); +} + +static enum vhd_ret_e __match_proto__(vhd_state_f) +vhd_lookup(struct vhd_ctx *ctx, unsigned first) +{ + const struct vhd_state *s; + struct vhd_lookup *lu; + const char *p; + size_t l; + + AN(ctx); + assert(ctx->d->state < VHD_S__MAX); + s = &vhd_states[ctx->d->state]; + lu = ctx->d->lookup; + + if (first) + INIT_OBJ(lu, VHD_LOOKUP_MAGIC); + CHECK_OBJ_NOTNULL(lu, VHD_LOOKUP_MAGIC); + + switch (s->arg1) { + case VHD_NAME: + case VHD_NAME_SEC: + p = VHT_LookupName(ctx->tbl, ctx->d->index, &l); + break; + case VHD_VALUE: + case VHD_VALUE_SEC: + p = VHT_LookupValue(ctx->tbl, ctx->d->index, &l); + break; + default: + WRONG("vhd_lookup wrong arg1"); + break; + } + if (first && p == NULL) + return (VHD_ERR_IDX); + AN(p); + assert(l <= UINT_MAX); + if (first) + lu->l = l; + + assert(lu->l <= l); + p += l - lu->l; + l = lu->l; + if (l > ctx->out_e - ctx->out) + l = ctx->out_e - ctx->out; + memcpy(ctx->out, p, l); + ctx->out += l; + lu->l -= l; + + if (lu->l == 0) { + vhd_next_state(ctx->d); + return (s->arg1); + } + assert(ctx->out == ctx->out_e); + return (VHD_BUF); +} + +static enum vhd_ret_e __match_proto__(vhd_state_f) +vhd_new(struct vhd_ctx *ctx, unsigned first) +{ + AN(ctx); + AN(first); + if (ctx->tbl != NULL) + VHT_NewEntry(ctx->tbl); + vhd_next_state(ctx->d); + return (VHD_AGAIN); +} + +static enum vhd_ret_e __match_proto__(vhd_state_f) +vhd_new_idx(struct vhd_ctx *ctx, unsigned first) +{ + AN(ctx); + AN(first); + if (ctx->tbl != NULL) { + if (VHT_NewEntry_Indexed(ctx->tbl, ctx->d->index)) + return (VHD_ERR_IDX); + } + vhd_next_state(ctx->d); + return (VHD_AGAIN); +} + +static enum vhd_ret_e __match_proto__(vhd_state_f) +vhd_branch_zidx(struct vhd_ctx *ctx, unsigned first) +{ + const struct vhd_state *s; + + AN(ctx); + (void)first; + assert(ctx->d->state < VHD_S__MAX); + s = &vhd_states[ctx->d->state]; + assert(s->arg1 < VHD_S__MAX); + + if (ctx->d->index == 0) + vhd_set_state(ctx->d, s->arg1); + else + vhd_next_state(ctx->d); + return (VHD_AGAIN); +} + +static enum vhd_ret_e __match_proto__(vhd_state_f) +vhd_branch_bit0(struct vhd_ctx *ctx, unsigned first) +{ + const struct vhd_state *s; + + AN(ctx); + (void)first; + assert(ctx->d->state < VHD_S__MAX); + s = &vhd_states[ctx->d->state]; + assert(s->arg1 < VHD_S__MAX); + + if (ctx->in == ctx->in_e) + return (VHD_MORE); + + if (*ctx->in & 0x80) + vhd_set_state(ctx->d, s->arg1); + else + vhd_next_state(ctx->d); + return (VHD_AGAIN); +} + +static enum vhd_ret_e __match_proto__(vhd_state_f) +vhd_raw(struct vhd_ctx *ctx, unsigned first) +{ + const struct vhd_state *s; + struct vhd_raw *raw; + size_t l2; + + AN(ctx); + assert(ctx->d->state < VHD_S__MAX); + s = &vhd_states[ctx->d->state]; + + raw = ctx->d->raw; + if (first) { + CHECK_OBJ_NOTNULL(ctx->d->integer, VHD_INT_MAGIC); + l2 = ctx->d->integer->v; + INIT_OBJ(raw, VHD_RAW_MAGIC); + raw->l = l2; + } + CHECK_OBJ_NOTNULL(raw, VHD_RAW_MAGIC); + + while (raw->l > 0) { + l2 = raw->l; + if (l2 > (ctx->in_e - ctx->in)) + l2 = ctx->in_e - ctx->in; + if (l2 == 0) + return (VHD_MORE); + if (l2 > (ctx->out_e - ctx->out)) + l2 = ctx->out_e - ctx->out; + if (l2 == 0) + return (VHD_BUF); + memcpy(ctx->out, ctx->in, l2); + ctx->in += l2; + if (ctx->tbl != NULL && (s->arg2 & VHD_INCREMENTAL)) { + switch (s->arg1) { + case VHD_NAME: + VHT_AppendName(ctx->tbl, ctx->out, l2); + break; + case VHD_VALUE: + VHT_AppendValue(ctx->tbl, ctx->out, l2); + break; + default: + WRONG("vhd_raw wrong arg1"); + break; + } + } + ctx->out += l2; + raw->l -= l2; + } + vhd_next_state(ctx->d); + return (s->arg1); +} + +static enum vhd_ret_e __match_proto__(vhd_state_f) +vhd_huffman(struct vhd_ctx *ctx, unsigned first) +{ + const struct vhd_state *s; + struct vhd_huffman *huf; + enum vhd_ret_e r; + unsigned u, l; + + AN(ctx); + assert(ctx->d->state < VHD_S__MAX); + s = &vhd_states[ctx->d->state]; + + huf = ctx->d->huffman; + if (first) { + CHECK_OBJ_NOTNULL(ctx->d->integer, VHD_INT_MAGIC); + l = ctx->d->integer->v; + INIT_OBJ(huf, VHD_HUFFMAN_MAGIC); + huf->len = l; + } + CHECK_OBJ_NOTNULL(huf, VHD_HUFFMAN_MAGIC); + + r = VHD_OK; + l = 0; + while (1) { + assert(huf->pos < HUFDEC_LEN); + assert(hufdec[huf->pos].mask > 0); + assert(hufdec[huf->pos].mask <= 8); + + if (huf->len > 0 && huf->blen < hufdec[huf->pos].mask) { + /* Refill from input */ + if (ctx->in == ctx->in_e) { + r = VHD_MORE; + break; + } + huf->bits = (huf->bits << 8) | *ctx->in; + huf->blen += 8; + huf->len--; + ctx->in++; + } + + if (huf->len == 0 && huf->pos == 0 && huf->blen <= 7 && + huf->bits == (1U << huf->blen) - 1U) { + /* End of stream */ + r = s->arg1; + vhd_next_state(ctx->d); + break; + } + + if (ctx->out + l == ctx->out_e) { + r = VHD_BUF; + break; + } + + if (huf->blen >= hufdec[huf->pos].mask) + u = huf->bits >> (huf->blen - hufdec[huf->pos].mask); + else + u = huf->bits << (hufdec[huf->pos].mask - huf->blen); + huf->pos += u; + assert(huf->pos < HUFDEC_LEN); + + if (hufdec[huf->pos].len == 0 || + hufdec[huf->pos].len > huf->blen) { + /* Invalid or incomplete code */ + r = VHD_ERR_HUF; + break; + } + + huf->blen -= hufdec[huf->pos].len; + huf->bits &= (1U << huf->blen) - 1U; + + if (hufdec[huf->pos].jump) { + huf->pos += hufdec[huf->pos].jump; + assert(huf->pos < HUFDEC_LEN); + } else { + ctx->out[l++] = hufdec[huf->pos].chr; + huf->pos = 0; + } + } + + if (l > 0 && ctx->tbl != NULL && (s->arg2 & VHD_INCREMENTAL)) { + switch (s->arg1) { + case VHD_NAME: + VHT_AppendName(ctx->tbl, ctx->out, l); + break; + case VHD_VALUE: + VHT_AppendValue(ctx->tbl, ctx->out, l); + break; + default: + WRONG("vhd_raw wrong arg1"); + break; + } + } + ctx->out += l; + + assert(r != VHD_OK); + return (r); +} + +/* Public interface */ + +const char * +VHD_Error(enum vhd_ret_e r) +{ + switch (r) { +#define VHD_RET(NAME, VAL, DESC) \ + case VHD_##NAME: \ + return ("VHD_" #NAME " (" DESC ")"); +#include "tbl/vhd_return.h" +#undef VHD_RET + default: + return ("VHD_UNKNOWN"); + } +} + +enum vhd_ret_e +VHD_Decode(struct vhd_decode *d, struct vht_table *tbl, + const uint8_t *in, size_t inlen, size_t *p_inused, + char *out, size_t outlen, size_t *p_outused) +{ + const struct vhd_state *s; + struct vhd_ctx ctx[1]; + enum vhd_ret_e ret; + unsigned first; + + CHECK_OBJ_NOTNULL(d, VHD_DECODE_MAGIC); + CHECK_OBJ_ORNULL(tbl, VHT_TABLE_MAGIC); + AN(in); + AN(p_inused); + AN(out); + AN(p_outused); + + if (d->error < 0) + return (d->error); + + assert(*p_inused <= inlen); + assert(*p_outused <= outlen); + + ctx->d = d; + ctx->tbl = tbl; + ctx->in = in + *p_inused; + ctx->in_e = in + inlen; + ctx->out = out + *p_outused; + ctx->out_e = out + outlen; + + do { + first = d->first; + d->first = 0; + assert(d->state < VHD_S__MAX); + s = &vhd_states[d->state]; + switch (s->func) { +#define VHD_FSM_FUNC(NAME, func) \ + case VHD_F_##NAME: \ + ret = func(ctx, first); \ + break; +#include "tbl/vhd_fsm_funcs.h" +#undef VHD_FSM_FUNC + default: + WRONG("Undefined vhd function"); + break; + } + } while (ret == VHD_AGAIN); + + if (ret < 0) + d->error = ret; + + assert(in + *p_inused <= ctx->in); + *p_inused += ctx->in - (in + *p_inused); + assert(out + *p_outused <= ctx->out); + *p_outused += ctx->out - (out + *p_outused); + + return (ret); +} + +void +VHD_Init(struct vhd_decode *d) +{ + + AN(d); + assert(VHD_S__MAX <= UINT16_MAX); + assert(HUFDEC_LEN <= UINT16_MAX); + INIT_OBJ(d, VHD_DECODE_MAGIC); + d->state = VHD_S_IDLE; + d->first = 1; +} + +/* Test driver */ + +#ifdef DECODE_TEST_DRIVER + +#include +#include + +static int verbose = 0; + +static size_t +hexbuf(uint8_t *buf, size_t buflen, const char *h) +{ + size_t l; + uint8_t u; + + AN(h); + AN(buf); + + l = 0; + for (; *h != '\0'; h++) { + if (l == buflen * 2) + WRONG("Too small buffer"); + if (isspace(*h)) + continue; + if (*h >= '0' && *h <= '9') + u = *h - '0'; + else if (*h >= 'a' && *h <= 'f') + u = 0xa + *h - 'a'; + else if (*h >= 'A' && *h <= 'F') + u = 0xa + *h - 'A'; + else + WRONG("Bad input character"); + assert(u <= 0xf); + if (l % 2 == 0) { + u <<= 4; + buf[l / 2] = u; + } else { + buf[l / 2] |= u; + } + l++; + } + AZ(l % 2); + return (l / 2); +} + +static int +match(const char *b, size_t l, ...) +{ + va_list ap; + const char *e; + const char *m; + int r = 0; + + va_start(ap, l); + e = b + l; + while (1) { + m = va_arg(ap, const char *); + if (m == NULL) + break; + l = strlen(m); + if (e - b <= l || b[l] != '\0' || strncmp(b, m, l)) { + printf("%.*s != %s\n", (int)(e - b), b, m); + r = -1; + break; + } else if (verbose) { + printf("%s == %s\n", b, m); + } + b += l + 1; + } + va_end(ap); + return (r); +} + +#define M_1IN (1U << 0) +#define M_1OUT (1U << 1) + +static enum vhd_ret_e +decode(struct vhd_decode *d, struct vht_table *tbl, uint8_t *in, size_t in_l, + char *out, size_t out_l, unsigned m) +{ + size_t in_u, out_u; + enum vhd_ret_e r; + + CHECK_OBJ_NOTNULL(d, VHD_DECODE_MAGIC); + AN(in); + AN(out); + + in_u = 0; + out_u = 0; + + while (1) { + r = VHD_Decode(d, tbl, in, + (m & M_1IN ? (in_l > in_u ? in_u + 1 : in_u) : in_l), + &in_u, + out, + (m & M_1OUT ? (out_l > out_u ? out_u + 1 : out_u) : out_l), + &out_u); + assert(in_u <= in_l); + assert(out_u <= out_l); + if (r < VHD_OK) + return (r); + + switch (r) { + case VHD_OK: + return (r); + + case VHD_MORE: + if (in_u == in_l) + return (r); + break; + + case VHD_BUF: + if (out_u == out_l) + return (r); + break; + + case VHD_NAME: + case VHD_NAME_SEC: + assert(out_l - out_u > 0); + out[out_u++] = '\0'; + if (verbose) + printf("Name%s: '%s'\n", + (r == VHD_NAME_SEC ? " (sec)" : ""), + out); + out += out_u; + out_l -= out_u; + out_u = 0; + break; + + case VHD_VALUE: + case VHD_VALUE_SEC: + assert(out_l - out_u > 0); + out[out_u++] = '\0'; + if (verbose) + printf("Value%s: '%s'\n", + (r == VHD_VALUE_SEC ? " (sec)" : ""), + out); + out += out_u; + out_l -= out_u; + out_u = 0; + break; + + default: + WRONG("Wrong return code"); + break; + } + } + + NEEDLESS_RETURN(0); +} + +#define CHECK_RET(r, e) \ + do { \ + if (verbose || r != e) { \ + printf("%s %s %s\n", \ + VHD_Error(r), \ + (r == e ? "==" : "!="), \ + VHD_Error(e)); \ + } \ + assert(r == e); \ + } while (0) + +#define CHECK_INT(d, u) \ + do { \ + CHECK_OBJ_NOTNULL(d->integer, VHD_INT_MAGIC); \ + if (verbose || d->integer->v != u) { \ + printf("%u %s %u\n", d->integer->v, \ + (d->integer->v == u ? "==" : "!="), \ + u); \ + } \ + assert(d->integer->v == u); \ + } while (0) + +static void +test_integer(unsigned mode) +{ + struct vhd_decode d[1]; + uint8_t in[128]; + size_t in_l; + char out[128]; + enum vhd_ret_e r; + + /* Test single byte decoding */ + VHD_Init(d); + vhd_set_state(d, VHD_S_TEST_INT5); + in_l = hexbuf(in, sizeof in, "1e"); + r = decode(d, NULL, in, in_l, out, sizeof out, mode); + CHECK_RET(r, VHD_OK); + CHECK_INT(d, 30); + + /* Test multibyte decoding */ + VHD_Init(d); + vhd_set_state(d, VHD_S_TEST_INT5); + in_l = hexbuf(in, sizeof in, "ff 9a 0a"); + r = decode(d, NULL, in, in_l, out, sizeof out, mode); + CHECK_RET(r, VHD_OK); + CHECK_INT(d, 1337); + + /* Test max size we allow */ + VHD_Init(d); + vhd_set_state(d, VHD_S_TEST_INT5); + in_l = hexbuf(in, sizeof in, "1f ff ff ff ff 07"); + r = decode(d, NULL, in, in_l, out, sizeof out, mode); + CHECK_RET(r, VHD_OK); + CHECK_INT(d, 0x8000001E); + + /* Test overflow */ + VHD_Init(d); + vhd_set_state(d, VHD_S_TEST_INT5); + in_l = hexbuf(in, sizeof in, "1f ff ff ff ff 08"); + r = decode(d, NULL, in, in_l, out, sizeof out, mode); + CHECK_RET(r, VHD_ERR_INT); +} + +static void +test_raw(unsigned mode) +{ + struct vhd_decode d[1]; + uint8_t in[128]; + size_t in_l; + char out[128]; + enum vhd_ret_e r; + + /* Test raw encoding */ + VHD_Init(d); + vhd_set_state(d, VHD_S_TEST_LITERAL); + in_l = hexbuf(in, sizeof in, + "0a63 7573 746f 6d2d 6b65 790d 6375 7374 6f6d 2d68 6561 6465 72"); + r = decode(d, NULL, in, in_l, out, sizeof out, mode); + CHECK_RET(r, VHD_OK); + AZ(match(out, sizeof out, "custom-key", "custom-header", NULL)); + + /* Test too short input */ + VHD_Init(d); + vhd_set_state(d, VHD_S_TEST_LITERAL); + in_l = hexbuf(in, sizeof in, + "02"); + r = decode(d, NULL, in, in_l, out, sizeof out, mode); + CHECK_RET(r, VHD_MORE); +} + +static void +test_huffman(unsigned mode) +{ + struct vhd_decode d[1]; + uint8_t in[256]; + size_t in_l; + char out[256]; + enum vhd_ret_e r; + + /* Decode a huffman encoded value */ + VHD_Init(d); + in_l = hexbuf(in, sizeof in, + "0141 8cf1 e3c2 e5f2 3a6b a0ab 90f4 ff"); + vhd_set_state(d, VHD_S_TEST_LITERAL); + r = decode(d, NULL, in, in_l, out, sizeof out, mode); + CHECK_RET(r, VHD_OK); + AZ(match(out, sizeof out, "A", "www.example.com", NULL)); + + /* Decode an incomplete input buffer */ + VHD_Init(d); + in_l = hexbuf(in, sizeof in, + "0141 81"); + vhd_set_state(d, VHD_S_TEST_LITERAL); + r = decode(d, NULL, in, in_l, out, sizeof out, mode); + CHECK_RET(r, VHD_MORE); + + /* Decode an incomplete huffman code */ + VHD_Init(d); + in_l = hexbuf(in, sizeof in, + "0141 81 fe"); + vhd_set_state(d, VHD_S_TEST_LITERAL); + r = decode(d, NULL, in, in_l, out, sizeof out, mode); + CHECK_RET(r, VHD_ERR_HUF); + + /* Decode an invalid huffman code */ + VHD_Init(d); + in_l = hexbuf(in, sizeof in, + "0141 84 ff ff ff ff"); + vhd_set_state(d, VHD_S_TEST_LITERAL); + r = decode(d, NULL, in, in_l, out, sizeof out, mode); + CHECK_RET(r, VHD_ERR_HUF); +} + +static void +test_c2(unsigned mode) +{ + struct vhd_decode d[1]; + uint8_t in[256]; + size_t in_l; + char out[256]; + enum vhd_ret_e r; + + /* See RFC 7541 Appendix C.2 */ + + VHD_Init(d); + + /* C.2.1 */ + in_l = hexbuf(in, sizeof in, + "400a 6375 7374 6f6d 2d6b 6579 0d63 7573" + "746f 6d2d 6865 6164 6572"); + r = decode(d, NULL, in, in_l, out, sizeof out, mode); + CHECK_RET(r, VHD_OK); + AZ(match(out, sizeof out, + "custom-key", "custom-header", + NULL)); + + /* C.2.2 */ + in_l = hexbuf(in, sizeof in, + "040c 2f73 616d 706c 652f 7061 7468"); + r = decode(d, NULL, in, in_l, out, sizeof out, mode); + CHECK_RET(r, VHD_OK); + AZ(match(out, sizeof out, + ":path", "/sample/path", + NULL)); + + /* C.2.3 */ + in_l = hexbuf(in, sizeof in, + "1008 7061 7373 776f 7264 0673 6563 7265" + "74"); + r = decode(d, NULL, in, in_l, out, sizeof out, mode); + CHECK_RET(r, VHD_OK); + AZ(match(out, sizeof out, + "password", "secret", + NULL)); + + /* C.2.4 */ + in_l = hexbuf(in, sizeof in, + "82"); + r = decode(d, NULL, in, in_l, out, sizeof out, mode); + CHECK_RET(r, VHD_OK); + AZ(match(out, sizeof out, + ":method", "GET", + NULL)); +} + +static void +test_c3(unsigned mode) +{ + struct vht_table t[1]; + struct vhd_decode d[1]; + uint8_t in[256]; + size_t in_l; + char out[256]; + enum vhd_ret_e r; + + /* See RFC 7541 Appendix C.3 */ + + AZ(VHT_Init(t, 4096)); + VHD_Init(d); + + /* C.3.1 */ + in_l = hexbuf(in, sizeof in, + "8286 8441 0f77 7777 2e65 7861 6d70 6c65" + "2e63 6f6d"); + r = decode(d, t, in, in_l, out, sizeof out, mode); + CHECK_RET(r, VHD_OK); + AZ(match(out, sizeof out, + ":method", "GET", + ":scheme", "http", + ":path", "/", + ":authority", "www.example.com", + NULL)); + + /* C.3.2 */ + in_l = hexbuf(in, sizeof in, + "8286 84be 5808 6e6f 2d63 6163 6865"); + r = decode(d, t, in, in_l, out, sizeof out, mode); + CHECK_RET(r, VHD_OK); + AZ(match(out, sizeof out, + ":method", "GET", + ":scheme", "http", + ":path", "/", + ":authority", "www.example.com", + "cache-control", "no-cache", + NULL)); + + /* C.3.3 */ + in_l = hexbuf(in, sizeof in, + "8287 85bf 400a 6375 7374 6f6d 2d6b 6579" + "0c63 7573 746f 6d2d 7661 6c75 65"); + r = decode(d, t, in, in_l, out, sizeof out, mode); + CHECK_RET(r, VHD_OK); + AZ(match(out, sizeof out, + ":method", "GET", + ":scheme", "https", + ":path", "/index.html", + ":authority", "www.example.com", + "custom-key", "custom-value", + NULL)); + + VHT_Fini(t); +} + +static void +test_c4(unsigned mode) +{ + struct vht_table t[1]; + struct vhd_decode d[1]; + uint8_t in[256]; + size_t in_l; + char out[256]; + enum vhd_ret_e r; + + /* See RFC 7541 Appendix C.4 */ + + AZ(VHT_Init(t, 4096)); + VHD_Init(d); + + /* C.4.1 */ + in_l = hexbuf(in, sizeof in, + "8286 8441 8cf1 e3c2 e5f2 3a6b a0ab 90f4 ff"); + r = decode(d, t, in, in_l, out, sizeof out, mode); + CHECK_RET(r, VHD_OK); + AZ(match(out, sizeof out, + ":method", "GET", + ":scheme", "http", + ":path", "/", + ":authority", "www.example.com", + NULL)); + + /* C.4.2 */ + in_l = hexbuf(in, sizeof in, + "8286 84be 5886 a8eb 1064 9cbf"); + r = decode(d, t, in, in_l, out, sizeof out, mode); + CHECK_RET(r, VHD_OK); + AZ(match(out, sizeof out, + ":method", "GET", + ":scheme", "http", + ":path", "/", + ":authority", "www.example.com", + "cache-control", "no-cache", + NULL)); + + /* C.4.3 */ + in_l = hexbuf(in, sizeof in, + "8287 85bf 4088 25a8 49e9 5ba9 7d7f 8925" + "a849 e95b b8e8 b4bf"); + r = decode(d, t, in, in_l, out, sizeof out, mode); + CHECK_RET(r, VHD_OK); + AZ(match(out, sizeof out, + ":method", "GET", + ":scheme", "https", + ":path", "/index.html", + ":authority", "www.example.com", + "custom-key", "custom-value", + NULL)); + + VHT_Fini(t); +} + +static void +test_c5(unsigned mode) +{ + struct vht_table t[1]; + struct vhd_decode d[1]; + uint8_t in[256]; + size_t in_l; + char out[256]; + enum vhd_ret_e r; + + /* See RFC 7541 Appendix C.5 */ + + AZ(VHT_Init(t, 256)); + VHD_Init(d); + + /* C.5.1 */ + in_l = hexbuf(in, sizeof in, + "4803 3330 3258 0770 7269 7661 7465 611d" + "4d6f 6e2c 2032 3120 4f63 7420 3230 3133" + "2032 303a 3133 3a32 3120 474d 546e 1768" + "7474 7073 3a2f 2f77 7777 2e65 7861 6d70" + "6c65 2e63 6f6d"); + r = decode(d, t, in, in_l, out, sizeof out, mode); + CHECK_RET(r, VHD_OK); + AZ(match(out, sizeof out, + ":status", "302", + "cache-control", "private", + "date", "Mon, 21 Oct 2013 20:13:21 GMT", + "location", "https://www.example.com", + NULL)); + + /* C.5.2 */ + in_l = hexbuf(in, sizeof in, + "4803 3330 37c1 c0bf"); + r = decode(d, t, in, in_l, out, sizeof out, mode); + CHECK_RET(r, VHD_OK); + AZ(match(out, sizeof out, + ":status", "307", + "cache-control", "private", + "date", "Mon, 21 Oct 2013 20:13:21 GMT", + "location", "https://www.example.com", + NULL)); + + /* C.5.3 */ + in_l = hexbuf(in, sizeof in, + "88c1 611d 4d6f 6e2c 2032 3120 4f63 7420" + "3230 3133 2032 303a 3133 3a32 3220 474d" + "54c0 5a04 677a 6970 7738 666f 6f3d 4153" + "444a 4b48 514b 425a 584f 5157 454f 5049" + "5541 5851 5745 4f49 553b 206d 6178 2d61" + "6765 3d33 3630 303b 2076 6572 7369 6f6e" + "3d31"); + r = decode(d, t, in, in_l, out, sizeof out, mode); + CHECK_RET(r, VHD_OK); + AZ(match(out, sizeof out, + ":status", "200", + "cache-control", "private", + "date", "Mon, 21 Oct 2013 20:13:22 GMT", + "location", "https://www.example.com", + "content-encoding", "gzip", + "set-cookie", + "foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU; max-age=3600; version=1", + NULL)); + + VHT_Fini(t); +} + +static void +test_c6(unsigned mode) +{ + struct vht_table t[1]; + struct vhd_decode d[1]; + uint8_t in[256]; + size_t in_l; + char out[256]; + enum vhd_ret_e r; + + /* See RFC 7541 Appendix C.6 */ + + AZ(VHT_Init(t, 256)); + VHD_Init(d); + + /* C.6.1 */ + in_l = hexbuf(in, sizeof in, + "4882 6402 5885 aec3 771a 4b61 96d0 7abe" + "9410 54d4 44a8 2005 9504 0b81 66e0 82a6" + "2d1b ff6e 919d 29ad 1718 63c7 8f0b 97c8" + "e9ae 82ae 43d3"); + r = decode(d, t, in, in_l, out, sizeof out, mode); + CHECK_RET(r, VHD_OK); + AZ(match(out, sizeof out, + ":status", "302", + "cache-control", "private", + "date", "Mon, 21 Oct 2013 20:13:21 GMT", + "location", "https://www.example.com", + NULL)); + + /* C.6.2 */ + in_l = hexbuf(in, sizeof in, + "4883 640e ffc1 c0bf"); + r = decode(d, t, in, in_l, out, sizeof out, mode); + CHECK_RET(r, VHD_OK); + AZ(match(out, sizeof out, + ":status", "307", + "cache-control", "private", + "date", "Mon, 21 Oct 2013 20:13:21 GMT", + "location", "https://www.example.com", + NULL)); + + /* C.6.3 */ + in_l = hexbuf(in, sizeof in, + "88c1 6196 d07a be94 1054 d444 a820 0595" + "040b 8166 e084 a62d 1bff c05a 839b d9ab" + "77ad 94e7 821d d7f2 e6c7 b335 dfdf cd5b" + "3960 d5af 2708 7f36 72c1 ab27 0fb5 291f" + "9587 3160 65c0 03ed 4ee5 b106 3d50 07"); + r = decode(d, t, in, in_l, out, sizeof out, mode); + CHECK_RET(r, VHD_OK); + AZ(match(out, sizeof out, + ":status", "200", + "cache-control", "private", + "date", "Mon, 21 Oct 2013 20:13:22 GMT", + "location", "https://www.example.com", + "content-encoding", "gzip", + "set-cookie", + "foo=ASDJKHQKBZXOQWEOPIUAXQWEOIU; max-age=3600; version=1", + NULL)); + + VHT_Fini(t); +} + +#define do_test(name) \ + do { \ + printf("Doing test: %s\n", #name); \ + name(0); \ + printf("Doing test: %s 1IN\n", #name); \ + name(M_1IN); \ + printf("Doing test: %s 1OUT\n", #name); \ + name(M_1OUT); \ + printf("Doing test: %s 1IN|1OUT\n", #name); \ + name(M_1IN|M_1OUT); \ + printf("Test finished: %s\n\n", #name); \ + } while (0) + +int +main(int argc, char **argv) +{ + if (argc == 2 && !strcmp(argv[1], "-v")) + verbose = 1; + else if (argc != 1) { + fprintf(stderr, "Usage: %s [-v]\n", argv[0]); + return (1); + } + + if (verbose) { + printf("sizeof (struct vhd_int)=%ju\n", + sizeof (struct vhd_int)); + printf("sizeof (struct vhd_lookup)=%ju\n", + sizeof (struct vhd_lookup)); + printf("sizeof (struct vhd_raw)=%ju\n", + sizeof (struct vhd_raw)); + printf("sizeof (struct vhd_huffman)=%ju\n", + sizeof (struct vhd_huffman)); + printf("sizeof (struct vhd_decode)=%ju\n", + sizeof (struct vhd_decode)); + } + + do_test(test_integer); + do_test(test_raw); + do_test(test_huffman); + + do_test(test_c2); + do_test(test_c3); + do_test(test_c4); + do_test(test_c5); + do_test(test_c6); + + return (0); +} + +#endif /* DECODE_TEST_DRIVER */ diff --git a/bin/varnishd/hpack/vhp_gen_hufdec.c b/bin/varnishd/hpack/vhp_gen_hufdec.c new file mode 100644 index 0000000..7372815 --- /dev/null +++ b/bin/varnishd/hpack/vhp_gen_hufdec.c @@ -0,0 +1,255 @@ +/*- + * Copyright (c) 2016 Dridi Boukelmoune + * All rights reserved. + * + * Author: Dridi Boukelmoune + * + * 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 +#include + +#include "vdef.h" +#include "vas.h" + +static unsigned minlen = UINT_MAX; +static unsigned maxlen = 0; +static unsigned idx = 0; + +static const struct { + uint32_t code; + unsigned blen; + char chr; +} huf[] = { +#define HPH(c, h, l) { h, l, (char)c }, +#include "tbl/vhp_huffman.h" +#undef HPH +}; + +#define HUF_LEN (sizeof huf / sizeof huf[0]) + +struct tbl; + +struct cod { + uint32_t bits; + unsigned len; + uint8_t chr; + struct tbl *next; +}; + +struct tbl { + unsigned mask; + uint32_t code; + unsigned masked; + unsigned n; + unsigned idx; + unsigned lvl; + unsigned p_idx; + struct cod e[]; +}; + +static struct tbl * +tbl_new(unsigned mask) +{ + unsigned n; + size_t size; + struct tbl *tbl; + + assert(mask > 0); + assert(mask <= 8); + n = 1U << mask; + size = sizeof (struct tbl) + n * sizeof (struct cod); + tbl = calloc(1, size); + AN(tbl); + memset(tbl, 0, size); + tbl->mask = mask; + tbl->n = n; + tbl->idx = idx; + idx += n; + return (tbl); +} + +static void +tbl_add(struct tbl *tbl, uint32_t code, unsigned codelen, + uint32_t bits, unsigned len, char chr) +{ + uint32_t b; + unsigned u; + + AN(tbl); + assert(codelen > 0); + assert(codelen <= maxlen); + assert(len > 0); + assert(tbl->mask > 0); + + if (len > tbl->mask) { + /* Does not belong in this table */ + b = bits >> (len - tbl->mask); + bits &= (1U << (len - tbl->mask)) - 1; + if (tbl->e[b].next == NULL) { + tbl->e[b].len = tbl->mask; + tbl->e[b].next = tbl_new(len - tbl->mask); + AN(tbl->e[b].next); + + tbl->e[b].next->masked = tbl->masked + tbl->mask; + tbl->e[b].next->code = code; + tbl->e[b].next->lvl = tbl->lvl + 1; + tbl->e[b].next->p_idx = tbl->idx + b; + } + AN(tbl->e[b].next); + tbl_add(tbl->e[b].next, code, codelen, + bits, len - tbl->mask, chr); + return; + } + + bits = bits << (tbl->mask - len); + for (u = 0; u < (1U << (tbl->mask - len)); u++) { + b = bits | u; + assert(b < tbl->n); + AZ(tbl->e[b].len); + AZ(tbl->e[b].next); + tbl->e[b].len = len; + tbl->e[b].chr = chr; + } +} + +static void +print_lsb(uint32_t c, int l) +{ + assert(l <= 32); + + while (l > 0) { + if (c & (1U << (l - 1))) + printf("1"); + else + printf("0"); + l--; + } +} + +static void +tbl_print(const struct tbl *tbl) +{ + unsigned u; + + printf("/* Table: lvl=%u p_idx=%u n=%u mask=%u masked=%u */\n", + tbl->lvl, tbl->p_idx, tbl->n, tbl->mask, tbl->masked); + for (u = 0; u < tbl->n; u++) { + printf("/* %3u: ", tbl->idx + u); + printf("%*s", maxlen - tbl->mask - tbl->masked, ""); + printf("%*s", tbl->mask - tbl->e[u].len, ""); + + if (tbl->masked > 0) { + printf("("); + print_lsb(tbl->code >> tbl->mask, tbl->masked); + printf(") "); + } else + printf(" "); + if (tbl->e[u].len < tbl->mask) { + print_lsb(u >> (tbl->mask - tbl->e[u].len), + tbl->e[u].len); + printf(" ("); + print_lsb(u, tbl->mask - tbl->e[u].len); + printf(")"); + } else { + assert(tbl->e[u].len == tbl->mask); + print_lsb(u, tbl->e[u].len); + printf(" "); + } + printf("%*s", 3 - (tbl->mask - tbl->e[u].len), ""); + printf(" */ "); + + if (tbl->e[u].next) { + /* Jump to next table */ + assert(tbl->e[u].next->idx - (tbl->idx + u) + <= UINT8_MAX); + printf("{ .len = %u, .jump = %u },", + tbl->e[u].len, + tbl->e[u].next->idx - (tbl->idx + u)); + printf(" /* Next: %u */", tbl->e[u].next->idx); + } else if (tbl->e[u].len) { + printf("{ "); + printf(".len = %u", tbl->e[u].len); + printf(", .chr = (char)0x%02x", tbl->e[u].chr); + if (isgraph(tbl->e[u].chr)) + printf(" /* '%c' */", tbl->e[u].chr); + if (u == 0) + /* First in table, set mask */ + printf(", .mask = %u", tbl->mask); + printf(" },"); + } else + printf("{ .len = 0 }, /* invalid */"); + printf("\n"); + } + + for (u = 0; u < tbl->n; u++) + if (tbl->e[u].next) + tbl_print(tbl->e[u].next); +} + +int +main(int argc, const char **argv) +{ + struct tbl *top; + unsigned u; + + (void)argc; + (void)argv; + + for (u = 0; u < HUF_LEN; u++) { + if (maxlen < huf[u].blen) + maxlen = huf[u].blen; + if (minlen > huf[u].blen) + minlen = huf[u].blen; + } + + top = tbl_new(8); + AN(top); + + for (u = 0; u < HUF_LEN; u++) + tbl_add(top, huf[u].code, huf[u].blen, + huf[u].code, huf[u].blen, huf[u].chr); + + printf("/*\n"); + printf(" * NB: This file is machine generated, DO NOT EDIT!\n"); + printf(" */\n\n"); + + printf("#define HUFDEC_LEN %u\n", idx); + printf("#define HUFDEC_MIN %u\n", minlen); + printf("#define HUFDEC_MAX %u\n\n", maxlen); + + printf("static const struct {\n"); + printf("\tuint8_t\tmask;\n"); + printf("\tuint8_t\tlen;\n"); + printf("\tuint8_t\tjump;\n"); + printf("\tchar\tchr;\n"); + printf("} hufdec[HUFDEC_LEN] = {\n"); + tbl_print(top); + printf("};\n"); + + return (0); +} diff --git a/bin/varnishd/hpack/vhp_table.c b/bin/varnishd/hpack/vhp_table.c new file mode 100644 index 0000000..26a046b --- /dev/null +++ b/bin/varnishd/hpack/vhp_table.c @@ -0,0 +1,785 @@ +/*- + * Copyright (c) 2016 Varnish Software + * All rights reserved. + * + * Author: Martin Blix Grydeland + * + * 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. + * + */ + +/* + * Layout: + * + * buf [ + * + * + * ... + * + * + * (padding bytes for pointer alignment) + * + * + * + * ... + * + * ] + * + */ + +#include +#include +#include +#include +#include +#include + +#include "vdef.h" +#include "miniobj.h" +#include "vas.h" + +#include "hpack/vhp.h" + +#define VHT_STATIC_MAX 61 + +struct vht_static { + const char *name; + unsigned namelen; + const char *value; + unsigned valuelen; +}; + +static const struct vht_static static_table[] = { +#define HPS(NUM, NAME, VAL) \ + { NAME, sizeof NAME - 1, VAL, sizeof VAL - 1 }, +#include "tbl/vhp_static.h" +#undef HPS +}; + +#define TBLSIZE(tbl) ((tbl)->size + (tbl)->n * VHT_ENTRY_SIZE) +#define ENTRIES(buf, bufsize, n) \ + (((struct vht_entry *)((uintptr_t)(buf) + bufsize)) - (n)) +#define TBLENTRIES(tbl) ENTRIES((tbl)->buf, (tbl)->bufsize, (tbl)->n) +#define TBLENTRY(tbl, i) (&TBLENTRIES(tbl)[(i)]) +#define ENTRYLEN(e) ((e)->namelen + (e)->valuelen) +#define ENTRYSIZE(e) (ENTRYLEN(e) + VHT_ENTRY_SIZE) + +/****************************************************************************/ +/* Internal interface */ + +static void +vht_newentry(struct vht_table *tbl) +{ + struct vht_entry *e; + + assert(tbl->maxsize - TBLSIZE(tbl) >= VHT_ENTRY_SIZE); + tbl->n++; + e = TBLENTRY(tbl, 0); + INIT_OBJ(e, VHT_ENTRY_MAGIC); + e->offset = tbl->size; +} + +/* Trim elements from the end until the table size is less than max. */ +static void +vht_trim(struct vht_table *tbl, ssize_t max) +{ + unsigned u, v; + int i; + struct vht_entry *e; + + CHECK_OBJ_NOTNULL(tbl, VHT_TABLE_MAGIC); + + if (max < 0) + max = 0; + if (TBLSIZE(tbl) <= max) + return; + + u = v = 0; + for (i = tbl->n - 1; i >= 0; i--) { + e = TBLENTRY(tbl, i); + CHECK_OBJ_NOTNULL(e, VHT_ENTRY_MAGIC); + if (TBLSIZE(tbl) - (u + v * VHT_ENTRY_SIZE) > max) { + /* Trim entry */ + assert(e->offset == u); + u += ENTRYLEN(e); + v++; + e->magic = 0; + } else { + /* Fixup offset */ + assert(e->offset >= u); + e->offset -= u; + } + } + assert(v <= tbl->n); + + memmove(tbl->buf, tbl->buf + u, tbl->size - u); + memmove(TBLENTRY(tbl, v), TBLENTRY(tbl, 0), (tbl->n - v) * sizeof *e); + tbl->n -= v; + tbl->size -= u; +} + +/* Append len bytes from buf to entry 0 name. Asserts if no space. */ +static void +vht_appendname(struct vht_table *tbl, const char *buf, size_t len) +{ + struct vht_entry *e; + + CHECK_OBJ_NOTNULL(tbl, VHT_TABLE_MAGIC); + e = TBLENTRY(tbl, 0); + CHECK_OBJ_NOTNULL(e, VHT_ENTRY_MAGIC); + AZ(e->valuelen); /* Name needs to be set before value */ + assert(TBLSIZE(tbl) + len <= tbl->maxsize); + assert(e->offset + e->namelen == tbl->size); + memcpy(tbl->buf + tbl->size, buf, len); + e->namelen += len; + tbl->size += len; +} + +/* Append len bytes from buf to entry 0 value. Asserts if no space. */ +static void +vht_appendvalue(struct vht_table *tbl, const char *buf, size_t len) +{ + struct vht_entry *e; + + CHECK_OBJ_NOTNULL(tbl, VHT_TABLE_MAGIC); + e = TBLENTRY(tbl, 0); + CHECK_OBJ_NOTNULL(e, VHT_ENTRY_MAGIC); + assert(TBLSIZE(tbl) + len <= tbl->maxsize); + assert(e->offset + e->namelen + e->valuelen == tbl->size); + memcpy(tbl->buf + tbl->size, buf, len); + e->valuelen += len; + tbl->size += len; +} + +/****************************************************************************/ +/* Public interface */ + +void +VHT_NewEntry(struct vht_table *tbl) +{ + + CHECK_OBJ_NOTNULL(tbl, VHT_TABLE_MAGIC); + assert(tbl->maxsize <= tbl->protomax); + vht_trim(tbl, tbl->maxsize - VHT_ENTRY_SIZE); + if (tbl->maxsize - TBLSIZE(tbl) < VHT_ENTRY_SIZE) { + /* Maxsize less than one entry */ + assert(tbl->maxsize < VHT_ENTRY_SIZE); + return; + } + vht_newentry(tbl); +} + +int +VHT_NewEntry_Indexed(struct vht_table *tbl, unsigned idx) +{ + struct vht_entry *e, *e2; + unsigned l, l2, lbuf, lentry, lname, u; + uint8_t buf[48]; + + /* Referenced name insertion. This has to be done carefully + because the referenced name may be evicted as the result of the + insertion (RFC 7541 section 4.4). */ + + assert(sizeof buf >= VHT_ENTRY_SIZE); + + CHECK_OBJ_NOTNULL(tbl, VHT_TABLE_MAGIC); + assert(tbl->maxsize <= tbl->protomax); + + if (idx == 0) + return (-1); + + if (idx <= VHT_STATIC_MAX) { + /* Static table reference */ + VHT_NewEntry(tbl); + VHT_AppendName(tbl, static_table[idx - 1].name, + static_table[idx - 1].namelen); + return (0); + } + idx -= VHT_STATIC_MAX + 1; + + if (idx >= tbl->n) + return (-1); /* No such index */ + assert(tbl->maxsize >= VHT_ENTRY_SIZE); + + e = TBLENTRY(tbl, idx); + CHECK_OBJ_NOTNULL(e, VHT_ENTRY_MAGIC); + + /* Count how many elements we can safely evict to make space + without evicting the referenced entry. */ + l = 0; + u = 0; + while (tbl->n - 1 - u > idx && + tbl->maxsize - TBLSIZE(tbl) + l < VHT_ENTRY_SIZE + e->namelen) { + e2 = TBLENTRY(tbl, tbl->n - 1 - u); + CHECK_OBJ_NOTNULL(e2, VHT_ENTRY_MAGIC); + l += ENTRYSIZE(e2); + u++; + } + vht_trim(tbl, TBLSIZE(tbl) - l); + e += u; + assert(e == TBLENTRY(tbl, idx)); + + if (tbl->maxsize - TBLSIZE(tbl) >= VHT_ENTRY_SIZE + e->namelen) { + /* New entry with name fits */ + vht_newentry(tbl); + idx++; + assert(e == TBLENTRY(tbl, idx)); + CHECK_OBJ_NOTNULL(e, VHT_ENTRY_MAGIC); + vht_appendname(tbl, tbl->buf + e->offset, e->namelen); + return (0); + } + + /* The tricky case: The referenced name will be evicted as a + result of the insertion. Move the element data to the end of + the buffer through a local buffer. */ + + /* Remove the referenced element from the entry list */ + assert(idx == tbl->n - 1); + assert(e->offset == 0); + lname = e->namelen; + lentry = ENTRYLEN(e); + e->magic = 0; + memmove(TBLENTRY(tbl, 1), TBLENTRY(tbl, 0), (tbl->n - 1) * sizeof *e); + tbl->n--; + + /* Shift the referenced element last in the buffer. Use what space + is available in the table buffer and the local buffer to keep + memmove operations to a minimum. */ + l = 0; + while (l < lentry) { + l2 = lentry - l; + if (l2 > tbl->maxsize - TBLSIZE(tbl)) + l2 = tbl->maxsize - TBLSIZE(tbl); + lbuf = lentry - l - l2; + if (lbuf > sizeof buf) + lbuf = sizeof buf; + memcpy(tbl->buf + tbl->size, tbl->buf, l2); + memcpy(buf, tbl->buf + l2, lbuf); + memmove(tbl->buf, tbl->buf + l2 + lbuf, tbl->size + l2); + memcpy(tbl->buf + tbl->size - lbuf, buf, lbuf); + l += l2 + lbuf; + } + assert(l == lentry); + tbl->size -= lentry; + + /* Fix up the existing element offsets */ + for (u = 0; u < tbl->n; u++) { + e = TBLENTRY(tbl, u); + CHECK_OBJ_NOTNULL(e, VHT_ENTRY_MAGIC); + assert(e->offset >= lentry); + e->offset -= lentry; + assert(e->offset + ENTRYLEN(e) <= tbl->size); + } + + /* Insert the new entry with the name now present at the end of + the buffer. */ + assert(tbl->maxsize - TBLSIZE(tbl) >= VHT_ENTRY_SIZE + lname); + tbl->n++; + e = TBLENTRY(tbl, 0); + INIT_OBJ(e, VHT_ENTRY_MAGIC); + e->offset = tbl->size; + e->namelen = lname; + tbl->size += lname; + + return (0); +} + +void +VHT_AppendName(struct vht_table *tbl, const char *buf, ssize_t len) +{ + + CHECK_OBJ_NOTNULL(tbl, VHT_TABLE_MAGIC); + assert(tbl->maxsize <= tbl->protomax); + if (len == 0) + return; + AN(buf); + if (len < 0) + len = strlen(buf); + vht_trim(tbl, tbl->maxsize - len); + if (tbl->n == 0) + /* Max size exceeded */ + return; + vht_appendname(tbl, buf, len); +} + +void +VHT_AppendValue(struct vht_table *tbl, const char *buf, ssize_t len) +{ + + CHECK_OBJ_NOTNULL(tbl, VHT_TABLE_MAGIC); + assert(tbl->maxsize <= tbl->protomax); + if (len == 0) + return; + AN(buf); + if (len < 0) + len = strlen(buf); + vht_trim(tbl, tbl->maxsize - len); + if (tbl->n == 0) + /* Max size exceeded */ + return; + vht_appendvalue(tbl, buf, len); +} + +int +VHT_SetMaxTableSize(struct vht_table *tbl, size_t maxsize) +{ + + CHECK_OBJ_NOTNULL(tbl, VHT_TABLE_MAGIC); + assert(tbl->maxsize <= tbl->protomax); + if (maxsize > tbl->protomax) + return (-1); + vht_trim(tbl, maxsize); + assert(TBLSIZE(tbl) <= maxsize); + tbl->maxsize = maxsize; + return (0); +} + +int +VHT_SetProtoMax(struct vht_table *tbl, size_t protomax) +{ + size_t bufsize; + char *buf; + + CHECK_OBJ_NOTNULL(tbl, VHT_TABLE_MAGIC); + assert(protomax <= UINT_MAX); + assert(tbl->maxsize <= tbl->protomax); + + if (protomax == tbl->protomax) + return (0); + + if (tbl->maxsize > protomax) + tbl->maxsize = protomax; + vht_trim(tbl, tbl->maxsize); + assert(TBLSIZE(tbl) <= tbl->maxsize); + + bufsize = PRNDUP(protomax); + if (bufsize == tbl->bufsize) { + tbl->protomax = protomax; + return (0); + } + + buf = malloc(bufsize); + if (buf == NULL) + return (-1); + + memcpy(buf, tbl->buf, tbl->size); + memcpy(ENTRIES(buf, bufsize, tbl->n), TBLENTRIES(tbl), + sizeof (struct vht_entry) * tbl->n); + free(tbl->buf); + tbl->buf = buf; + tbl->bufsize = bufsize; + tbl->protomax = protomax; + return (0); +} + +const char * +VHT_LookupName(const struct vht_table *tbl, unsigned idx, size_t *plen) +{ + struct vht_entry *e; + + AN(plen); + *plen = 0; + + if (idx == 0) { + return (NULL); + } + if (idx <= VHT_STATIC_MAX) { + *plen = static_table[idx - 1].namelen; + return (static_table[idx - 1].name); + } + + if (tbl == NULL) + return (NULL); + CHECK_OBJ_NOTNULL(tbl, VHT_TABLE_MAGIC); + + idx -= VHT_STATIC_MAX + 1; + if (idx >= tbl->n) + return (NULL); + + e = TBLENTRY(tbl, idx); + CHECK_OBJ_NOTNULL(e, VHT_ENTRY_MAGIC); + assert(e->offset + e->namelen <= tbl->size); + *plen = e->namelen; + return (tbl->buf + e->offset); +} + +const char * +VHT_LookupValue(const struct vht_table *tbl, unsigned idx, size_t *plen) +{ + struct vht_entry *e; + + AN(plen); + *plen = 0; + + if (idx == 0) { + return (NULL); + } + if (idx <= VHT_STATIC_MAX) { + *plen = static_table[idx - 1].valuelen; + return (static_table[idx - 1].value); + } + + if (tbl == NULL) + return (NULL); + CHECK_OBJ_NOTNULL(tbl, VHT_TABLE_MAGIC); + + idx -= VHT_STATIC_MAX + 1; + if (idx >= tbl->n) + return (NULL); + + e = TBLENTRY(tbl, idx); + CHECK_OBJ_NOTNULL(e, VHT_ENTRY_MAGIC); + assert(e->offset + e->namelen + e->valuelen <= tbl->size); + *plen = e->valuelen; + return (tbl->buf + e->offset + e->namelen); +} + +int +VHT_Init(struct vht_table *tbl, size_t protomax) +{ + int r; + + assert(sizeof (struct vht_entry) <= VHT_ENTRY_SIZE); + + AN(tbl); + if (protomax > UINT_MAX) + return (-1); + INIT_OBJ(tbl, VHT_TABLE_MAGIC); + r = VHT_SetProtoMax(tbl, protomax); + if (r) { + tbl->magic = 0; + return (r); + } + tbl->maxsize = tbl->protomax; + return (0); +} + +void +VHT_Fini(struct vht_table *tbl) +{ + + CHECK_OBJ_NOTNULL(tbl, VHT_TABLE_MAGIC); + free(tbl->buf); + memset(tbl, 0, sizeof *tbl); +} + +/****************************************************************************/ +/* Internal interface */ + +#ifdef TABLE_TEST_DRIVER + +#define VHT_DYNAMIC (VHT_STATIC_MAX + 1) + +static int verbose = 0; + +static int +vht_matchtable(struct vht_table *tbl, ...) +{ + va_list ap; + unsigned u; + int r; + const char *a, *b; + const struct vht_entry *e; + + CHECK_OBJ_NOTNULL(tbl, VHT_TABLE_MAGIC); + + va_start(ap, tbl); + r = 0; + for (u = 0; u < tbl->n; u++) { + a = NULL; + b = NULL; + if (!r) { + a = va_arg(ap, const char *); + if (a == NULL) { + printf("Too many elements in table\n"); + r = -1; + } else { + b = va_arg(ap, const char *); + AN(b); + } + } + + e = TBLENTRY(tbl, u); + CHECK_OBJ_NOTNULL(e, VHT_ENTRY_MAGIC); + + if (a) { + AN(b); + if (e->namelen != strlen(a) || + strncmp(a, tbl->buf + e->offset, e->namelen)) + r = -1; + if (e->valuelen != strlen(b) || + strncmp(b, tbl->buf + e->offset + e->namelen, + e->valuelen)) + r = -1; + } + + if (verbose || r) + printf("%2u: @%03u (\"%.*s\", \"%.*s\")", + u, e->offset, (int)e->namelen, tbl->buf + e->offset, + (int)e->valuelen, tbl->buf + e->offset +e->namelen); + + if (a && (verbose || r)) { + AN(b); + printf(" %s (\"%s\", \"%s\")", (r ? "!=" : "=="), a, b); + } + + if (verbose || r) + printf("\n"); + } + if (!r) { + a = va_arg(ap, const char *); + if (a != NULL) { + printf("Missing elements in table\n"); + r = -1; + } + } + va_end(ap); + + if (verbose || r) + printf("n=%d, size=%u, tblsz=%u, max=%u, pmax=%u, bufsz=%u\n", + tbl->n, tbl->size, TBLSIZE(tbl), tbl->maxsize, + tbl->protomax, tbl->bufsize); + + return (r); +} + +static void +test_1(void) +{ + /* Static table */ + + const char *p; + size_t l; + + if (verbose) + printf("Test 1:\n"); + + /* 1: ':authority' -> '' */ + p = VHT_LookupName(NULL, 1, &l); + assert(l == strlen(":authority")); + AZ(strncmp(p, ":authority", strlen(":authority"))); + p = VHT_LookupValue(NULL, 1, &l); + AN(p); + AZ(l); + + /* 5: ':path' -> '/index.html' */ + p = VHT_LookupValue(NULL, 5, &l); + assert(l == strlen("/index.html")); + AZ(strncmp(p, "/index.html", strlen("/index.html"))); + + /* 61: 'www-authenticate' -> '' */ + p = VHT_LookupName(NULL, 61, &l); + assert(l == strlen("www-authenticate")); + AZ(strncmp(p, "www-authenticate", strlen("www-authenticate"))); + p = VHT_LookupValue(NULL, 61, &l); + AN(p); + AZ(l); + + /* Test zero index */ + AZ(VHT_LookupName(NULL, 0, &l)); + AZ(l); + AZ(VHT_LookupValue(NULL, 0, &l)); + AZ(l); + + printf("Test 1 finished successfully\n"); + if (verbose) + printf("\n"); +} + +static void +test_2(void) +{ + /* Test filling and overflow */ + + struct vht_table tbl[1]; + + if (verbose) + printf("Test 2:\n"); + + AZ(VHT_Init(tbl, VHT_ENTRY_SIZE + 10)); + + VHT_NewEntry(tbl); + VHT_AppendName(tbl, "12345", -1); + VHT_AppendValue(tbl, "abcde", -1); + assert(TBLSIZE(tbl) == VHT_ENTRY_SIZE + 10); + /* 0: '12345' -> 'abcde' */ + AZ(vht_matchtable(tbl, "12345", "abcde", NULL)); + + VHT_AppendValue(tbl, "f", -1); + AZ(vht_matchtable(tbl, NULL)); + + VHT_NewEntry(tbl); + AZ(vht_matchtable(tbl, "", "", NULL)); + + VHT_Fini(tbl); + AZ(tbl->buf); + + printf("Test 2 finished successfully\n"); + if (verbose) + printf("\n"); +} + +static void +test_3(void) +{ + /* Test change in proto max size and dynamic max size */ + + struct vht_table tbl[1]; + + if (verbose) + printf("Test 3:\n"); + + AZ(VHT_Init(tbl, 4096)); + + VHT_NewEntry(tbl); + VHT_AppendName(tbl, "a", -1); + VHT_AppendValue(tbl, "12345", -1); + VHT_NewEntry(tbl); + VHT_AppendName(tbl, "b", -1); + VHT_AppendValue(tbl, "67890", -1); + VHT_NewEntry(tbl); + VHT_AppendName(tbl, "c", -1); + VHT_AppendValue(tbl, "abcde", -1); + AZ(vht_matchtable(tbl, "c", "abcde", "b", "67890", "a", "12345", NULL)); + + /* Buffer reallocation */ + AZ(VHT_SetProtoMax(tbl, VHT_ENTRY_SIZE * 2 + 6 * 2)); + AZ(vht_matchtable(tbl, "c", "abcde", "b", "67890", NULL)); + + /* Increase table size beyond protomax */ + assert(VHT_SetMaxTableSize(tbl, VHT_ENTRY_SIZE * 2 + 6 * 2 + 1) == -1); + + /* Decrease by one */ + AZ(VHT_SetMaxTableSize(tbl, VHT_ENTRY_SIZE * 2 + 6 * 2 - 1)); + AZ(vht_matchtable(tbl, "c", "abcde", NULL)); + + /* Increase by one back to protomax */ + AZ(VHT_SetMaxTableSize(tbl, VHT_ENTRY_SIZE * 2 + 6 * 2)); + AZ(vht_matchtable(tbl, "c", "abcde", NULL)); + + /* Add entry */ + VHT_NewEntry(tbl); + VHT_AppendName(tbl, "d", -1); + VHT_AppendValue(tbl, "ABCDE", -1); + AZ(vht_matchtable(tbl, "d", "ABCDE", "c", "abcde", NULL)); + + /* Set to zero */ + AZ(VHT_SetMaxTableSize(tbl, 0)); + AZ(vht_matchtable(tbl, NULL)); + VHT_NewEntry(tbl); + AZ(vht_matchtable(tbl, NULL)); + + /* Set protomax to zero */ + AZ(VHT_SetProtoMax(tbl, 0)); + AZ(vht_matchtable(tbl, NULL)); + VHT_NewEntry(tbl); + AZ(vht_matchtable(tbl, NULL)); + + VHT_Fini(tbl); + + printf("Test 3 finished successfully\n"); + if (verbose) + printf("\n"); +} + +static void +test_4(void) +{ + /* Referenced name new entry */ + + struct vht_table tbl[1]; + static const char longname[] = + "1234567890" + "1234567890" + "1234567890" + "1234567890" + "1234567890" + "1"; /* 51 bytes + VHT_ENTRY_SIZE == 83 */ + + if (verbose) + printf("Test 4:\n"); + + AZ(VHT_Init(tbl, VHT_ENTRY_SIZE * 2 + 10 * 2)); /* 84 bytes */ + + /* New entry indexed from static table */ + AZ(VHT_NewEntry_Indexed(tbl, 4)); + VHT_AppendValue(tbl, "12345", -1); + AZ(vht_matchtable(tbl, ":path", "12345", NULL)); + + /* New entry indexed from dynamic table */ + AZ(VHT_NewEntry_Indexed(tbl, VHT_DYNAMIC + 0)); + VHT_AppendValue(tbl, "abcde", -1); + AZ(vht_matchtable(tbl, ":path", "abcde", ":path", "12345", NULL)); + AZ(tbl->maxsize - TBLSIZE(tbl)); /* No space left */ + + /* New entry indexed from dynamic table, no overlap eviction */ + AZ(VHT_NewEntry_Indexed(tbl, VHT_DYNAMIC + 0)); + VHT_AppendValue(tbl, "ABCDE", -1); + AZ(vht_matchtable(tbl, ":path", "ABCDE", ":path", "abcde", NULL)); + + /* New entry indexed from dynamic table, overlap eviction */ + AZ(VHT_NewEntry_Indexed(tbl, VHT_DYNAMIC + 1)); + AZ(vht_matchtable(tbl, ":path", "", ":path", "ABCDE", NULL)); + + /* New entry indexed from dynamic table, overlap eviction with + overlap larger than the copy buffer size */ + VHT_NewEntry(tbl); + VHT_AppendName(tbl, longname, strlen(longname)); + AZ(vht_matchtable(tbl, longname, "", NULL)); + AZ(VHT_NewEntry_Indexed(tbl, VHT_DYNAMIC + 0)); + VHT_AppendValue(tbl, "2", -1); + AZ(vht_matchtable(tbl, longname, "2", NULL)); + + VHT_Fini(tbl); + printf("Test 4 finished successfully\n"); + if (verbose) + printf("\n"); +} + +int +main(int argc, char **argv) +{ + + if (argc == 2 && !strcmp(argv[1], "-v")) + verbose = 1; + else if (argc != 1) { + fprintf(stderr, "Usage: %s [-v]\n", argv[0]); + return (1); + } + + if (verbose) { + printf("sizeof (struct vht_table) == %ju\n", + sizeof (struct vht_table)); + printf("sizeof (struct vht_entry) == %ju\n", + sizeof (struct vht_entry)); + printf("\n"); + } + + test_1(); + test_2(); + test_3(); + test_4(); + + return (0); +} + +#endif /* TABLE_TEST_DRIVER */ diff --git a/bin/varnishd/http1/cache_http1_fsm.c b/bin/varnishd/http1/cache_http1_fsm.c index 27a5597..2a4a11a 100644 --- a/bin/varnishd/http1/cache_http1_fsm.c +++ b/bin/varnishd/http1/cache_http1_fsm.c @@ -392,16 +392,34 @@ HTTP1_Session(struct worker *wrk, struct req *req) if (hs != HTC_S_COMPLETE) WRONG("htc_status (nonbad)"); + if (H2_prism_complete(req->htc) == HTC_S_COMPLETE) { + VSLb(req->vsl, SLT_Debug, + "H2 Prior Knowledge Upgrade"); + http1_setstate(sp, NULL); + req->err_code = 1; + SES_SetTransport(wrk, sp, req, &H2_transport); + return; + } + i = http1_dissect(wrk, req); req->acct.req_hdrbytes += req->htc->rxbuf_e - req->htc->rxbuf_b; if (i) { SES_Close(req->sp, req->doclose); http1_setstate(sp, H1CLEANUP); - } else { - req->req_step = R_STP_RECV; - http1_setstate(sp, H1PROC); + continue; } + if (req->htc->body_status == BS_NONE && + http_HdrIs(req->http, H_Upgrade, "h2c")) { + VSLb(req->vsl, SLT_Debug, + "H2 Optimistic Upgrade"); + http1_setstate(sp, NULL); + req->err_code = 2; + SES_SetTransport(wrk, sp, req, &H2_transport); + return; + } + req->req_step = R_STP_RECV; + http1_setstate(sp, H1PROC); } else if (st == H1BUSY) { /* * Return from waitinglist. diff --git a/bin/varnishd/http2/cache_http2.h b/bin/varnishd/http2/cache_http2.h new file mode 100644 index 0000000..1378ef8 --- /dev/null +++ b/bin/varnishd/http2/cache_http2.h @@ -0,0 +1,138 @@ +/*- + * Copyright (c) 2016 Varnish Software AS + * All rights reserved. + * + * Author: Poul-Henning Kamp + * + * 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 h2_sess; + +#include "hpack/vhp.h" + +enum h2_error_e { +#define H2_ERROR(NAME, val, desc) \ + H2E_##NAME = val, +#include "tbl/h2_error.h" +}; + +enum h2_frame_e { +#define H2_FRAME(l,u,t,f) H2_FRAME_##u = t, +#include "tbl/h2_frames.h" +}; + +enum h2_stream_e { +#define H2_STREAM(U,s,d) H2_S_##U, +#include "tbl/h2_stream.h" +}; + +#define H2_FRAME_FLAGS(l,u,v) extern const uint8_t H2FF_##u; +#include "tbl/h2_frames.h" + +#define H2_SETTINGS_N 7 + +struct h2_req { + unsigned magic; +#define H2_REQ_MAGIC 0x03411584 + uint32_t stream; + enum h2_stream_e state; + struct h2_sess *h2sess; + struct req *req; + VTAILQ_ENTRY(h2_req) list; + int64_t window; +}; + +VTAILQ_HEAD(h2_req_s, h2_req); + +struct h2_sess { + unsigned magic; +#define H2_SESS_MAGIC 0xa16f7e4b + + struct sess *sess; + int refcnt; + uint32_t highest_stream; + + struct h2_req_s streams; + + struct req *srq; + struct ws *ws; + struct http_conn *htc; + struct vsl_log *vsl; + struct vht_table dectbl[1]; + + unsigned rxf_len; + unsigned rxf_flags; + unsigned rxf_stream; + uint8_t *rxf_data; + + uint32_t their_settings[H2_SETTINGS_N]; + uint32_t our_settings[H2_SETTINGS_N]; + + struct req *new_req; + int go_away; + uint32_t go_away_last_stream; +}; + +/* http2/cache_http2_panic.c */ +#ifdef TRANSPORT_MAGIC +vtr_sess_panic_f h2_sess_panic; +#endif + +/* http2/cache_http2_deliver.c */ +#ifdef TRANSPORT_MAGIC +vtr_deliver_f h2_deliver; +#endif /* TRANSPORT_MAGIC */ + +/* http2/cache_http2_hpack.c */ +struct h2h_decode { + unsigned magic; +#define H2H_DECODE_MAGIC 0xd092bde4 + + int error; + enum vhd_ret_e vhd_ret; + char *out; + char *reset; + size_t out_l; + size_t out_u; + size_t namelen; + struct vhd_decode vhd[1]; +}; + +void h2h_decode_init(const struct h2_sess *h2, struct h2h_decode *d); +int h2h_decode_fini(const struct h2_sess *h2, struct h2h_decode *d); +int h2h_decode_bytes(struct h2_sess *h2, struct h2h_decode *d, + const uint8_t *ptr, size_t len); + +int H2_Send_Frame(struct worker *, const struct h2_sess *, + enum h2_frame_e type, uint8_t flags, uint32_t len, uint32_t stream, + const void *); + +int H2_Send(struct worker *, struct h2_req *, int flush, + enum h2_frame_e type, uint8_t flags, uint32_t len, const void *); + +typedef void h2_frame_f(struct worker *, struct h2_sess *, + struct h2_req *); +#define H2_FRAME(l,u,t,f) h2_frame_f h2_rx_##l ; +#include "tbl/h2_frames.h" + diff --git a/bin/varnishd/http2/cache_http2_deliver.c b/bin/varnishd/http2/cache_http2_deliver.c new file mode 100644 index 0000000..5d28140 --- /dev/null +++ b/bin/varnishd/http2/cache_http2_deliver.c @@ -0,0 +1,227 @@ +/*- + * Copyright (c) 2016 Varnish Software AS + * All rights reserved. + * + * Author: Poul-Henning Kamp + * + * 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 "../cache/cache.h" +#include "../cache/cache_filter.h" +#include "../cache/cache_transport.h" + +#include "../http1/cache_http1.h" +#include "../http2/cache_http2.h" + +#include "vct.h" +#include "vend.h" + +/**********************************************************************/ + +struct hpack_static { + uint8_t idx; + const char * name; + const char * val; +}; + +static const struct hpack_static hp_static[] = { +#define HPS(I,N,V) [I] = { I, N ":", V }, +#include "tbl/vhp_static.h" +#undef HPS + { 0, "\377:", ""} // Terminator +}; + +static const struct hpack_static *hp_idx[256]; + +void +V2D_Init(void) +{ + int i; +#define HPS(I,N,V) \ + i = hp_static[I].name[0]; \ + if (hp_idx[i] == NULL) hp_idx[i] = &hp_static[I]; +#include "tbl/vhp_static.h" +#undef HPS +} + +/**********************************************************************/ + +static int __match_proto__(vdp_bytes) +h2_bytes(struct req *req, enum vdp_action act, void **priv, + const void *ptr, ssize_t len) +{ + struct h2_req *r2; + + CHECK_OBJ_NOTNULL(req, REQ_MAGIC); + CAST_OBJ_NOTNULL(r2, req->transport_priv, H2_REQ_MAGIC); + (void)priv; + if (act == VDP_INIT || act == VDP_FINI) + return (0); + AZ(req->vdp_nxt); /* always at the bottom of the pile */ + + H2_Send(req->wrk, r2, + act == VDP_FLUSH ? 1 : 0, + H2_FRAME_DATA, H2FF_NONE, len, ptr); + + return (0); +} + +void __match_proto__(vtr_deliver_f) +h2_deliver(struct req *req, struct boc *boc, int sendbody) +{ + ssize_t sz, sz1; + uint8_t *p; + unsigned u; + const char *r; + struct http *hp; + struct sess *sp; + struct h2_req *r2; + int i, err = 0; + const struct hpack_static *hps; + + CHECK_OBJ_NOTNULL(req, REQ_MAGIC); + CHECK_OBJ_ORNULL(boc, BOC_MAGIC); + CHECK_OBJ_NOTNULL(req->objcore, OBJCORE_MAGIC); + CAST_OBJ_NOTNULL(r2, req->transport_priv, H2_REQ_MAGIC); + sp = req->sp; + CHECK_OBJ_NOTNULL(sp, SESS_MAGIC); + (void)sendbody; + + VSLb(req->vsl, SLT_Debug, "H2: Deliver"); + + (void)WS_Reserve(req->ws, 0); + p = (void*)req->ws->f; + + switch (req->resp->status) { + case 200: *p++ = 0x80 | 8; break; + case 204: *p++ = 0x80 | 9; break; + case 206: *p++ = 0x80 | 10; break; + case 304: *p++ = 0x80 | 11; break; + case 400: *p++ = 0x80 | 12; break; + case 404: *p++ = 0x80 | 13; break; + case 500: *p++ = 0x80 | 14; break; + default: + *p++ = 0x18; + *p++ = 0x03; + + (void)sprintf((char*)p, "%03d", req->resp->status); + p += 3; + break; + } + + hp = req->resp; + for (u = HTTP_HDR_FIRST; u < hp->nhd; u++) { + assert((char*)p < req->ws->e); + + r = strchr(hp->hd[u].b, ':'); + AN(r); + + hps = hp_idx[tolower(*hp->hd[u].b)]; + sz = 1 + r - hp->hd[u].b; + assert(sz > 0); + while (hps != NULL && hps->idx > 0) { + i = strncasecmp(hps->name, hp->hd[u].b, sz); + if (i < 0) { + hps++; + continue; + } + if (i > 0) + hps = NULL; + break; + } + if (hps != NULL) { + VSLb(req->vsl, SLT_Debug, + "HP {%d, \"%s\", \"%s\"} <%s>", + hps->idx, hps->name, hps->val, hp->hd[u].b); + if (hps->idx < 15) { + *p++ = 0x10 | hps->idx; + } else { + *p++ = 0x1f; + *p++ = hps->idx - 0x0f; + } + } else { + + *p++ = 0x10; + sz--; + if (sz < 127) { + *p++ = (uint8_t)sz; + } else { + *p++ = 0x7f; + *p++ = (uint8_t)sz - 0x7f; + } + + for(sz1 = 0; sz1 < sz; sz1++) + *p++ = (uint8_t)tolower(hp->hd[u].b[sz1]); + + } + + while(vct_islws(*++r)) + continue; + sz = hp->hd[u].e - r; + assert(sz <= 254); + if (sz < 127) { + *p++ = (uint8_t)sz; + } else if (sz < 127 * 2) { + *p++ = 0x7f; + *p++ = (uint8_t)sz - 0x7f; + } + + memcpy(p, r, sz); + p += sz; + assert((char*)p < req->ws->e); + } + sz = (char*)p - req->ws->f; + + /* XXX: Optimize !sendbody case */ + H2_Send(req->wrk, r2, 1, H2_FRAME_HEADERS, H2FF_HEADERS_END_HEADERS, + sz, req->ws->f); + + WS_Release(req->ws, 0); + + if (sendbody && req->resp_len != 0) + VDP_push(req, h2_bytes, NULL, 1, "H2"); + + AZ(req->wrk->v1l); + + if (sendbody && req->resp_len != 0) + err = VDP_DeliverObj(req); + + H2_Send(req->wrk, r2, 1, H2_FRAME_DATA, H2FF_DATA_END_STREAM, 0, NULL); + + AZ(req->wrk->v1l); + VDP_close(req); +} diff --git a/bin/varnishd/http2/cache_http2_hpack.c b/bin/varnishd/http2/cache_http2_hpack.c new file mode 100644 index 0000000..a1470d7 --- /dev/null +++ b/bin/varnishd/http2/cache_http2_hpack.c @@ -0,0 +1,313 @@ +/*- + * Copyright (c) 2016 Varnish Software AS + * All rights reserved. + * + * Author: Martin Blix Grydeland + * + * 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 "../cache/cache.h" +#include "../http2/cache_http2.h" +#include "vct.h" + +static int +h2h_checkhdr(const struct http *hp, const char *b, size_t namelen, size_t len) +{ + const char *p; + + CHECK_OBJ_NOTNULL(hp, HTTP_MAGIC); + AN(b); + assert(namelen >= 2); /* 2 chars from the ': ' that we added */ + assert(namelen <= len); + + if (namelen == 2) { + VSLb(hp->vsl, SLT_BogoHeader, "Empty name"); + return (H2E_PROTOCOL_ERROR); + } + + for (p = b; p < b + len; p++) { + if (p < b + (namelen - 2)) { + /* Check valid name characters */ + if (p == b && *p == ':') + continue; /* pseudo-header */ + if (vct_istchar(*p) && (!isupper(*p))) + /* XXX: vct should have a proper class for + this avoiding two checks */ + continue; + VSLb(hp->vsl, SLT_BogoHeader, + "Illegal header name: %.*s", + (int)(len > 20 ? 20 : len), b); + return (H2E_PROTOCOL_ERROR); + } else if (p < b + namelen) { + /* ': ' added by us */ + assert(*p == ':' || *p == ' '); + } else { + /* Check valid value characters */ + if (!vct_isctl(*p) || vct_issp(*p)) + continue; + VSLb(hp->vsl, SLT_BogoHeader, + "Illegal header value: %.*s", + (int)(len > 20 ? 20 : len), b); + return (H2E_PROTOCOL_ERROR); + } + } + + return (0); +} + +static int +h2h_addhdr(struct http *hp, char *b, size_t namelen, size_t len) +{ + /* XXX: This might belong in cache/cache_http.c */ + unsigned n; + + CHECK_OBJ_NOTNULL(hp, HTTP_MAGIC); + AN(b); + assert(namelen >= 2); /* 2 chars from the ': ' that we added */ + assert(namelen <= len); + + if (len > UINT_MAX) { /* XXX: cache_param max header size */ + VSLb(hp->vsl, SLT_BogoHeader, "Header too large: %.*s", + (int)(len > 20 ? 20 : len), b); + return (H2E_ENHANCE_YOUR_CALM); + } + + if (b[0] == ':') { + /* Match H/2 pseudo headers */ + /* XXX: Should probably have some include tbl for + pseudo-headers */ + if (!strncmp(b, ":method: ", namelen)) { + b += namelen; + len -= namelen; + n = HTTP_HDR_METHOD; + } else if (!strncmp(b, ":path: ", namelen)) { + b += namelen; + len -= namelen; + n = HTTP_HDR_URL; + } else if (!strncmp(b, ":scheme: ", namelen)) { + /* XXX: What to do about this one? (typically + "http" or "https"). For now set it as a normal + header, stripping the first ':'. */ + b++; + n = hp->nhd; + } else if (!strncmp(b, ":authority: ", namelen)) { + b+=6; + memcpy(b, "host", 4); + n = hp->nhd; + } else { + /* Unknown pseudo-header */ + VSLb(hp->vsl, SLT_BogoHeader, + "Unknown pseudo-header: %.*s", + (int)(len > 20 ? 20 : len), b); + return (H2E_PROTOCOL_ERROR); + } + } else + n = hp->nhd; + + if (n < HTTP_HDR_FIRST) { + /* Check for duplicate pseudo-header */ + if (n < HTTP_HDR_FIRST && hp->hd[n].b != NULL) { + VSLb(hp->vsl, SLT_BogoHeader, + "Duplicate pseudo-header: %.*s", + (int)(len > 20 ? 20 : len), b); + return (H2E_PROTOCOL_ERROR); + } + } else { + /* Check for space in struct http */ + if (n >= hp->shd) { + VSLb(hp->vsl, SLT_LostHeader, "Too many headers: %.*s", + (int)(len > 20 ? 20 : len), b); + return (H2E_ENHANCE_YOUR_CALM); + } + hp->nhd++; + } + + hp->hd[n].b = b; + hp->hd[n].e = b + len; + + return (0); +} + +void +h2h_decode_init(const struct h2_sess *h2, struct h2h_decode *d) +{ + + CHECK_OBJ_NOTNULL(h2, H2_SESS_MAGIC); + CHECK_OBJ_NOTNULL(h2->new_req, REQ_MAGIC); + CHECK_OBJ_NOTNULL(h2->new_req->http, HTTP_MAGIC); + AN(d); + INIT_OBJ(d, H2H_DECODE_MAGIC); + VHD_Init(d->vhd); + d->out_l = WS_Reserve(h2->new_req->http->ws, 0); + assert(d->out_l > 0); /* Can't do any work without any buffer + space. Require non-zero size. */ + d->out = h2->new_req->http->ws->f; + d->reset = d->out; +} + +/* Possible error returns: + * + * H2E_COMPRESSION_ERROR: Lost compression state due to incomplete header + * block. This is a connection level error. + * + * H2E_ENHANCE_YOUR_CALM: Ran out of workspace or http header space. This + * is a stream level error. + */ +int +h2h_decode_fini(const struct h2_sess *h2, struct h2h_decode *d) +{ + int ret; + + CHECK_OBJ_NOTNULL(h2, H2_SESS_MAGIC); + CHECK_OBJ_NOTNULL(h2->new_req, REQ_MAGIC); + CHECK_OBJ_NOTNULL(d, H2H_DECODE_MAGIC); + WS_ReleaseP(h2->new_req->http->ws, d->out); + if (d->vhd_ret != VHD_OK) { + /* HPACK header block didn't finish at an instruction + boundary */ + VSLb(h2->new_req->http->vsl, SLT_BogoHeader, + "HPACK compression error (%s)", VHD_Error(d->vhd_ret)); + ret = H2E_COMPRESSION_ERROR; + } else + ret = d->error; + d->magic = 0; + return (ret); +} + +/* Possible error returns: + * + * H2E_COMPRESSION_ERROR: Lost compression state due to invalid header + * block. This is a connection level error. + * + * H2E_PROTOCOL_ERROR: Malformed header or duplicate pseudo-header. + */ +int +h2h_decode_bytes(struct h2_sess *h2, struct h2h_decode *d, + const uint8_t *in, size_t in_l) +{ + struct http *hp; + size_t in_u = 0; + + CHECK_OBJ_NOTNULL(h2, H2_SESS_MAGIC); + CHECK_OBJ_NOTNULL(h2->new_req, REQ_MAGIC); + hp = h2->new_req->http; + CHECK_OBJ_NOTNULL(hp, HTTP_MAGIC); + CHECK_OBJ_NOTNULL(hp->ws, WS_MAGIC); + AN(hp->ws->r); + CHECK_OBJ_NOTNULL(d, H2H_DECODE_MAGIC); + + /* Only H2E_ENHANCE_YOUR_CALM indicates that we should continue + processing. Other errors should have been returned and handled + by the caller. */ + assert(d->error == 0 || d->error == H2E_ENHANCE_YOUR_CALM); + + while (1) { + AN(d->out); + assert(d->out_u <= d->out_l); + d->vhd_ret = VHD_Decode(d->vhd, h2->dectbl, in, in_l, &in_u, + d->out, d->out_l, &d->out_u); + + if (d->vhd_ret < 0) { + VSLb(hp->vsl, SLT_BogoHeader, + "HPACK compression error (%s)", + VHD_Error(d->vhd_ret)); + d->error = H2E_COMPRESSION_ERROR; + break; + } else if (d->vhd_ret == VHD_OK || d->vhd_ret == VHD_MORE) { + assert(in_u == in_l); + break; + } + + if (d->error == H2E_ENHANCE_YOUR_CALM) { + d->out_u = 0; + assert(d->out_u < d->out_l); + continue; + } + + switch (d->vhd_ret) { + case VHD_NAME_SEC: + /* XXX: header flag for never-indexed header */ + case VHD_NAME: + assert(d->namelen == 0); + if (d->out_l - d->out_u < 2) { + d->error = H2E_ENHANCE_YOUR_CALM; + break; + } + d->out[d->out_u++] = ':'; + d->out[d->out_u++] = ' '; + d->namelen = d->out_u; + break; + + case VHD_VALUE_SEC: + /* XXX: header flag for never-indexed header */ + case VHD_VALUE: + assert(d->namelen > 0); + if (d->out_l - d->out_u < 1) { + d->error = H2E_ENHANCE_YOUR_CALM; + break; + } + d->error = h2h_checkhdr(hp, d->out, d->namelen, + d->out_u); + if (d->error) + break; + d->error = h2h_addhdr(hp, d->out, d->namelen, d->out_u); + if (d->error) + break; + d->out[d->out_u++] = '\0'; /* Zero guard */ + d->out += d->out_u; + d->out_l -= d->out_u; + d->out_u = 0; + d->namelen = 0; + break; + + case VHD_BUF: + d->error = H2E_ENHANCE_YOUR_CALM; + break; + + default: + WRONG("Unhandled return value"); + break; + } + + if (d->error == H2E_ENHANCE_YOUR_CALM) { + http_Teardown(hp); + d->out = d->reset; + d->out_l = hp->ws->r - d->out; + d->out_u = 0; + assert(d->out_u < d->out_l); + } else if (d->error) + break; + } + + if (d->error == H2E_ENHANCE_YOUR_CALM) + return (0); /* Stream error, delay reporting until + h2h_decode_fini so that we can process the + complete header block */ + return (d->error); +} diff --git a/bin/varnishd/http2/cache_http2_panic.c b/bin/varnishd/http2/cache_http2_panic.c new file mode 100644 index 0000000..085c5c4 --- /dev/null +++ b/bin/varnishd/http2/cache_http2_panic.c @@ -0,0 +1,79 @@ +/*- + * Copyright (c) 2016 Varnish Software AS + * All rights reserved. + * + * Author: Poul-Henning Kamp + * + * 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 "../cache/cache.h" +#include "../cache/cache_filter.h" +#include "../cache/cache_transport.h" +#include "../http2/cache_http2.h" + +#include "vend.h" +#include "vsb.h" +#include "vtcp.h" +#include "vtim.h" + +void +h2_sess_panic(struct vsb *vsb, const struct sess *sp) +{ + uintptr_t *up; + struct h2_sess *h2; + struct h2_req *r2; + + AZ(SES_Get_xport_priv(sp, &up)); + + h2 = (void*)*up; + CHECK_OBJ_NOTNULL(h2, H2_SESS_MAGIC); + VSB_printf(vsb, "streams {\n"); + VSB_indent(vsb, 2); + VTAILQ_FOREACH(r2, &h2->streams, list) { + VSB_printf(vsb, "0x%08x", r2->stream); + switch(r2->state) { +#define H2_STREAM(U,sd,d) case H2_S_##U: VSB_printf(vsb, " %-6s", sd); break; +#include + default: + VSB_printf(vsb, " State %d", r2->state); + break; + } + VSB_printf(vsb, "\n"); + } + VSB_indent(vsb, -2); + VSB_printf(vsb, "}\n"); +} diff --git a/bin/varnishd/http2/cache_http2_proto.c b/bin/varnishd/http2/cache_http2_proto.c new file mode 100644 index 0000000..e7884b0 --- /dev/null +++ b/bin/varnishd/http2/cache_http2_proto.c @@ -0,0 +1,745 @@ +/*- + * Copyright (c) 2016 Varnish Software AS + * All rights reserved. + * + * Author: Poul-Henning Kamp + * + * 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 "../cache/cache.h" +#include "../cache/cache_filter.h" +#include "../cache/cache_transport.h" +#include "../http2/cache_http2.h" + +#include "vend.h" +#include "vsb.h" +#include "vtcp.h" +#include "vtim.h" + +enum h2frame { +#define H2_FRAME(l,u,t,f) H2F_##u = t, +#include "tbl/h2_frames.h" +}; + +enum h2setting { +#define H2_SETTINGS(n,v,d) H2S_##n = v, +#include "tbl/h2_settings.h" +#undef H2_SETTINGS +}; + +static const char * +h2_framename(enum h2frame h2f) +{ + + switch(h2f) { +#define H2_FRAME(l,u,t,f) case H2F_##u: return #u; +#include "tbl/h2_frames.h" + default: + return (NULL); + } +} + +static const char * +h2_settingname(enum h2setting h2f) +{ + + switch(h2f) { +#define H2_SETTINGS(n,v,d) case H2S_##n: return #n; +#include "tbl/h2_settings.h" +#undef H2_SETTINGS + default: + return (NULL); + } +} + +#define H2_FRAME_FLAGS(l,u,v) const uint8_t H2FF_##u = v; +#include "tbl/h2_frames.h" + +static const char h2_resp_101[] = + "HTTP/1.1 101 Switching Protocols\r\n" + "Connection: Upgrade\r\n" + "Upgrade: h2c\r\n" + "\r\n"; + +static const char H2_prism[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 +}; + +static const uint8_t H2_settings[] = { + 0x00, 0x03, + 0x00, 0x00, 0x00, 0x64, + 0x00, 0x04, + 0x00, 0x00, 0xff, 0xff +}; + +/**********************************************************************/ +#define DUMMY_FRAME(l) \ + void \ + h2_rx_##l(struct worker *wrk, struct h2_sess *h2, struct h2_req *r2) \ + __match_proto__(h2_frame_f) \ + { (void)wrk; (void)r2; VSLb(h2->vsl, SLT_Debug, "XXX implement " #l); INCOMPL(); } + +DUMMY_FRAME(data) +DUMMY_FRAME(rst_stream) +DUMMY_FRAME(ping) +DUMMY_FRAME(push_promise) +DUMMY_FRAME(continuation) + +/********************************************************************** + * The h2_sess struct needs many of the same things as a request, + * WS, VSL, HTC &c, but rather than implement all that stuff over, we + * grab an actual struct req, and mirror the relevant fields into + * struct h2_sess. + * To make things really incestuous, we allocate the h2_sess on + * the WS of that "Session ReQuest". + */ + +static struct h2_sess * +h2_new_sess(const struct worker *wrk, struct sess *sp, struct req *srq) +{ + uintptr_t *up; + struct h2_sess *h2; + + if (SES_Get_xport_priv(sp, &up)) { + /* Already reserved if we came via H1 */ + SES_Reserve_xport_priv(sp, &up); + *up = 0; + } + if (*up == 0) { + if (srq == NULL) + srq = Req_New(wrk, sp); + AN(srq); + h2 = WS_Alloc(srq->ws, sizeof *h2); + AN(h2); + INIT_OBJ(h2, H2_SESS_MAGIC); + h2->refcnt = 1; + h2->srq = srq; + h2->htc = srq->htc; + h2->ws = srq->ws; + h2->vsl = srq->vsl; + h2->vsl->wid = sp->vxid; + h2->htc->fd = sp->fd; + h2->sess = sp; + VTAILQ_INIT(&h2->streams); +#define H2_SETTINGS(n,v,d) \ + do { \ + assert(v < H2_SETTINGS_N); \ + h2->their_settings[v] = d; \ + h2->our_settings[v] = d; \ + } while (0); +#include "tbl/h2_settings.h" +#undef H2_SETTINGS + + /* XXX: Lacks a VHT_Fini counterpart. Will leak memory. */ + AZ(VHT_Init(h2->dectbl, + h2->our_settings[H2S_HEADER_TABLE_SIZE])); + + SES_Reserve_xport_priv(sp, &up); + *up = (uintptr_t)h2; + } + AN(up); + CAST_OBJ_NOTNULL(h2, (void*)(*up), H2_SESS_MAGIC); + return (h2); +} + +/********************************************************************** + */ + +static struct h2_req * +h2_new_req(const struct worker *wrk, struct h2_sess *h2, + unsigned stream, struct req *req) +{ + struct h2_req *r2; + + Lck_AssertHeld(&h2->sess->mtx); + if (req == NULL) + req = Req_New(wrk, h2->sess); + CHECK_OBJ_NOTNULL(req, REQ_MAGIC); + + r2 = WS_Alloc(req->ws, sizeof *r2); + AN(r2); + INIT_OBJ(r2, H2_REQ_MAGIC); + r2->h2sess = h2; + r2->stream = stream; + r2->req = req; + req->transport_priv = r2; + // XXX: ordering ? + VTAILQ_INSERT_TAIL(&h2->streams, r2, list); + h2->refcnt++; + return (r2); +} + +static void +h2_del_req(struct worker *wrk, struct h2_req *r2, enum h2_error_e err) +{ + struct h2_sess *h2; + int r; + + (void)err; + h2 = r2->h2sess; + Lck_Lock(&h2->sess->mtx); + assert(h2->refcnt > 0); + r = --h2->refcnt; + /* XXX: PRIORITY reshuffle */ + VTAILQ_REMOVE(&h2->streams, r2, list); + Lck_Unlock(&h2->sess->mtx); + Req_Cleanup(h2->sess, wrk, r2->req); + if (r) + return; + SES_Delete(h2->sess, SC_RX_JUNK, NAN); +} + +/********************************************************************** + * Update and VSL a single SETTING rx'ed from the other side + * 'd' must point to six bytes. + */ + +static void +h2_setting(struct h2_sess *h2, const uint8_t *d) +{ + uint16_t x; + uint32_t y; + const char *n; + char nb[8]; + + x = vbe16dec(d); + y = vbe32dec(d + 2); + n = h2_settingname((enum h2setting)x); + if (n == NULL) { + bprintf(nb, "0x%04x", x); + n = nb; + } + VSLb(h2->vsl, SLT_Debug, "H2SETTING %s 0x%08x", n, y); + if (x > 0 && x < H2_SETTINGS_N) + h2->their_settings[x] = y; +} + +/**********************************************************************/ + +static void +h2_vsl_frame(const struct h2_sess *h2, const void *ptr, size_t len) +{ + const uint8_t *b; + struct vsb *vsb; + const char *p; + unsigned u; + + AN(ptr); + assert(len >= 9); + b = ptr; + + VSLb_bin(h2->vsl, SLT_H2RxHdr, 9, b); + if (len > 9) + VSLb_bin(h2->vsl, SLT_H2RxBody, len - 9, b + 9); + + u = vbe32dec(b) >> 8; + + vsb = VSB_new_auto(); + AN(vsb); + p = h2_framename((enum h2frame)b[3]); + if (p != NULL) + VSB_cat(vsb, p); + else + VSB_quote(vsb, b + 3, 1, VSB_QUOTE_HEX); + VSB_printf(vsb, "[%u] ", u); + VSB_quote(vsb, b + 4, 1, VSB_QUOTE_HEX); + VSB_putc(vsb, ' '); + VSB_quote(vsb, b + 5, 4, VSB_QUOTE_HEX); + if (u > 0) { + VSB_putc(vsb, ' '); + VSB_quote(vsb, b + 9, u, VSB_QUOTE_HEX); + } + AZ(VSB_finish(vsb)); + VSLb(h2->vsl, SLT_Debug, "H2RXF %s", VSB_data(vsb)); + VSB_destroy(&vsb); +} + + +/********************************************************************** + */ + +void __match_proto__(h2_frame_f) +h2_rx_goaway(struct worker *wrk, struct h2_sess *h2, struct h2_req *r2) +{ + uint32_t error; + + (void)wrk; + (void)r2; + h2->go_away_last_stream = vbe32dec(h2->rxf_data); + error = vbe32dec(h2->rxf_data + 4); + h2->go_away = 1; +} + +void __match_proto__(h2_frame_f) +h2_rx_window_update(struct worker *wrk, struct h2_sess *h2, struct h2_req *r2) +{ + uint32_t wu; + + (void)wrk; + Lck_AssertHeld(&h2->sess->mtx); + xxxassert(h2->rxf_len == 4); // conn FRAME_SIZE_ERROR + wu = vbe32dec(h2->rxf_data); + xxxassert(wu != 0); // stream PROTOCOL_ERROR + r2->window += wu; + xxxassert(r2->window < (1LLU<<32)); // FLOW_CONTROL_ERROR +} + +/********************************************************************** + * Incoming PRIORITY, possibly an ACK of one we sent. + */ + +void __match_proto__(h2_frame_f) +h2_rx_priority(struct worker *wrk, struct h2_sess *h2, struct h2_req *r2) +{ + + (void)wrk; + (void)h2; + xxxassert(r2->stream & 1); +} + +/********************************************************************** + * Incoming SETTINGS, possibly an ACK of one we sent. + */ + +void __match_proto__(h2_frame_f) +h2_rx_settings(struct worker *wrk, struct h2_sess *h2, struct h2_req *r2) +{ + const uint8_t *p = h2->rxf_data; + unsigned l = h2->rxf_len; + + (void)wrk; + (void)r2; + AZ(h2->rxf_stream); + if (h2->rxf_flags == H2FF_SETTINGS_ACK) { + XXXAZ(h2->rxf_len); + } else if (h2->rxf_flags == 0) { + for (;l >= 6; l -= 6, p += 6) + h2_setting(h2, p); + if (l > 0) + VSLb(h2->vsl, SLT_Debug, + "NB: SETTINGS had %u dribble-bytes", l); + H2_Send_Frame(wrk, h2, + H2_FRAME_SETTINGS, H2FF_SETTINGS_ACK, 0, 0, NULL); + } else { + WRONG("SETTINGS FRAME"); + } +} + +/********************************************************************** + * Incoming HEADERS, this is where the partys at... + */ + +static void __match_proto__(task_func_t) +h2_do_req(struct worker *wrk, void *priv) +{ + struct req *req; + struct h2_req *r2; + + CAST_OBJ_NOTNULL(req, priv, REQ_MAGIC); + CAST_OBJ_NOTNULL(r2, req->transport_priv, H2_REQ_MAGIC); + assert(CNT_Request(wrk, req) != REQ_FSM_DISEMBARK); + VSL(SLT_Debug, 0, "H2REQ CNT done"); + /* XXX clean up req */ + h2_del_req(wrk, r2, H2E_NO_ERROR); +} + +void __match_proto__(h2_frame_f) +h2_rx_headers(struct worker *wrk, struct h2_sess *h2, struct h2_req *r2) +{ + struct req *req; + struct h2h_decode d[1]; + const uint8_t *p; + size_t l; + int i; + + /* XXX: This still lacks support for CONTINUATION frames, half + * read frames and proper error handling. + */ + + assert(h2->rxf_stream & 1); + + req = r2->req; + CHECK_OBJ_NOTNULL(req, REQ_MAGIC); + + req->vsl->wid = VXID_Get(wrk, VSL_CLIENTMARKER); + VSLb(req->vsl, SLT_Begin, "req %u rxreq", VXID(req->sp->vxid)); + VSL(SLT_Link, req->sp->vxid, "req %u rxreq", VXID(req->vsl->wid)); + + h2->new_req = req; + req->sp = h2->sess; + req->req_step = R_STP_RECV; + req->transport = &H2_transport; + + req->t_first = VTIM_real(); + req->t_req = VTIM_real(); + req->t_prev = req->t_first; + VSLb_ts_req(req, "Start", req->t_first); + VCL_Refresh(&wrk->vcl); + req->vcl = wrk->vcl; + wrk->vcl = NULL; + + HTTP_Setup(req->http, req->ws, req->vsl, SLT_ReqMethod); + h2h_decode_init(h2, d); + /* XXX: Error handling */ + p = h2->rxf_data; + l = h2->rxf_len; + if (h2->rxf_flags & 0x20) { + p += 5; + l -= 5; + } + i = h2h_decode_bytes(h2, d, p, l); + if (i) + VSLb(h2->vsl, SLT_Debug, "H2H_decode_bytes = %d", i); + XXXAZ(i); + i = h2h_decode_fini(h2, d); + if (i) + VSLb(h2->vsl, SLT_Debug, "H2H_decode_fini = %d", i); + XXXAZ(i); + VSLb_ts_req(req, "Req", req->t_req); + http_SetH(req->http, HTTP_HDR_PROTO, "HTTP/2.0"); + + req->req_body_status = REQ_BODY_NONE; + wrk->stats->client_req++; + wrk->stats->s_req++; + req->ws_req = WS_Snapshot(req->ws); + HTTP_Copy(req->http0, req->http); + + req->task.func = h2_do_req; + req->task.priv = req; + XXXAZ(Pool_Task(wrk->pool, &req->task, TASK_QUEUE_REQ)); +} + +/**********************************************************************/ + +enum htc_status_e __match_proto__(htc_complete_f) +H2_prism_complete(struct http_conn *htc) +{ + int l; + + CHECK_OBJ_NOTNULL(htc, HTTP_CONN_MAGIC); + l = htc->rxbuf_e - htc->rxbuf_b; + if (l < strlen(H2_prism)) + return (HTC_S_MORE); + if (!memcmp(htc->rxbuf_b, H2_prism, sizeof(H2_prism))) + return (HTC_S_COMPLETE); + return (HTC_S_JUNK); +} + +static enum htc_status_e __match_proto__(htc_complete_f) +h2_frame_complete(struct http_conn *htc) +{ + int l; + unsigned u; + + CHECK_OBJ_NOTNULL(htc, HTTP_CONN_MAGIC); + l = htc->rxbuf_e - htc->rxbuf_b; + if (l < 9) + return (HTC_S_MORE); + u = vbe32dec(htc->rxbuf_b) >> 8; + VSL(SLT_Debug, 0, "RX %p %d %u", htc->rxbuf_b, l, u); + if (l < u + 9) // XXX: Only for !DATA frames + return (HTC_S_MORE); + return (HTC_S_COMPLETE); +} + +static int +h2_rxframe(struct worker *wrk, struct h2_sess *h2) +{ + enum htc_status_e hs; + enum h2frame ft; + struct h2_req *r2 = NULL; + + (void)VTCP_blocking(h2->htc->fd); + h2->sess->t_idle = VTIM_real(); + hs = HTC_RxStuff(h2->htc, h2_frame_complete, + NULL, NULL, NAN, + h2->sess->t_idle + cache_param->timeout_idle + 100, + 1024); + if (hs != HTC_S_COMPLETE) { + Lck_Lock(&h2->sess->mtx); + VSLb(h2->vsl, SLT_Debug, "H2: No frame (hs=%d)", hs); + Lck_Unlock(&h2->sess->mtx); + return (0); + } + + h2->rxf_len = vbe32dec(h2->htc->rxbuf_b) >> 8; + h2->rxf_flags = h2->htc->rxbuf_b[4]; + h2->rxf_stream = vbe32dec(h2->htc->rxbuf_b + 5); + h2->rxf_data = (void*)(h2->htc->rxbuf_b + 9); + /* XXX: later full DATA will not be rx'ed yet. */ + HTC_RxPipeline(h2->htc, h2->htc->rxbuf_b + h2->rxf_len + 9); + + Lck_Lock(&h2->sess->mtx); + VTAILQ_FOREACH(r2, &h2->streams, list) + if (r2->stream == h2->rxf_stream) + break; + if (r2 == NULL) { + xxxassert(h2->rxf_stream > h2->highest_stream); + h2->highest_stream = h2->rxf_stream; + r2 = h2_new_req(wrk, h2, h2->rxf_stream, NULL); + } + + h2_vsl_frame(h2, h2->htc->rxbuf_b, 9L + h2->rxf_len); + + ft = (enum h2frame)h2->htc->rxbuf_b[3]; + switch (ft) { +#define H2_FRAME(l,u,t,f) \ + case H2F_##u: \ + if (!(h2->rxf_flags & ~f)) { \ + h2_rx_##l(wrk, h2, r2); \ + } else { \ + VSLb(h2->vsl, SLT_Debug, \ + "H2: Bad flags 0x%02x on " #u, \ + h2->rxf_flags); \ + Lck_Unlock(&h2->sess->mtx); \ + return (0); \ + } \ + break; +#include "tbl/h2_frames.h" + default: + INCOMPL(); + } + Lck_Unlock(&h2->sess->mtx); + return (1); +} + + +/********************************************************************** + * Deal with the base64url (NB: ...url!) encoded SETTINGS in the H1 req + * of a H2C upgrade. + */ + +static int +h2_b64url_settings(struct h2_sess *h2, struct req *req) +{ + const char *p, *q; + uint8_t u[6], *up; + unsigned x; + int i, n; + static const char s[] = + "ABCDEFGHIJKLMNOPQRSTUVWXYZ" + "abcdefghijklmnopqrstuvwxyz" + "0123456789" + "-_="; + + /* + * If there is trouble with this, we could reject the upgrade + * but putting this on the H1 side is just plain wrong... + */ + AN(http_GetHdr(req->http, H_HTTP2_Settings, &p)); + if (p == NULL) + return (-1); + VSLb(req->vsl, SLT_Debug, "H2CS %s", p); + + n = 0; + x = 0; + up = u; + for(;*p; p++) { + q = strchr(s, *p); + if (q == NULL) + return (-1); + i = q - s; + assert(i >= 0 && i <= 63); + x <<= 6; + x |= i; + n += 6; + if (n < 8) + continue; + *up++ = (uint8_t)(x >> (n - 8)); + n -= 8; + if (up == u + sizeof u) { + AZ(n); + h2_setting(h2, (void*)u); + up = u; + } + } + if (up != u) + return (-1); + return (0); +} + +/**********************************************************************/ + +static int +h2_new_pu_session(struct worker *wrk, const struct h2_sess *h2) +{ + enum htc_status_e hs; + + (void)wrk; + + hs = H2_prism_complete(h2->htc); + if (hs == HTC_S_MORE) { + VSLb(h2->vsl, SLT_Debug, "Short pu PRISM"); + return (0); + } + if (hs != HTC_S_COMPLETE) { + VSLb(h2->vsl, SLT_Debug, "Wrong pu PRISM"); + return (0); + } + HTC_RxPipeline(h2->htc, h2->htc->rxbuf_b + sizeof(H2_prism)); + HTC_RxInit(h2->htc, wrk->aws); + + VSLb(h2->vsl, SLT_Debug, "H2: Got pu PRISM"); + return (1); +} + +/**********************************************************************/ + +static int +h2_new_ou_session(struct worker *wrk, struct h2_sess *h2, + struct req *req) +{ + ssize_t sz; + enum htc_status_e hs; + + sz = write(h2->sess->fd, h2_resp_101, strlen(h2_resp_101)); + assert(sz == strlen(h2_resp_101)); + + AZ(h2_b64url_settings(h2, req)); + http_Unset(req->http, H_Upgrade); + http_Unset(req->http, H_HTTP2_Settings); + + /* Steal pipelined read-ahead, if any */ + h2->htc->pipeline_b = req->htc->pipeline_b; + h2->htc->pipeline_e = req->htc->pipeline_e; + req->htc->pipeline_b = NULL; + req->htc->pipeline_e = NULL; + HTC_RxInit(h2->htc, wrk->aws); + + /* Start req thread */ + (void)h2_new_req(wrk, h2, 1, req); + req->req_step = R_STP_RECV; + req->transport = &H2_transport; + req->task.func = h2_do_req; + req->task.priv = req; + req->err_code = 0; + http_SetH(req->http, HTTP_HDR_PROTO, "HTTP/2.0"); + XXXAZ(Pool_Task(wrk->pool, &req->task, TASK_QUEUE_REQ)); + + /* Wait for PRISM response */ + hs = HTC_RxStuff(h2->htc, H2_prism_complete, + NULL, NULL, NAN, h2->sess->t_idle + cache_param->timeout_idle, 256); + if (hs != HTC_S_COMPLETE) { + /* XXX clean up req thread */ + VSLb(h2->vsl, SLT_Debug, "H2: No OU PRISM (hs=%d)", hs); + Req_Release(req); + Lck_Unlock(&h2->sess->mtx); + SES_Delete(h2->sess, SC_RX_JUNK, NAN); + return (0); + } + HTC_RxPipeline(h2->htc, h2->htc->rxbuf_b + sizeof(H2_prism)); + HTC_RxInit(h2->htc, wrk->aws); + VSLb(h2->vsl, SLT_Debug, "H2: Got PRISM"); + return (1); +} + +static void __match_proto__(task_func_t) +h2_new_session(struct worker *wrk, void *arg) +{ + struct req *req; + struct sess *sp; + struct h2_sess *h2; + struct h2_req *r2; + char *wsp; + + CHECK_OBJ_NOTNULL(wrk, WORKER_MAGIC); + CAST_OBJ_NOTNULL(req, arg, REQ_MAGIC); + sp = req->sp; + CHECK_OBJ_NOTNULL(sp, SESS_MAGIC); + + assert(req->transport == &H2_transport); + + THR_SetRequest(req); + wsp = WS_Snapshot(wrk->aws); + + switch(req->err_code) { + case 0: + /* Direct H2 connection (via Proxy) */ + h2 = h2_new_sess(wrk, sp, req); + Lck_Lock(&h2->sess->mtx); + (void)h2_new_req(wrk, h2, 0, NULL); + break; + case 1: + /* Prior Knowledge H1->H2 upgrade */ + h2 = h2_new_sess(wrk, sp, req); + Lck_Lock(&h2->sess->mtx); + (void)h2_new_req(wrk, h2, 0, NULL); + + if (!h2_new_pu_session(wrk, h2)) + return; + break; + case 2: + /* Optimistic H1->H2 upgrade */ + h2 = h2_new_sess(wrk, sp, NULL); + Lck_Lock(&h2->sess->mtx); + (void)h2_new_req(wrk, h2, 0, NULL); + + if (!h2_new_ou_session(wrk, h2, req)) + return; + break; + default: + WRONG("Bad req->err_code"); + } + + THR_SetRequest(h2->srq); + + H2_Send_Frame(wrk, h2, + H2_FRAME_SETTINGS, H2FF_NONE, sizeof H2_settings, 0, H2_settings); + + /* and off we go... */ + Lck_Unlock(&h2->sess->mtx); + + while (h2_rxframe(wrk, h2)) { + WS_Reset(wrk->aws, wsp); + HTC_RxInit(h2->htc, wrk->aws); + } + + r2 = VTAILQ_FIRST(&h2->streams); + CHECK_OBJ_NOTNULL(r2, H2_REQ_MAGIC); + assert(r2->stream == 0); + h2_del_req(wrk, r2, H2E_NO_ERROR); +} + +struct transport H2_transport = { + .name = "H2", + .magic = TRANSPORT_MAGIC, + .new_session = h2_new_session, + .sess_panic = h2_sess_panic, + .deliver = h2_deliver, +}; diff --git a/bin/varnishd/http2/cache_http2_send.c b/bin/varnishd/http2/cache_http2_send.c new file mode 100644 index 0000000..70f48bf --- /dev/null +++ b/bin/varnishd/http2/cache_http2_send.c @@ -0,0 +1,114 @@ +/*- + * Copyright (c) 2016 Varnish Software AS + * All rights reserved. + * + * Author: Poul-Henning Kamp + * + * 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 "../cache/cache.h" +#include "../cache/cache_filter.h" +#include "../cache/cache_transport.h" +#include "../http2/cache_http2.h" + +#include "vend.h" +#include "vsb.h" + +static void +h2_mk_hdr(uint8_t *hdr, enum h2_frame_e type, uint8_t flags, + uint32_t len, uint32_t stream) +{ + + AN(hdr); + assert(len < (1U << 24)); + vbe32enc(hdr, len << 8); + hdr[3] = (uint8_t)type; + hdr[4] = flags; + vbe32enc(hdr + 5, stream); +} + +/* + * This is the "raw" frame sender, all per stream accounting and + * prioritization must have happened before this is called, and + * the session mtx must be held. + */ + +int +H2_Send_Frame(struct worker *wrk, const struct h2_sess *h2, + enum h2_frame_e type, uint8_t flags, + uint32_t len, uint32_t stream, const void *ptr) +{ + uint8_t hdr[9]; + + Lck_AssertHeld(&h2->sess->mtx); + (void)wrk; + + h2_mk_hdr(hdr, type, flags, len, stream); + VSLb_bin(h2->vsl, SLT_H2TxHdr, 9, hdr); + + /*XXX*/(void)write(h2->sess->fd, hdr, sizeof hdr); + if (len > 0) { + /*XXX*/(void)write(h2->sess->fd, ptr, len); + VSLb_bin(h2->vsl, SLT_H2TxBody, len, ptr); + } + return (0); +} + +/* + * This is the per-stream frame sender. + * XXX: windows + * XXX: priority + */ + +int +H2_Send(struct worker *wrk, struct h2_req *r2, int flush, + enum h2_frame_e type, uint8_t flags, uint32_t len, const void *ptr) +{ + int retval; + struct h2_sess *h2; + + (void)flush; + + CHECK_OBJ_NOTNULL(r2, H2_REQ_MAGIC); + h2 = r2->h2sess; + CHECK_OBJ_NOTNULL(h2, H2_SESS_MAGIC); + + Lck_Lock(&h2->sess->mtx); + retval = H2_Send_Frame(wrk, h2, type, flags, len, r2->stream, ptr); + Lck_Unlock(&h2->sess->mtx); + return (retval); +} From phk at FreeBSD.org Tue Aug 30 11:54:10 2016 From: phk at FreeBSD.org (Poul-Henning Kamp) Date: Tue, 30 Aug 2016 13:54:10 +0200 Subject: [master] 80b9937 Minor polish. Message-ID: commit 80b993762f2ddf457253f13ecdfae6301380ab55 Author: Poul-Henning Kamp Date: Tue Aug 30 11:53:33 2016 +0000 Minor polish. diff --git a/bin/varnishd/http2/cache_http2_deliver.c b/bin/varnishd/http2/cache_http2_deliver.c index 5d28140..9446227 100644 --- a/bin/varnishd/http2/cache_http2_deliver.c +++ b/bin/varnishd/http2/cache_http2_deliver.c @@ -110,7 +110,7 @@ h2_deliver(struct req *req, struct boc *boc, int sendbody) struct http *hp; struct sess *sp; struct h2_req *r2; - int i, err = 0; + int i, err; const struct hpack_static *hps; CHECK_OBJ_NOTNULL(req, REQ_MAGIC); diff --git a/include/tbl/vsl_tags.h b/include/tbl/vsl_tags.h index 4a37594..b4727d9 100644 --- a/include/tbl/vsl_tags.h +++ b/include/tbl/vsl_tags.h @@ -541,11 +541,11 @@ SLTM(H2RxBody, 0, "Received HTTP2 frame body", "Binary data" ) -SLTM(H2TxHdr, 0, "Received HTTP2 frame header", +SLTM(H2TxHdr, 0, "Transmit HTTP2 frame header", "Binary data" ) -SLTM(H2TxBody, 0, "Received HTTP2 frame body", +SLTM(H2TxBody, 0, "Transmit HTTP2 frame body", "Binary data" ) From phk at FreeBSD.org Tue Aug 30 11:57:09 2016 From: phk at FreeBSD.org (Poul-Henning Kamp) Date: Tue, 30 Aug 2016 13:57:09 +0200 Subject: [master] cdd6f8a Try to explain a temporary hack to gcc Message-ID: commit cdd6f8aac1e29220fbcad7882d28402534b06ff8 Author: Poul-Henning Kamp Date: Tue Aug 30 11:56:10 2016 +0000 Try to explain a temporary hack to gcc diff --git a/bin/varnishd/http2/cache_http2_deliver.c b/bin/varnishd/http2/cache_http2_deliver.c index 9446227..14b3d8a 100644 --- a/bin/varnishd/http2/cache_http2_deliver.c +++ b/bin/varnishd/http2/cache_http2_deliver.c @@ -219,6 +219,7 @@ h2_deliver(struct req *req, struct boc *boc, int sendbody) if (sendbody && req->resp_len != 0) err = VDP_DeliverObj(req); + /*XXX*/(void)err; H2_Send(req->wrk, r2, 1, H2_FRAME_DATA, H2FF_DATA_END_STREAM, 0, NULL); From phk at FreeBSD.org Tue Aug 30 12:05:10 2016 From: phk at FreeBSD.org (Poul-Henning Kamp) Date: Tue, 30 Aug 2016 14:05:10 +0200 Subject: [master] cbc900a Quench another GCC whine Message-ID: commit cbc900aef3274be57facd9c9bb4e240a59d9a2f0 Author: Poul-Henning Kamp Date: Tue Aug 30 12:04:18 2016 +0000 Quench another GCC whine diff --git a/bin/varnishd/http2/cache_http2_proto.c b/bin/varnishd/http2/cache_http2_proto.c index e7884b0..e013b6d 100644 --- a/bin/varnishd/http2/cache_http2_proto.c +++ b/bin/varnishd/http2/cache_http2_proto.c @@ -303,6 +303,7 @@ h2_rx_goaway(struct worker *wrk, struct h2_sess *h2, struct h2_req *r2) (void)r2; h2->go_away_last_stream = vbe32dec(h2->rxf_data); error = vbe32dec(h2->rxf_data + 4); + /*XXX*/(void)error; h2->go_away = 1; } From varnish-commit at varnish-cache.org Tue Aug 30 12:22:40 2016 From: varnish-commit at varnish-cache.org (mixor) Date: Tue, 30 Aug 2016 20:22:40 +0800 Subject: =?utf-8?B?dmFybmlzaC1jb21taXTvvJrkvIHkuJrlpoLkvZXlgZo=?= =?utf-8?B?5aW95YaF6YOo5a6h6K6h77yfcnUzbHo=?= Message-ID: <20160830202252408742@jpbo.com> varnish-commit???? ???????????????????????????????????????????2004?????????????????????????????????2001??2004????????????10???????61.05%?????????????????????????????????????2004?????????????2001???2.5???????34.4%????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????? ???????????????,??????????????????????????????????,??????????????????,?????????????????????????????????????????????????????????,???????????????????????????? ??????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????????? ???????????????????????????????? ????????????? 2016-8-3020:22:51 -------------- next part -------------- An HTML attachment was scrubbed... URL: -------------- next part -------------- A non-text attachment was scrubbed... Name: ??????????.docx Type: application/vnd.openxmlformats-officedocument.wordprocessingml.document Size: 21644 bytes Desc: not available URL: From nils.goroll at uplex.de Tue Aug 30 21:06:10 2016 From: nils.goroll at uplex.de (Nils Goroll) Date: Tue, 30 Aug 2016 23:06:10 +0200 Subject: [master] c2bfe9d Tolerate whitespace in declarations Message-ID: commit c2bfe9d090a8f9383f2840edae05157966bb729c Author: Nils Goroll Date: Tue Aug 30 23:04:06 2016 +0200 Tolerate whitespace in declarations This fixes a regression against 4.1 Reported by Dridi diff --git a/lib/libvcc/vmodtool.py b/lib/libvcc/vmodtool.py index 6377adc..944df58 100755 --- a/lib/libvcc/vmodtool.py +++ b/lib/libvcc/vmodtool.py @@ -244,12 +244,12 @@ def arg(txt): i = s.find(',') if i < 0: i = len(s) - a.nm = s[:i] + a.nm = s[:i].rstrip() s = s[i:] return a, s - a.nm = s[:i] - s = s[i+1:] + a.nm = s[:i].rstrip() + s = s[i+1:].lstrip() if s[0] == '"' or s[0] == "'": m = re.match("(['\"]).*?(\\1)", s) if not m: @@ -260,7 +260,7 @@ def arg(txt): i = s.find(',') if i < 0: i = len(s) - a.defval = s[:i] + a.defval = s[:i].rstrip() s = s[i:] return a,s @@ -294,6 +294,7 @@ class prototype(object): while len(s) > 0: a,s = arg(s) self.args.append(a) + s = s.lstrip() if len(s) == 0: break; assert s[0] == ',' diff --git a/lib/libvmod_debug/vmod.vcc b/lib/libvmod_debug/vmod.vcc index a3699bd..973337d 100644 --- a/lib/libvmod_debug/vmod.vcc +++ b/lib/libvmod_debug/vmod.vcc @@ -103,8 +103,8 @@ Encrypt the HTTP header with quad-ROT13 encryption, (this is approx 33% better than triple-DES). $Function STRING argtest( - STRING one, REAL two=2, STRING three="3", - STRING comma=",", INT four=4) + STRING one, REAL two =2, STRING three= "3", + STRING comma="," , INT four = 4 ) $Function INT vre_limit() From nils.goroll at uplex.de Tue Aug 30 21:43:11 2016 From: nils.goroll at uplex.de (Nils Goroll) Date: Tue, 30 Aug 2016 23:43:11 +0200 Subject: [master] 4dd0ab6 vmod doc tlc: named arguments and default values, enums Message-ID: commit 4dd0ab6805ce9c543ded51ae16516068f81ba16d Author: Nils Goroll Date: Tue Aug 30 23:42:24 2016 +0200 vmod doc tlc: named arguments and default values, enums diff --git a/bin/varnishtest/tests/m00023.vtc b/bin/varnishtest/tests/m00023.vtc index 0434b84..75970d3 100644 --- a/bin/varnishtest/tests/m00023.vtc +++ b/bin/varnishtest/tests/m00023.vtc @@ -24,7 +24,7 @@ varnish v1 -vcl+backend { } sub vcl_recv { - if (debug.match_acl(local, client.ip)) { + if (debug.match_acl(ip=client.ip, acl=local)) { return (hash); } return (synth(500)); diff --git a/doc/sphinx/reference/vmod.rst b/doc/sphinx/reference/vmod.rst index 208070a..1a55a19 100644 --- a/doc/sphinx/reference/vmod.rst +++ b/doc/sphinx/reference/vmod.rst @@ -102,6 +102,43 @@ For the std VMOD, the compiled vcc_if.h file looks like this:: Those are your C prototypes. Notice the ``vmod_`` prefix on the function names. +Named arguments and default values +---------------------------------- + +The basic vmod.vcc function declaration syntax introduced above makes all +arguments mandatory for calls from vcl - which implies that they need +to be given in order. + +Naming the arguments as in:: + + $Function BOOL match_acl(ACL acl, IP ip) + +allows calls from VCL with named arguments in any order, for example:: + + if (debug.match_acl(ip=client.ip, acl=local)) { # ... + +Named arguments also take default values, so for this example from +the debug vmod:: + + $Function STRING argtest(STRING one, REAL two=2, STRING three="3", + STRING comma=",", INT four=4) + +only argument `one` is required, so that all of the following are +valid invocations from vcl:: + + debug.argtest("1", 2.1, "3a") + debug.argtest("1", two=2.2, three="3b") + debug.argtest("1", three="3c", two=2.3) + debug.argtest("1", 2.4, three="3d") + debug.argtest("1", 2.5) + debug.argtest("1", four=6); + +The C interface does not change with named arguments and default +values, arguments remain positional and defaul values appear no +differnt to user specified values. + +`Note` that default values have to be fiven in the native C-type +syntax, see below. As a special case, ``NULL`` has to be given as ``0``. .. _ref-vmod-vcl-c-types: @@ -152,9 +189,14 @@ DURATION A time interval, as in 25 seconds. ENUM + vcc syntax: ENUM { val1, val2, ... } + + vcc example: ``ENUM { one, two, three } number="one"`` + C-type: ``const char *`` - TODO + Allows values from a set of constant strings. `Note` that the + C-type is a string, not a C enum. HEADER C-type: ``const struct gethdr_s *`` From noreply at github.com Tue Aug 30 21:53:08 2016 From: noreply at github.com (GitHub) Date: Tue, 30 Aug 2016 23:53:08 +0200 Subject: [master] e7b05d7 Typo Message-ID: commit e7b05d7064cb6a49dac27ca0c80fb902728515ad Author: Dridi Boukelmoune Date: Tue Aug 30 23:52:28 2016 +0200 Typo diff --git a/doc/sphinx/reference/vmod.rst b/doc/sphinx/reference/vmod.rst index 1a55a19..406c0f3 100644 --- a/doc/sphinx/reference/vmod.rst +++ b/doc/sphinx/reference/vmod.rst @@ -135,9 +135,9 @@ valid invocations from vcl:: The C interface does not change with named arguments and default values, arguments remain positional and defaul values appear no -differnt to user specified values. +different to user specified values. -`Note` that default values have to be fiven in the native C-type +`Note` that default values have to be given in the native C-type syntax, see below. As a special case, ``NULL`` has to be given as ``0``. .. _ref-vmod-vcl-c-types: From varnish-commit at varnish-cache.org Tue Aug 30 22:34:19 2016 From: varnish-commit at varnish-cache.org (ohqytb) Date: Wed, 31 Aug 2016 06:34:19 +0800 Subject: =?utf-8?B?dmFybmlzaC1jb21taXTvvJrotKjph4/mmK/kuIDnp40=?= =?utf-8?B?6LSj5Lu777yM6LSo6YeP5bCx5piv5LyB5Lia55qE56ue5LqJ5Yqb?= Message-ID: <20160831063427151105@ysoe.com> ??????TQM?????? ??????????? ?????2016?9?9-10????9?22-23? ?? ????????????????????????????????????????? ?????3000?/?(??????????????) ?????0755-61280006, 010-51299910 18917870808 ??? QQ/??? 320588808 ????????????????????????????????? ????? ????????????????????????????????????????????????????????????????????????????????????????????????????????????????????? ???????????????????????????????????????????? ??????????????????????????????????????????????????????????????????????????????????????????????????????????? ?????????????????????????????????????????????????????????????????????????????????????????????????????????????????? ??????????????????????????????????????????????????????????????????????????TQM????????????????????????????????????????????????????TQM??????????????????????????????????TQM?????????????????????????????????????????????????????TQM????????????????????????????????????????????????? ???? 1.??????????????????????? 2.???????????,???????????????? 3.????????????????,????????????????? 4.?????TQM???TQM?????????? 5.???????????,????????? 6.????,?????????????????????????? 7.?????????????????????????????? 8.???????????????????????? 9.?????????,???????FMEA??????? 10.??????QCC??????,??????????? 11.??????????????,????????????? ????? ?????????????? 1?21???????? 2?????? 3???????????? 4???????? 5??????????????? 6?????????? 7??????????????? 8??????????? 9??????????? 10??????????????? 11?????????????? ???1?????? 2??????? 3)????? 4)???????? ???1??????? 2????? ??????????????? ???1)????????????? 2)??????????????????????? ?????????????? ?????????????????? 1?????????????KPI 2???????? 3?????????? 4?????????? 5??????? 6??????????????? 7?????? 8?????????????? 9?????????? 10???????????? 11????????? 12??????????? 13??????? 14?????????????? ??????? ?????????????? ???1????????? 2?????????? ?????? ?????????????? ??????? ?? ???????? 1???????????? 2????????????????? 3???????????????? 4????????????? 5?ISO9000??????????? 6?????????????? ???1???????? 2???? ??? ????????:1)????,???? 2)?????????????? ?????????? ?????? ??? ??TQM?????? 1??????? 2?TQM????? 3?TQM?????? 4????????????? 5?????????? 6?????????? 7?????????????? ???1????????? 2?????????? ????????????? ??????????? 1??????????????? 2??????????? 3???????????? 4?????????? 5????????? 6???????????? 7?????????? 8?????????????? ?????1???????????? 2??????????????? ?????1????????? 2?????? ????:??????????????? ????????????? 1??????????? 2????????????? 3????????????? 4???????????????? 5?????????????? ???????????????? ????????????????? ???1?????????????? 2?????????????? ????????????? 1???????????????? 2??????????????? 3???????????? 4???????????? 5?????????????? ????????????? ????????????????? ????????? ???????????? ??????????? ?????????CP ? ??????? ???????????? 1??????????????? 2??????????????? 3?????????? 4?????????? 5???????????? 6?????????????? 7???????????? 8???????????? 9?????????????? ???1????????????? 2???????????? ?????1)?????? 2?????? 3???????? ??????????????????????? ??????????????? ?? ??????????? 1??????????????? 2?QC????????? 3???? 4???8D 5?QCC?????? 6?FMEA???? 7?FMEA???? 8????????????? 9?????????????? ?????????8D???? ???????????????? ?????????? ???1???????FMEA???? 2???????8D???? ?????????????????? ?????????????????? ????? ???? ????????????????????? ???TWI?????? ??????????? ????????????? ???????????? ????????????????? ??????????????? ????????????????? ?????????? ???? 1997.3---1999.2 ??????????????????????????? 1999.3---2003.9 ????????????????? 2003.9---2008.9 ????????? 2008.9 ?? ???????? ???????????????????????????????????????????????????????????? ?2014??????????????????????????????????????????????????????????????????????????? ??????? ?????????????????????????????????????????????? ?????????????????????????????????????? ????????????????????????????? ????????????????????????????????????????????? ??????????????????????????????????????????? ?????????????????????????????????? ????????????????? ?????????????????????????? ??????????????????????????????? ???????????????????????? ???????????????????????????????????????? ??????????????????????????????? ??????????????????? ????????????????????? ???????????????????? ???????????????????????????? ?????? 2013??????????????????????????????? ?2011????????????????????????????????????????????????????????????? 2013????????????????TQM??????????????????93???? ???????????????????????? ------???? ???????????????????????????? ------???? ?????????????????????????????????????????? ------???? ??????????????????????????????????------???????????????????????????????????????? ------???? ????????????????????????????????????------????????????????????????????????------???? ?????????????? ------???? ????? ??????? ???????TQM??????? ???????? ????????????? ????????? ?????????????? ?TPM??????? ????????????? ???????? ???????? ???????? ?TQM??????? ?TPM??????? ?????? ?7S??? ???? ??????????? ?????????????? ??????????? -------------- next part -------------- An HTML attachment was scrubbed... URL: From guillaume at varnish-software.com Wed Aug 31 11:52:14 2016 From: guillaume at varnish-software.com (Guillaume Quintard) Date: Wed, 31 Aug 2016 13:52:14 +0200 Subject: [master] 6da0cfd Check malloc return Message-ID: commit 6da0cfdae50c64dbe4af050fc0c19c78a496cd10 Author: Guillaume Quintard Date: Tue Aug 30 10:25:25 2016 +0200 Check malloc return diff --git a/lib/libvarnish/vcli_proto.c b/lib/libvarnish/vcli_proto.c index 27e2104..3c6ede8 100644 --- a/lib/libvarnish/vcli_proto.c +++ b/lib/libvarnish/vcli_proto.c @@ -168,6 +168,7 @@ VCLI_ReadResult(int fd, unsigned *status, char **ptr, double tmo) *status = u; p = malloc(v + 1L); + AN(p); if (p == NULL) break; From guillaume at varnish-software.com Wed Aug 31 11:52:14 2016 From: guillaume at varnish-software.com (Guillaume Quintard) Date: Wed, 31 Aug 2016 13:52:14 +0200 Subject: [master] 8e910c3 Typo Message-ID: commit 8e910c3d1cdfe5c819edb15ae044504c8e697eaa Author: Guillaume Quintard Date: Tue Aug 30 10:37:29 2016 +0200 Typo diff --git a/bin/varnishtest/vtc_server.c b/bin/varnishtest/vtc_server.c index 22b9849..e8b1928 100644 --- a/bin/varnishtest/vtc_server.c +++ b/bin/varnishtest/vtc_server.c @@ -166,7 +166,7 @@ server_thread(void *priv) l = sizeof addr_s; fd = accept(s->sock, addr, &l); if (fd < 0) - vtc_log(vl, 0, "Accepted failed: %s", strerror(errno)); + vtc_log(vl, 0, "Accept failed: %s", strerror(errno)); vtc_log(vl, 3, "accepted fd %d", fd); fd = http_process(vl, s->spec, fd, &s->sock); vtc_log(vl, 3, "shutting fd %d", fd); From guillaume at varnish-software.com Wed Aug 31 11:52:14 2016 From: guillaume at varnish-software.com (Guillaume Quintard) Date: Wed, 31 Aug 2016 13:52:14 +0200 Subject: [master] 910f501 Add useless break statement to please Coverity Message-ID: commit 910f5016cf057320fd67b4070b36252783890426 Author: Guillaume Quintard Date: Tue Aug 30 10:41:15 2016 +0200 Add useless break statement to please Coverity diff --git a/bin/varnishtest/vtc_barrier.c b/bin/varnishtest/vtc_barrier.c index 3498ee5..af4c502 100644 --- a/bin/varnishtest/vtc_barrier.c +++ b/bin/varnishtest/vtc_barrier.c @@ -342,6 +342,7 @@ barrier_sync(struct barrier *b, struct vtclog *vl) case BARRIER_NONE: vtc_log(vl, 0, "Barrier(%s) use error: not initialized", b->name); + break; case BARRIER_COND: barrier_cond_sync(b, vl); break; From guillaume at varnish-software.com Wed Aug 31 11:52:14 2016 From: guillaume at varnish-software.com (Guillaume Quintard) Date: Wed, 31 Aug 2016 13:52:14 +0200 Subject: [master] 990857e Check pthread_cond_signal returns Message-ID: commit 990857ead1742f189dcbc3acabf319ac89f85416 Author: Guillaume Quintard Date: Tue Aug 30 10:43:32 2016 +0200 Check pthread_cond_signal returns diff --git a/bin/varnishtest/vtc_barrier.c b/bin/varnishtest/vtc_barrier.c index af4c502..a5e5b47 100644 --- a/bin/varnishtest/vtc_barrier.c +++ b/bin/varnishtest/vtc_barrier.c @@ -146,7 +146,7 @@ barrier_sock_thread(void *priv) sock = VTCP_listen_on("127.0.0.1:0", NULL, b->expected, &err); if (sock < 0) { - pthread_cond_signal(&b->cond); + AZ(pthread_cond_signal(&b->cond)); AZ(pthread_mutex_unlock(&b->mtx)); vtc_log(vl, 0, "Barrier(%s) %s fails: %s (errno=%d)", b->name, err, strerror(errno), errno); @@ -159,7 +159,7 @@ barrier_sock_thread(void *priv) macro_def(vl, b->name, "port", "%s", pbuf); macro_def(vl, b->name, "sock", "%s:%s", abuf, pbuf); - pthread_cond_signal(&b->cond); + AZ(pthread_cond_signal(&b->cond)); AZ(pthread_mutex_unlock(&b->mtx)); conns = calloc(b->expected, sizeof *conns); From guillaume at varnish-software.com Wed Aug 31 11:52:14 2016 From: guillaume at varnish-software.com (Guillaume Quintard) Date: Wed, 31 Aug 2016 13:52:14 +0200 Subject: [master] 00c7e7b Protect against use-after-free/unitialized Message-ID: commit 00c7e7b24201f2a5a7cb5c2ae9c31173dd6da6ef Author: Guillaume Quintard Date: Tue Aug 30 11:11:35 2016 +0200 Protect against use-after-free/unitialized diff --git a/bin/varnishtest/vtc_varnish.c b/bin/varnishtest/vtc_varnish.c index 6f58bf1..47b7709 100644 --- a/bin/varnishtest/vtc_varnish.c +++ b/bin/varnishtest/vtc_varnish.c @@ -136,7 +136,7 @@ varnish_ask_cli(const struct varnish *v, const char *cmd, char **repl) static void wait_stopped(const struct varnish *v) { - char *r; + char *r = NULL; enum VCLI_status_e st; while (1) { @@ -150,6 +150,7 @@ wait_stopped(const struct varnish *v) break; } free(r); + r = NULL; (void)usleep(200000); } } @@ -160,7 +161,7 @@ wait_stopped(const struct varnish *v) static void wait_running(const struct varnish *v) { - char *r; + char *r = NULL; enum VCLI_status_e st; while (1) { @@ -180,6 +181,7 @@ wait_running(const struct varnish *v) break; } free(r); + r = NULL; (void)usleep(200000); } } @@ -392,7 +394,7 @@ varnish_launch(struct varnish *v) struct pollfd fd[2]; enum VCLI_status_e u; const char *err; - char *r; + char *r = NULL; v->vd = VSM_New(); @@ -521,6 +523,7 @@ varnish_launch(struct varnish *v) VCLI_AuthResponse(nfd, r, abuf + 5); AZ(close(nfd)); free(r); + r = NULL; strcat(abuf, "\n"); u = varnish_ask_cli(v, abuf, &r); @@ -542,7 +545,7 @@ static void varnish_start(struct varnish *v) { enum VCLI_status_e u; - char *resp, *h, *p; + char *resp = NULL, *h, *p; if (v->cli_fd < 0) varnish_launch(v); @@ -556,6 +559,7 @@ varnish_start(struct varnish *v) vtc_log(v->vl, 0, "CLI start command failed: %u %s", u, resp); wait_running(v); free(resp); + resp = NULL; u = varnish_ask_cli(v, "debug.xid 999", &resp); if (vtc_error) return; @@ -563,6 +567,7 @@ varnish_start(struct varnish *v) vtc_log(v->vl, 0, "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; From guillaume at varnish-software.com Wed Aug 31 12:03:10 2016 From: guillaume at varnish-software.com (Guillaume Quintard) Date: Wed, 31 Aug 2016 14:03:10 +0200 Subject: [master] 779c46f Check the pthread functions returns in varnishhist Message-ID: commit 779c46faaaa05a65b3232ffc8d90ad33c8ac058d Author: Guillaume Quintard Date: Wed Aug 31 14:00:49 2016 +0200 Check the pthread functions returns in varnishhist diff --git a/bin/varnishhist/varnishhist.c b/bin/varnishhist/varnishhist.c index 066d0e8..561c0a7 100644 --- a/bin/varnishhist/varnishhist.c +++ b/bin/varnishhist/varnishhist.c @@ -243,10 +243,10 @@ accumulate(struct VSL_data *vsl, struct VSL_transaction * const pt[], i = VSL_Next(tr->c); if (i == -3) { /* overrun - need to skip forward */ - pthread_mutex_lock(&mtx); + AZ(pthread_mutex_lock(&mtx)); vsl_to = vsl_t0 = vsl_ts = 0; t0 = VTIM_mono(); - pthread_mutex_unlock(&mtx); + AZ(pthread_mutex_unlock(&mtx)); break; } if (i != 1) @@ -301,7 +301,7 @@ accumulate(struct VSL_data *vsl, struct VSL_transaction * const pt[], assert(i >= 0); assert(i < hist_buckets); - pthread_mutex_lock(&mtx); + AZ(pthread_mutex_lock(&mtx)); /* * only parse the last tsp seen in this transaction - @@ -337,7 +337,7 @@ accumulate(struct VSL_data *vsl, struct VSL_transaction * const pt[], if (++next_hist == HIST_N) { next_hist = 0; } - pthread_mutex_unlock(&mtx); + AZ(pthread_mutex_unlock(&mtx)); } if (vsl_ts < vsl_to) @@ -345,7 +345,7 @@ accumulate(struct VSL_data *vsl, struct VSL_transaction * const pt[], t = VTIM_mono(); - pthread_mutex_lock(&mtx); + AZ(pthread_mutex_lock(&mtx)); if (vsl_t0 == 0) vsl_to = vsl_t0 = vsl_ts; @@ -360,7 +360,7 @@ accumulate(struct VSL_data *vsl, struct VSL_transaction * const pt[], ts.tv_sec = (long)t; (void)pthread_cond_timedwait(&timebend_cv, &mtx, &ts); } - pthread_mutex_unlock(&mtx); + AZ(pthread_mutex_unlock(&mtx)); return (0); } @@ -385,9 +385,9 @@ do_curses(void *arg) curs_set(0); erase(); for (;;) { - pthread_mutex_lock(&mtx); + AZ(pthread_mutex_lock(&mtx)); update(); - pthread_mutex_unlock(&mtx); + AZ(pthread_mutex_unlock(&mtx)); timeout(delay * 1000); switch ((ch = getch())) { From dridi.boukelmoune at gmail.com Wed Aug 31 14:11:17 2016 From: dridi.boukelmoune at gmail.com (Dridi Boukelmoune) Date: Wed, 31 Aug 2016 16:11:17 +0200 Subject: [master] 5043df4 Git-ignore HTTP/2 droppings Message-ID: commit 5043df492e3812011a6e73fdb7a59e02bcf52c98 Author: Dridi Boukelmoune Date: Wed Aug 31 16:08:03 2016 +0200 Git-ignore HTTP/2 droppings diff --git a/.gitignore b/.gitignore index b1fb502..a4943d4 100644 --- a/.gitignore +++ b/.gitignore @@ -12,7 +12,6 @@ Makefile.in *.sw[op] *.trs - # Various auto-tools artifacts /aclocal.m4 /autom4te.cache/ @@ -42,7 +41,6 @@ Makefile.in TAGS tags cscope.*out -# /varnishapi-uninstalled.pc # Default vcl made from bin/varnishd/default.vcl @@ -70,13 +68,12 @@ cscope.*out /lib/libvarnishapi/vxp_tokens.h /lib/libvarnishapi/vsl2rst - # Misc. generated files for included vmods. +/bin/varnishd/vhp_hufdec.h /lib/libvmod_*/vcc_if.c /lib/libvmod_*/vcc_if.h /lib/libvmod_*/vmod_*.rst - # Man-files and binaries /man/*.1 /man/*.3 @@ -97,6 +94,7 @@ cscope.*out /doc/*.html /doc/sphinx/build/ /doc/sphinx/conf.py + # graphviz-generated /doc/graphviz/*.pdf @@ -104,10 +102,13 @@ cscope.*out /nbproject/private/ # Test droppings +/bin/varnishd/vhp_decode_test +/bin/varnishd/vhp_gen_hufdec +/bin/varnishd/vhp_table_test /bin/varnishtest/tests/*.log /bin/varnishtest/tests/*.log-t /bin/varnishtest/test-suite.log -# + # Coverity output /cov-int /myproject.tgz