Skip to content
This repository was archived by the owner on Feb 16, 2026. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions bin/varnishd/http2/cache_http2.h
Original file line number Diff line number Diff line change
Expand Up @@ -211,11 +211,14 @@ vtr_deliver_f h2_deliver;
vtr_minimal_response_f h2_minimal_response;
#endif /* TRANSPORT_MAGIC */

#define H2H_DECODE_FLAG_SCHEME_SEEN 0x1

/* http2/cache_http2_hpack.c */
struct h2h_decode {
unsigned magic;
#define H2H_DECODE_MAGIC 0xd092bde4

int flags;
h2_error error;
enum vhd_ret_e vhd_ret;
char *out;
Expand Down
14 changes: 12 additions & 2 deletions bin/varnishd/http2/cache_http2_hpack.c
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ h2h_checkhdr(const struct http *hp, const char *b, size_t namelen, size_t len)
}

static h2_error
h2h_addhdr(struct http *hp, char *b, size_t namelen, size_t len)
h2h_addhdr(struct http *hp, char *b, size_t namelen, size_t len, int *flags)
{
/* XXX: This might belong in cache/cache_http.c */
const char *b0;
Expand Down Expand Up @@ -188,9 +188,18 @@ h2h_addhdr(struct http *hp, char *b, size_t namelen, size_t len)
/* XXX: What to do about this one? (typically
"http" or "https"). For now set it as a normal
header, stripping the first ':'. */
if (*flags & H2H_DECODE_FLAG_SCHEME_SEEN) {
VSLb(hp->vsl, SLT_BogoHeader,
"Duplicate pseudo-header %.*s%.*s",
(int)namelen, b0,
(int)(len > 20 ? 20 : len), b);
return (H2SE_PROTOCOL_ERROR);
}

b++;
len-=1;
n = hp->nhd;
*flags |= H2H_DECODE_FLAG_SCHEME_SEEN;

for (p = b + namelen, u = 0; u < len-namelen;
p++, u++) {
Expand Down Expand Up @@ -380,7 +389,8 @@ h2h_decode_bytes(struct h2_sess *h2, const uint8_t *in, size_t in_l)
d->out_u);
if (d->error)
break;
d->error = h2h_addhdr(hp, d->out, d->namelen, d->out_u);
d->error = h2h_addhdr(hp, d->out, d->namelen, d->out_u,
&d->flags);
if (d->error)
break;
d->out[d->out_u++] = '\0'; /* Zero guard */
Expand Down
9 changes: 9 additions & 0 deletions bin/varnishd/http2/cache_http2_proto.c
Original file line number Diff line number Diff line change
Expand Up @@ -601,11 +601,13 @@ static h2_error
h2_end_headers(struct worker *wrk, struct h2_sess *h2,
struct req *req, struct h2_req *r2)
{
int scheme_seen;
h2_error h2e;
ssize_t cl;

ASSERT_RXTHR(h2);
assert(r2->state == H2_S_OPEN);
scheme_seen = h2->decode->flags & H2H_DECODE_FLAG_SCHEME_SEEN;
Comment on lines +604 to +610

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'm not convinced we need this local variable when we use it just once. I will add a separate commit to maintain the authorship of this one.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fwiw, I think wrt machine code the local variable makes no difference

h2e = h2h_decode_fini(h2);
h2->new_req = NULL;
if (h2e != NULL) {
Expand Down Expand Up @@ -656,10 +658,17 @@ h2_end_headers(struct worker *wrk, struct h2_sess *h2,
VSLb(h2->vsl, SLT_Debug, "Missing :method");
return (H2SE_PROTOCOL_ERROR); //rfc7540,l,3087,3090
}

if (req->http->hd[HTTP_HDR_URL].b == NULL) {
VSLb(h2->vsl, SLT_Debug, "Missing :path");
return (H2SE_PROTOCOL_ERROR); //rfc7540,l,3087,3090
}

if (!(scheme_seen)) {
VSLb(h2->vsl, SLT_Debug, "Missing :scheme");
return (H2SE_PROTOCOL_ERROR); //rfc7540,l,3087,3090
}

AN(req->http->hd[HTTP_HDR_PROTO].b);

if (*req->http->hd[HTTP_HDR_URL].b == '*' &&
Expand Down
48 changes: 48 additions & 0 deletions bin/varnishtest/tests/t02026.vtc
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
varnishtest "Dublicate pseudo-headers"

server s1 {
rxreq
txresp
} -start

varnish v1 -arg "-p feature=+http2" -vcl+backend {
} -start

#client c1 {
# txreq -url "/some/path" -url "/some/other/path"
# rxresp
# expect resp.status == 400
#} -run

#client c1 {
# txreq -req "GET" -req "POST"
# rxresp
# expect resp.status == 400
#} -run

#client c1 {
# txreq -proto "HTTP/1.1" -proto "HTTP/2.0"
# rxresp
# expect resp.status == 400
#} -run

client c1 {
stream 1 {
txreq -url "/some/path" -url "/some/other/path"
rxrst
} -run
} -run

client c1 {
stream 1 {
txreq -scheme "http" -scheme "https"
rxrst
} -run
} -run

client c1 {
stream 1 {
txreq -req "GET" -req "POST"
rxrst
} -run
} -run