Skip to content
This repository was archived by the owner on Feb 16, 2026. It is now read-only.

Commit 1ab71cf

Browse files
asadsa92dridi
authored andcommitted
h2: the :scheme pseudo header is not optional
The :scheme pseudo header is not optional in H/2 except when doing CONNECT. There is also a strict requirement for it appear only once. Signed-off-by: Asad Sajjad Ahmed <asadsa@varnish-software.com> Conflicts: bin/varnishtest/tests/t02025.vtc
1 parent 2bf97ff commit 1ab71cf

4 files changed

Lines changed: 72 additions & 2 deletions

File tree

bin/varnishd/http2/cache_http2.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -218,11 +218,14 @@ vtr_deliver_f h2_deliver;
218218
vtr_minimal_response_f h2_minimal_response;
219219
#endif /* TRANSPORT_MAGIC */
220220

221+
#define H2H_DECODE_FLAG_SCHEME_SEEN 0x1
222+
221223
/* http2/cache_http2_hpack.c */
222224
struct h2h_decode {
223225
unsigned magic;
224226
#define H2H_DECODE_MAGIC 0xd092bde4
225227

228+
int flags;
226229
h2_error error;
227230
enum vhd_ret_e vhd_ret;
228231
char *out;

bin/varnishd/http2/cache_http2_hpack.c

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -127,7 +127,7 @@ h2h_checkhdr(const struct http *hp, const char *b, size_t namelen, size_t len)
127127
}
128128

129129
static h2_error
130-
h2h_addhdr(struct http *hp, char *b, size_t namelen, size_t len)
130+
h2h_addhdr(struct http *hp, char *b, size_t namelen, size_t len, int *flags)
131131
{
132132
/* XXX: This might belong in cache/cache_http.c */
133133
const char *b0;
@@ -188,9 +188,18 @@ h2h_addhdr(struct http *hp, char *b, size_t namelen, size_t len)
188188
/* XXX: What to do about this one? (typically
189189
"http" or "https"). For now set it as a normal
190190
header, stripping the first ':'. */
191+
if (*flags & H2H_DECODE_FLAG_SCHEME_SEEN) {
192+
VSLb(hp->vsl, SLT_BogoHeader,
193+
"Duplicate pseudo-header %.*s%.*s",
194+
(int)namelen, b0,
195+
(int)(len > 20 ? 20 : len), b);
196+
return (H2SE_PROTOCOL_ERROR);
197+
}
198+
191199
b++;
192200
len-=1;
193201
n = hp->nhd;
202+
*flags |= H2H_DECODE_FLAG_SCHEME_SEEN;
194203

195204
for (p = b + namelen, u = 0; u < len-namelen;
196205
p++, u++) {
@@ -380,7 +389,8 @@ h2h_decode_bytes(struct h2_sess *h2, const uint8_t *in, size_t in_l)
380389
d->out_u);
381390
if (d->error)
382391
break;
383-
d->error = h2h_addhdr(hp, d->out, d->namelen, d->out_u);
392+
d->error = h2h_addhdr(hp, d->out, d->namelen, d->out_u,
393+
&d->flags);
384394
if (d->error)
385395
break;
386396
d->out[d->out_u++] = '\0'; /* Zero guard */

bin/varnishd/http2/cache_http2_proto.c

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -601,11 +601,13 @@ static h2_error
601601
h2_end_headers(struct worker *wrk, struct h2_sess *h2,
602602
struct req *req, struct h2_req *r2)
603603
{
604+
int scheme_seen;
604605
h2_error h2e;
605606
ssize_t cl;
606607

607608
ASSERT_RXTHR(h2);
608609
assert(r2->state == H2_S_OPEN);
610+
scheme_seen = h2->decode->flags & H2H_DECODE_FLAG_SCHEME_SEEN;
609611
h2e = h2h_decode_fini(h2);
610612
h2->new_req = NULL;
611613
if (h2e != NULL) {
@@ -656,10 +658,17 @@ h2_end_headers(struct worker *wrk, struct h2_sess *h2,
656658
VSLb(h2->vsl, SLT_Debug, "Missing :method");
657659
return (H2SE_PROTOCOL_ERROR); //rfc7540,l,3087,3090
658660
}
661+
659662
if (req->http->hd[HTTP_HDR_URL].b == NULL) {
660663
VSLb(h2->vsl, SLT_Debug, "Missing :path");
661664
return (H2SE_PROTOCOL_ERROR); //rfc7540,l,3087,3090
662665
}
666+
667+
if (!(scheme_seen)) {
668+
VSLb(h2->vsl, SLT_Debug, "Missing :scheme");
669+
return (H2SE_PROTOCOL_ERROR); //rfc7540,l,3087,3090
670+
}
671+
663672
AN(req->http->hd[HTTP_HDR_PROTO].b);
664673

665674
if (*req->http->hd[HTTP_HDR_URL].b == '*' &&

bin/varnishtest/tests/t02026.vtc

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
varnishtest "Dublicate pseudo-headers"
2+
3+
server s1 {
4+
rxreq
5+
txresp
6+
} -start
7+
8+
varnish v1 -arg "-p feature=+http2" -vcl+backend {
9+
} -start
10+
11+
#client c1 {
12+
# txreq -url "/some/path" -url "/some/other/path"
13+
# rxresp
14+
# expect resp.status == 400
15+
#} -run
16+
17+
#client c1 {
18+
# txreq -req "GET" -req "POST"
19+
# rxresp
20+
# expect resp.status == 400
21+
#} -run
22+
23+
#client c1 {
24+
# txreq -proto "HTTP/1.1" -proto "HTTP/2.0"
25+
# rxresp
26+
# expect resp.status == 400
27+
#} -run
28+
29+
client c1 {
30+
stream 1 {
31+
txreq -url "/some/path" -url "/some/other/path"
32+
rxrst
33+
} -run
34+
} -run
35+
36+
client c1 {
37+
stream 1 {
38+
txreq -scheme "http" -scheme "https"
39+
rxrst
40+
} -run
41+
} -run
42+
43+
client c1 {
44+
stream 1 {
45+
txreq -req "GET" -req "POST"
46+
rxrst
47+
} -run
48+
} -run

0 commit comments

Comments
 (0)