diff --git a/bin/varnishncsa/varnishncsa.c b/bin/varnishncsa/varnishncsa.c index 94af8374855..15e5eb09858 100644 --- a/bin/varnishncsa/varnishncsa.c +++ b/bin/varnishncsa/varnishncsa.c @@ -160,10 +160,48 @@ static struct ctx { const char *handling; const char *side; int64_t vxid; + int recv_compl; } CTX; +enum format_policy { + FMTPOL_INTERNAL, + FMTPOL_REQ, + FMTPOL_RESP, +}; + static void parse_format(const char *format); +static unsigned +frag_needed(const struct fragment *frag, enum format_policy fp) +{ + unsigned is_first, want_first, want_frag; + + is_first = CTX.gen != frag->gen; + + switch (fp) { + case FMTPOL_INTERNAL: + want_first = 1; + want_frag = 1; + break; + case FMTPOL_REQ: + want_first = *CTX.side == 'c'; + want_frag = !CTX.recv_compl; + break; + case FMTPOL_RESP: + want_first = *CTX.side == 'b'; + want_frag = (*CTX.side == 'c') || !CTX.recv_compl; + break; + default: + WRONG("Invalid format policy"); + } + + if (!want_frag) + return (0); + if (want_first && !is_first) + return (0); + return (1); +} + static void openout(int append) { @@ -850,7 +888,7 @@ isprefix(const char *prefix, size_t len, const char *b, } static void -frag_fields(int force, const char *b, const char *e, ...) +frag_fields(enum format_policy fp, const char *b, const char *e, ...) { va_list ap; const char *p, *q; @@ -882,7 +920,7 @@ frag_fields(int force, const char *b, const char *e, ...) assert(p != NULL && q != NULL); if (p >= e || q <= p) continue; - if (frag->gen != CTX.gen || force) { + if (frag_needed(frag, fp)) { /* We only grab the same matching field once */ frag->gen = CTX.gen; frag->b = p; @@ -893,13 +931,17 @@ frag_fields(int force, const char *b, const char *e, ...) } static void -frag_line(int force, const char *b, const char *e, struct fragment *f) +frag_line(enum format_policy fp, const char *b, const char *e, + struct fragment *f) { - if (f->gen == CTX.gen && !force) + if (!frag_needed(f, fp)) /* We only grab the same matching record once */ return; + if (e == NULL) + e = b + strlen(b); + /* Skip leading space */ while (b < e && isspace(*b)) ++b; @@ -914,7 +956,8 @@ frag_line(int force, const char *b, const char *e, struct fragment *f) } static void -process_hdr(const struct watch_head *head, const char *b, const char *e) +process_hdr(enum format_policy fp, const struct watch_head *head, const char *b, + const char *e, int unset) { struct watch *w; const char *p; @@ -923,7 +966,12 @@ process_hdr(const struct watch_head *head, const char *b, const char *e) CHECK_OBJ_NOTNULL(w, WATCH_MAGIC); if (!isprefix(w->key, w->keylen, b, e, &p)) continue; - frag_line(1, p, e, &w->frag); + if (unset) { + frag_line(fp, CTX.missing_string, + NULL, + &w->frag); + } else + frag_line(fp, p, e, &w->frag); } } @@ -942,9 +990,9 @@ process_vsl(const struct vsl_watch_head *head, enum VSL_tag_e tag, !isprefix(w->prefix, w->prefixlen, b, e, &p)) continue; if (w->idx == 0) - frag_line(0, p, e, &w->frag); + frag_line(FMTPOL_INTERNAL, p, e, &w->frag); else - frag_fields(0, p, e, w->idx, &w->frag, 0, NULL); + frag_fields(FMTPOL_INTERNAL, p, e, w->idx, &w->frag, 0, NULL); } } @@ -971,6 +1019,7 @@ dispatch_f(struct VSL_data *vsl, struct VSL_transaction * const pt[], } else continue; + CTX.recv_compl = 0; CTX.hitmiss = "-"; CTX.handling = "-"; CTX.vxid = t->vxid; @@ -993,44 +1042,44 @@ dispatch_f(struct VSL_data *vsl, struct VSL_transaction * const pt[], skip = 1; break; case SLT_PipeAcct: - frag_fields(0, b, e, + frag_fields(FMTPOL_INTERNAL, b, e, 3, &CTX.frag[F_I], 4, &CTX.frag[F_O], 0, NULL); break; case SLT_BackendOpen: - frag_fields(1, b, e, + frag_fields(FMTPOL_INTERNAL, b, e, 3, &CTX.frag[F_h], 0, NULL); break; case SLT_ReqStart: - frag_fields(0, b, e, + frag_fields(FMTPOL_INTERNAL, b, e, 1, &CTX.frag[F_h], 0, NULL); break; case SLT_BereqMethod: case SLT_ReqMethod: - frag_line(0, b, e, &CTX.frag[F_m]); + frag_line(FMTPOL_REQ, b, e, &CTX.frag[F_m]); break; case SLT_BereqURL: case SLT_ReqURL: p = memchr(b, '?', e - b); if (p == NULL) p = e; - frag_line(0, b, p, &CTX.frag[F_U]); - frag_line(0, p, e, &CTX.frag[F_q]); + frag_line(FMTPOL_REQ, b, p, &CTX.frag[F_U]); + frag_line(FMTPOL_REQ, p, e, &CTX.frag[F_q]); break; case SLT_BereqProtocol: case SLT_ReqProtocol: - frag_line(0, b, e, &CTX.frag[F_H]); + frag_line(FMTPOL_REQ, b, e, &CTX.frag[F_H]); break; case SLT_BerespStatus: case SLT_RespStatus: - frag_line(1, b, e, &CTX.frag[F_s]); + frag_line(FMTPOL_RESP, b, e, &CTX.frag[F_s]); break; case SLT_BereqAcct: case SLT_ReqAcct: - frag_fields(0, b, e, + frag_fields(FMTPOL_INTERNAL, b, e, 3, &CTX.frag[F_I], 5, &CTX.frag[F_b], 6, &CTX.frag[F_O], @@ -1039,40 +1088,49 @@ dispatch_f(struct VSL_data *vsl, struct VSL_transaction * const pt[], case SLT_Timestamp: #define ISPREFIX(a, b, c, d) isprefix(a, strlen(a), b, c, d) if (ISPREFIX("Start:", b, e, &p)) { - frag_fields(0, p, e, 1, + frag_fields(FMTPOL_INTERNAL, p, e, 1, &CTX.frag[F_tstart], 0, NULL); } else if (ISPREFIX("Resp:", b, e, &p) || ISPREFIX("PipeSess:", b, e, &p) || ISPREFIX("BerespBody:", b, e, &p)) { - frag_fields(0, p, e, 1, + frag_fields(FMTPOL_INTERNAL, p, e, 1, &CTX.frag[F_tend], 0, NULL); } else if (ISPREFIX("Process:", b, e, &p) || ISPREFIX("Pipe:", b, e, &p) || ISPREFIX("Beresp:", b, e, &p)) { - frag_fields(0, p, e, 2, + frag_fields(FMTPOL_INTERNAL, p, e, 2, &CTX.frag[F_ttfb], 0, NULL); } break; case SLT_BereqHeader: case SLT_ReqHeader: - process_hdr(&CTX.watch_reqhdr, b, e); + process_hdr(FMTPOL_REQ, &CTX.watch_reqhdr, b, e, 0); if (ISPREFIX("Authorization:", b, e, &p) && ISPREFIX("basic ", p, e, &p)) - frag_line(0, p, e, + frag_line(FMTPOL_REQ, p, e, &CTX.frag[F_auth]); else if (ISPREFIX("Host:", b, e, &p)) - frag_line(0, p, e, + frag_line(FMTPOL_REQ, p, e, &CTX.frag[F_host]); #undef ISPREFIX break; case SLT_BerespHeader: case SLT_RespHeader: - process_hdr(&CTX.watch_resphdr, b, e); + process_hdr(FMTPOL_RESP, &CTX.watch_resphdr, b, e, 0); + break; + case SLT_BereqUnset: + case SLT_ReqUnset: + process_hdr(FMTPOL_REQ, &CTX.watch_reqhdr, b, e, 1); + break; + case SLT_BerespUnset: + case SLT_RespUnset: + process_hdr(FMTPOL_RESP, &CTX.watch_resphdr, b, e, 1); break; case SLT_VCL_call: if (!strcasecmp(b, "recv")) { + CTX.recv_compl = 1; CTX.hitmiss = "-"; CTX.handling = "-"; } else if (!strcasecmp(b, "hit")) { @@ -1090,6 +1148,8 @@ dispatch_f(struct VSL_data *vsl, struct VSL_transaction * const pt[], wrong */ CTX.hitmiss = "miss"; CTX.handling = "synth"; + } else if (!strcasecmp(b, "backend_response")) { + CTX.recv_compl = 1; } break; case SLT_VCL_return: @@ -1106,7 +1166,7 @@ dispatch_f(struct VSL_data *vsl, struct VSL_transaction * const pt[], strncmp(b, w->key, w->keylen)) continue; p = b + w->keylen; - frag_line(0, p, e, &w->frag); + frag_line(FMTPOL_INTERNAL, p, e, &w->frag); } break; default: diff --git a/bin/varnishtest/tests/u00020.vtc b/bin/varnishtest/tests/u00020.vtc new file mode 100644 index 00000000000..84a8c71c3a4 --- /dev/null +++ b/bin/varnishtest/tests/u00020.vtc @@ -0,0 +1,168 @@ +varnishtest "new varnishncsa matching rules" + +# Test things we send to the backend + +server s1 { + rxreq + txresp +} -start + +varnish v1 -vcl+backend { + + sub vcl_backend_fetch { + set bereq.http.bereqhdr = "vbf-modified"; + set bereq.method = "HEAD"; + set bereq.url = "/vbf-url?q=vbfQuerry"; + set bereq.http.Authorization = "basic dmJmOnBhc3M="; + unset bereq.http.unset; + } + + sub vcl_backend_response { + set bereq.http.bereqhdr = "vbr-modified"; + set bereq.http.notsent = "notsent"; + set bereq.http.unset = "toolate"; + set bereq.method = "CONNECT"; + set bereq.url = "/vbr-url?q=vbrQuerry"; + set bereq.http.Authorization = "basic dmJyOnBhc3M="; + } + +} -start + + +client c1 { + txreq -url "/client-url?q=clientQuerry" -hdr "bereqhdr: client-header" -hdr "unset: client" -hdr "Authorization:basic Y2xpZW50OnBhc3M=" + rxresp +} -run + +shell { + varnishncsa -n ${v1_name} -d -b -F '%H %{bereqhdr}i %{notsent}i %{unset}i %m %q %U %u' > ncsa_sb.txt + + cat >expected_sb.txt <<-EOF + HTTP/1.1 vbf-modified - - HEAD ?q=vbfQuerry /vbf-url vbf + EOF + diff -u expected_sb.txt ncsa_sb.txt +} + +varnish v1 -stop + +# Test things we receive from the backend + +server s1 { + rxreq + txresp -status 202 -hdr "beresp: origin" -hdr "unset: origin" +} -start + +varnish v1 -vcl+backend { + + sub vcl_backend_response { + set beresp.http.beresp = "vbr-updated"; + set beresp.status = 200; + unset beresp.http.unset; + } + +} -start + + +client c1 { + txreq + rxresp +} -run + + +shell { + varnishncsa -n ${v1_name} -d -b -F '%s %{beresp}o %{unset}o' > ncsa_rb.txt + + cat >expected_rb.txt <<-EOF + 202 origin origin + EOF + diff -u expected_rb.txt ncsa_rb.txt +} + +varnish v1 -stop + +# Test things we send to the client + +server s1 { + rxreq + txresp -status 202 -hdr "resp: origin" -hdr "unset: origin" +} -start + +varnish v1 -vcl+backend { + + sub vcl_backend_response { + set beresp.http.resp = "vbr-updated"; + set beresp.status = 200; + } + + sub vcl_deliver { + set resp.http.resp = "deliver-updated"; + set resp.status = 201; + set resp.http.added = "deliver"; + unset resp.http.unset; + } + +} -start + + +client c1 { + txreq + rxresp +} -run + +shell { + varnishncsa -n ${v1_name} -d -c -F '%s %{resp}o %{unset}o %{added}o' > ncsa_sc.txt + + cat >expected_sc.txt <<-EOF + 201 deliver-updated - deliver + EOF + diff -u expected_sc.txt ncsa_sc.txt +} + +varnish v1 -stop + +# Test things we receive from the client + +server s1 { + rxreq + txresp +} -start + +varnish v1 -vcl+backend { + + sub vcl_recv { + set req.http.reqhdr = "recv-modified"; + set req.method = "HEAD"; + set req.url = "/recv-url?q=recvQuerry"; + set req.http.Authorization = "basic cmVjdjpwYXNz"; + set req.http.notreceived = "recv"; + unset req.http.unset; + } + + sub vcl_hash { + set req.http.reqhdr = "hash-modified"; + set req.method = "GET"; + set req.url = "/hash-url?q=hashQuerry"; + set req.http.Authorization = "basic aGFzaDpwYXNz"; + set req.http.notreceived = "hash"; + } + +} -start + + +client c1 { + txreq -req "POST" -url "/client-url?q=clientQuerry" \ + -hdr "reqhdr: client-header" \ + -hdr "Authorization:basic Y2xpZW50OnBhc3M=" \ + -hdr "unset: client" + rxresp +} -run + + +shell { + varnishncsa -n ${v1_name} -d -c -F '%H %{reqhdr}i %{notreceived}i %{unset}i %m %q %U %u' > ncsa_rc.txt + + cat >expected_rc.txt <<-EOF + HTTP/1.1 client-header - client POST ?q=clientQuerry /client-url client + EOF + diff -u expected_rc.txt ncsa_rc.txt +}