| | varnish-cache/bin/varnishd/http2/cache_http2_hpack.c |
0 |
|
/*- |
1 |
|
* Copyright (c) 2016 Varnish Software AS |
2 |
|
* All rights reserved. |
3 |
|
* |
4 |
|
* Author: Martin Blix Grydeland <martin@varnish-software.com> |
5 |
|
* |
6 |
|
* SPDX-License-Identifier: BSD-2-Clause |
7 |
|
* |
8 |
|
* Redistribution and use in source and binary forms, with or without |
9 |
|
* modification, are permitted provided that the following conditions |
10 |
|
* are met: |
11 |
|
* 1. Redistributions of source code must retain the above copyright |
12 |
|
* notice, this list of conditions and the following disclaimer. |
13 |
|
* 2. Redistributions in binary form must reproduce the above copyright |
14 |
|
* notice, this list of conditions and the following disclaimer in the |
15 |
|
* documentation and/or other materials provided with the distribution. |
16 |
|
* |
17 |
|
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND |
18 |
|
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE |
19 |
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE |
20 |
|
* ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE |
21 |
|
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL |
22 |
|
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS |
23 |
|
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) |
24 |
|
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT |
25 |
|
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY |
26 |
|
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
27 |
|
* SUCH DAMAGE. |
28 |
|
* |
29 |
|
*/ |
30 |
|
|
31 |
|
#include "config.h" |
32 |
|
|
33 |
|
#include "cache/cache_varnishd.h" |
34 |
|
|
35 |
|
#include <ctype.h> |
36 |
|
#include <stdio.h> |
37 |
|
|
38 |
|
#include "http2/cache_http2.h" |
39 |
|
#include "vct.h" |
40 |
|
|
41 |
|
static void |
42 |
2360 |
h2h_assert_ready(const struct h2h_decode *d) |
43 |
|
{ |
44 |
|
|
45 |
2360 |
CHECK_OBJ_NOTNULL(d, H2H_DECODE_MAGIC); |
46 |
2360 |
AN(d->out); |
47 |
2360 |
assert(d->namelen >= 2); /* 2 chars from the ": " that we added */ |
48 |
2360 |
assert(d->namelen <= d->out_u); |
49 |
2360 |
assert(d->out[d->namelen - 2] == ':'); |
50 |
2360 |
assert(d->out[d->namelen - 1] == ' '); |
51 |
2360 |
} |
52 |
|
|
53 |
|
// rfc9113,l,2493,2528 |
54 |
|
static h2_error |
55 |
2360 |
h2h_checkhdr(struct vsl_log *vsl, txt nm, txt val) |
56 |
|
{ |
57 |
|
const char *p; |
58 |
|
int l; |
59 |
|
enum { |
60 |
|
FLD_NAME_FIRST, |
61 |
|
FLD_NAME, |
62 |
|
FLD_VALUE_FIRST, |
63 |
|
FLD_VALUE |
64 |
|
} state; |
65 |
|
|
66 |
2360 |
if (Tlen(nm) == 0) { |
67 |
0 |
VSLb(vsl, SLT_BogoHeader, "Empty name"); |
68 |
0 |
return (H2SE_PROTOCOL_ERROR); |
69 |
|
} |
70 |
|
|
71 |
|
// VSLb(vsl, SLT_Debug, "CHDR [%.*s] [%.*s]", |
72 |
|
// (int)Tlen(nm), nm.b, (int)Tlen(val), val.b); |
73 |
|
|
74 |
2360 |
l = vmin_t(int, Tlen(nm) + 2 + Tlen(val), 20); |
75 |
2360 |
state = FLD_NAME_FIRST; |
76 |
17848 |
Tforeach(p, nm) { |
77 |
15504 |
switch(state) { |
78 |
|
case FLD_NAME_FIRST: |
79 |
2360 |
state = FLD_NAME; |
80 |
2360 |
if (*p == ':') |
81 |
1896 |
break; |
82 |
|
/* FALLTHROUGH */ |
83 |
|
case FLD_NAME: |
84 |
13608 |
if (isupper(*p)) { |
85 |
8 |
VSLb(vsl, SLT_BogoHeader, |
86 |
|
"Illegal field header name (upper-case): %.*s", |
87 |
4 |
l, nm.b); |
88 |
4 |
return (H2SE_PROTOCOL_ERROR); |
89 |
|
} |
90 |
13604 |
if (!vct_istchar(*p) || *p == ':') { |
91 |
24 |
VSLb(vsl, SLT_BogoHeader, |
92 |
|
"Illegal field header name (non-token): %.*s", |
93 |
12 |
l, nm.b); |
94 |
12 |
return (H2SE_PROTOCOL_ERROR); |
95 |
|
} |
96 |
13592 |
break; |
97 |
|
default: |
98 |
0 |
WRONG("http2 field name validation state"); |
99 |
0 |
} |
100 |
15488 |
} |
101 |
|
|
102 |
2344 |
state = FLD_VALUE_FIRST; |
103 |
127552 |
Tforeach(p, val) { |
104 |
125248 |
switch(state) { |
105 |
|
case FLD_VALUE_FIRST: |
106 |
2316 |
if (vct_issp(*p)) { |
107 |
72 |
VSLb(vsl, SLT_BogoHeader, |
108 |
|
"Illegal field value 0x%02x start %.*s", |
109 |
36 |
*p, l, nm.b); |
110 |
36 |
return (H2SE_PROTOCOL_ERROR); |
111 |
|
} |
112 |
2280 |
state = FLD_VALUE; |
113 |
|
/* FALLTHROUGH */ |
114 |
|
case FLD_VALUE: |
115 |
125212 |
if (!vct_ishdrval(*p)) { |
116 |
8 |
VSLb(vsl, SLT_BogoHeader, |
117 |
|
"Illegal field value 0x%02x %.*s", |
118 |
4 |
*p, l, nm.b); |
119 |
4 |
return (H2SE_PROTOCOL_ERROR); |
120 |
|
} |
121 |
125208 |
break; |
122 |
|
default: |
123 |
0 |
WRONG("http2 field value validation state"); |
124 |
0 |
} |
125 |
125208 |
} |
126 |
2304 |
if (state == FLD_VALUE && vct_issp(val.e[-1])) { |
127 |
16 |
VSLb(vsl, SLT_BogoHeader, |
128 |
|
"Illegal field value 0x%02x (end) at %.*s", |
129 |
8 |
val.e[-1], l, nm.b); |
130 |
8 |
return (H2SE_PROTOCOL_ERROR); |
131 |
|
} |
132 |
2296 |
return (0); |
133 |
2360 |
} |
134 |
|
|
135 |
|
static h2_error |
136 |
2360 |
h2h_addhdr(struct http *hp, struct h2h_decode *d) |
137 |
|
{ |
138 |
|
/* XXX: This might belong in cache/cache_http.c */ |
139 |
|
txt hdr, nm, val; |
140 |
|
int disallow_empty; |
141 |
|
const char *p; |
142 |
|
unsigned n, has_dup; |
143 |
|
h2_error err; |
144 |
|
|
145 |
2360 |
CHECK_OBJ_NOTNULL(hp, HTTP_MAGIC); |
146 |
2360 |
h2h_assert_ready(d); |
147 |
|
|
148 |
|
/* Assume hdr is by default a regular header from what we decoded. */ |
149 |
2360 |
hdr.b = d->out; |
150 |
2360 |
hdr.e = hdr.b + d->out_u; |
151 |
2360 |
n = hp->nhd; |
152 |
|
|
153 |
|
/* nm and val are separated by ": " */ |
154 |
2360 |
nm.b = hdr.b; |
155 |
2360 |
nm.e = nm.b + d->namelen - 2; |
156 |
2360 |
val.b = nm.e + 2; |
157 |
2360 |
val.e = hdr.e; |
158 |
|
|
159 |
2360 |
err = h2h_checkhdr(hp->vsl, nm, val); |
160 |
2360 |
if (err != NULL) |
161 |
64 |
return (err); |
162 |
|
|
163 |
2296 |
disallow_empty = 0; |
164 |
2296 |
has_dup = 0; |
165 |
|
|
166 |
2296 |
if (Tlen(hdr) > cache_param->http_req_hdr_len) { |
167 |
4 |
VSLb(hp->vsl, SLT_BogoHeader, "Header too large: %.20s", hdr.b); |
168 |
4 |
return (H2SE_ENHANCE_YOUR_CALM); |
169 |
|
} |
170 |
|
|
171 |
|
/* Match H/2 pseudo headers */ |
172 |
|
/* XXX: Should probably have some include tbl for pseudo-headers */ |
173 |
2292 |
if (!Tstrcmp(nm, ":method")) { |
174 |
588 |
hdr.b = val.b; |
175 |
588 |
n = HTTP_HDR_METHOD; |
176 |
588 |
disallow_empty = 1; |
177 |
|
|
178 |
|
/* Check HTTP token */ |
179 |
2472 |
Tforeach(p, hdr) { |
180 |
1884 |
if (!vct_istchar(*p)) |
181 |
0 |
return (H2SE_PROTOCOL_ERROR); |
182 |
1884 |
} |
183 |
2292 |
} else if (!Tstrcmp(nm, ":path")) { |
184 |
612 |
hdr.b = val.b; |
185 |
612 |
n = HTTP_HDR_URL; |
186 |
612 |
disallow_empty = 1; |
187 |
|
|
188 |
|
// rfc9113,l,2693,2705 |
189 |
612 |
if (Tlen(val) > 0 && val.b[0] != '/' && Tstrcmp(val, "*")) { |
190 |
16 |
VSLb(hp->vsl, SLT_BogoHeader, |
191 |
|
"Illegal :path pseudo-header %.*s", |
192 |
8 |
(int)Tlen(val), val.b); |
193 |
8 |
return (H2SE_PROTOCOL_ERROR); |
194 |
|
} |
195 |
|
|
196 |
|
/* Path cannot contain LWS or CTL */ |
197 |
1936 |
Tforeach(p, hdr) { |
198 |
1332 |
if (vct_islws(*p) || vct_isctl(*p)) |
199 |
0 |
return (H2SE_PROTOCOL_ERROR); |
200 |
1332 |
} |
201 |
1696 |
} else if (!Tstrcmp(nm, ":scheme")) { |
202 |
|
/* XXX: What to do about this one? (typically |
203 |
|
"http" or "https"). For now set it as a normal |
204 |
|
header, stripping the first ':'. */ |
205 |
592 |
hdr.b++; |
206 |
592 |
has_dup = d->has_scheme; |
207 |
592 |
d->has_scheme = 1; |
208 |
592 |
disallow_empty = 1; |
209 |
|
|
210 |
|
/* Check HTTP token */ |
211 |
2932 |
Tforeach(p, val) { |
212 |
2340 |
if (!vct_istchar(*p)) |
213 |
0 |
return (H2SE_PROTOCOL_ERROR); |
214 |
2340 |
} |
215 |
1092 |
} else if (!Tstrcmp(nm, ":authority")) { |
216 |
|
/* NB: we inject "host" in place of "rity" for |
217 |
|
* the ":authority" pseudo-header. |
218 |
|
*/ |
219 |
68 |
memcpy(d->out + 6, "host", 4); |
220 |
68 |
hdr.b += 6; |
221 |
68 |
nm = Tstr(":authority"); /* preserve original */ |
222 |
68 |
has_dup = d->has_authority; |
223 |
68 |
d->has_authority = 1; |
224 |
500 |
} else if (nm.b[0] == ':') { |
225 |
16 |
VSLb(hp->vsl, SLT_BogoHeader, |
226 |
|
"Unknown pseudo-header: %.*s", |
227 |
8 |
vmin_t(int, Tlen(hdr), 20), hdr.b); |
228 |
8 |
return (H2SE_PROTOCOL_ERROR); // rfc7540,l,2990,2992 |
229 |
|
} |
230 |
|
|
231 |
2276 |
if (disallow_empty && Tlen(val) == 0) { |
232 |
48 |
VSLb(hp->vsl, SLT_BogoHeader, |
233 |
|
"Empty pseudo-header %.*s", |
234 |
24 |
(int)Tlen(nm), nm.b); |
235 |
24 |
return (H2SE_PROTOCOL_ERROR); |
236 |
|
} |
237 |
|
|
238 |
2252 |
if (n >= HTTP_HDR_FIRST) { |
239 |
|
/* Check for space in struct http */ |
240 |
1076 |
if (n >= hp->shd) { |
241 |
8 |
VSLb(hp->vsl, SLT_LostHeader, |
242 |
|
"Too many headers: %.*s", |
243 |
4 |
vmin_t(int, Tlen(hdr), 20), hdr.b); |
244 |
4 |
return (H2SE_ENHANCE_YOUR_CALM); |
245 |
|
} |
246 |
1072 |
hp->nhd++; |
247 |
1072 |
AZ(hp->hd[n].b); |
248 |
1072 |
} |
249 |
|
|
250 |
2248 |
if (has_dup || hp->hd[n].b != NULL) { |
251 |
20 |
assert(nm.b[0] == ':'); |
252 |
40 |
VSLb(hp->vsl, SLT_BogoHeader, |
253 |
|
"Duplicate pseudo-header %.*s", |
254 |
20 |
(int)Tlen(nm), nm.b); |
255 |
20 |
return (H2SE_PROTOCOL_ERROR); // rfc7540,l,3158,3162 |
256 |
|
} |
257 |
|
|
258 |
2228 |
hp->hd[n] = hdr; |
259 |
2228 |
return (0); |
260 |
2360 |
} |
261 |
|
|
262 |
|
static void |
263 |
728 |
h2h_decode_init(const struct h2_sess *h2, struct ws *ws) |
264 |
|
{ |
265 |
|
struct h2h_decode *d; |
266 |
|
|
267 |
728 |
CHECK_OBJ_NOTNULL(h2, H2_SESS_MAGIC); |
268 |
728 |
CHECK_OBJ_NOTNULL(ws, WS_MAGIC); |
269 |
|
|
270 |
728 |
AN(h2->decode); |
271 |
728 |
d = h2->decode; |
272 |
728 |
INIT_OBJ(d, H2H_DECODE_MAGIC); |
273 |
728 |
VHD_Init(d->vhd); |
274 |
728 |
d->out_l = WS_ReserveSize(ws, cache_param->http_req_size); |
275 |
|
/* |
276 |
|
* Can't do any work without any buffer |
277 |
|
* space. Require non-zero size. |
278 |
|
*/ |
279 |
728 |
XXXAN(d->out_l); |
280 |
728 |
d->out = WS_Reservation(ws); |
281 |
|
|
282 |
728 |
if (cache_param->h2_max_header_list_size == 0) |
283 |
728 |
d->limit = |
284 |
728 |
(long)(h2->local_settings.max_header_list_size * 1.5); |
285 |
|
else |
286 |
0 |
d->limit = cache_param->h2_max_header_list_size; |
287 |
|
|
288 |
728 |
if (d->limit < h2->local_settings.max_header_list_size) |
289 |
0 |
d->limit = INT64_MAX; |
290 |
|
|
291 |
728 |
d->ws = ws; |
292 |
728 |
} |
293 |
|
|
294 |
|
void |
295 |
728 |
h2h_decode_hdr_init(const struct h2_sess *h2) |
296 |
|
{ |
297 |
|
|
298 |
728 |
CHECK_OBJ_NOTNULL(h2, H2_SESS_MAGIC); |
299 |
728 |
CHECK_OBJ_NOTNULL(h2->new_req, REQ_MAGIC); |
300 |
728 |
CHECK_OBJ_NOTNULL(h2->new_req->http, HTTP_MAGIC); |
301 |
728 |
h2h_decode_init(h2, h2->new_req->ws); |
302 |
728 |
} |
303 |
|
|
304 |
|
/* Possible error returns: |
305 |
|
* |
306 |
|
* H2E_COMPRESSION_ERROR: Lost compression state due to incomplete header |
307 |
|
* block. This is a connection level error. |
308 |
|
* |
309 |
|
* H2E_ENHANCE_YOUR_CALM: Ran out of workspace or http header space. This |
310 |
|
* is a stream level error. |
311 |
|
*/ |
312 |
|
h2_error |
313 |
728 |
h2h_decode_hdr_fini(const struct h2_sess *h2) |
314 |
|
{ |
315 |
|
h2_error ret; |
316 |
|
struct h2h_decode *d; |
317 |
|
|
318 |
728 |
CHECK_OBJ_NOTNULL(h2, H2_SESS_MAGIC); |
319 |
728 |
d = h2->decode; |
320 |
728 |
CHECK_OBJ_NOTNULL(h2->new_req, REQ_MAGIC); |
321 |
728 |
CHECK_OBJ_NOTNULL(d, H2H_DECODE_MAGIC); |
322 |
728 |
WS_ReleaseP(d->ws, d->out); |
323 |
728 |
if (d->vhd_ret != VHD_OK) { |
324 |
|
/* HPACK header block didn't finish at an instruction |
325 |
|
boundary */ |
326 |
280 |
VSLb(h2->new_req->http->vsl, SLT_BogoHeader, |
327 |
140 |
"HPACK compression error/fini (%s)", VHD_Error(d->vhd_ret)); |
328 |
140 |
ret = H2CE_COMPRESSION_ERROR; |
329 |
728 |
} else if (d->error == NULL && !d->has_scheme) { |
330 |
16 |
H2S_Lock_VSLb(h2, SLT_Debug, "Missing :scheme"); |
331 |
16 |
ret = H2SE_MISSING_SCHEME; //rfc7540,l,3087,3090 |
332 |
16 |
} else |
333 |
572 |
ret = d->error; |
334 |
728 |
FINI_OBJ(d); |
335 |
728 |
if (ret == H2SE_REQ_SIZE) { |
336 |
4 |
VSLb(h2->new_req->http->vsl, SLT_LostHeader, |
337 |
|
"Header list too large"); |
338 |
4 |
} |
339 |
728 |
return (ret); |
340 |
|
} |
341 |
|
|
342 |
|
/* Possible error returns: |
343 |
|
* |
344 |
|
* H2E_COMPRESSION_ERROR: Lost compression state due to invalid header |
345 |
|
* block. This is a connection level error. |
346 |
|
* |
347 |
|
* H2E_PROTOCOL_ERROR: Malformed header or duplicate pseudo-header. |
348 |
|
* Violation of field name/value charsets |
349 |
|
*/ |
350 |
|
h2_error |
351 |
784 |
h2h_decode_bytes(struct h2_sess *h2, const uint8_t *in, size_t in_l) |
352 |
|
{ |
353 |
|
struct http *hp; |
354 |
|
struct h2h_decode *d; |
355 |
784 |
size_t in_u = 0; |
356 |
|
const char *r, *e; |
357 |
|
|
358 |
784 |
CHECK_OBJ_NOTNULL(h2, H2_SESS_MAGIC); |
359 |
784 |
CHECK_OBJ_NOTNULL(h2->new_req, REQ_MAGIC); |
360 |
784 |
hp = h2->new_req->http; |
361 |
784 |
CHECK_OBJ_NOTNULL(hp, HTTP_MAGIC); |
362 |
784 |
d = h2->decode; |
363 |
784 |
CHECK_OBJ_NOTNULL(d, H2H_DECODE_MAGIC); |
364 |
784 |
CHECK_OBJ_NOTNULL(d->ws, WS_MAGIC); |
365 |
784 |
r = WS_Reservation(d->ws); |
366 |
784 |
AN(r); |
367 |
784 |
e = r + WS_ReservationSize(d->ws); |
368 |
|
|
369 |
|
/* Only H2E_ENHANCE_YOUR_CALM indicates that we should continue |
370 |
|
processing. Other errors should have been returned and handled |
371 |
|
by the caller. */ |
372 |
784 |
if (d->error != NULL) |
373 |
16 |
assert(H2_ERROR_MATCH(d->error, H2SE_ENHANCE_YOUR_CALM)); |
374 |
|
|
375 |
5500 |
while (d->limit >= 0) { |
376 |
5496 |
AN(d->out); |
377 |
5496 |
assert(d->out_u <= d->out_l); |
378 |
10992 |
d->vhd_ret = VHD_Decode(d->vhd, h2->dectbl, in, in_l, &in_u, |
379 |
5496 |
d->out, d->out_l, &d->out_u); |
380 |
|
|
381 |
5496 |
if (d->vhd_ret < 0) { |
382 |
16 |
H2S_Lock_VSLb(h2, SLT_BogoHeader, |
383 |
|
"HPACK compression error (%s)", |
384 |
8 |
VHD_Error(d->vhd_ret)); |
385 |
8 |
d->error = H2CE_COMPRESSION_ERROR; |
386 |
8 |
break; |
387 |
5488 |
} else if (d->vhd_ret == VHD_OK || d->vhd_ret == VHD_MORE) { |
388 |
648 |
assert(in_u == in_l); |
389 |
648 |
break; |
390 |
|
} |
391 |
|
|
392 |
4840 |
if (H2_ERROR_MATCH(d->error, H2SE_ENHANCE_YOUR_CALM)) { |
393 |
104 |
d->limit -= d->out_u; |
394 |
104 |
d->out_u = 0; |
395 |
104 |
assert(d->out_u < d->out_l); |
396 |
104 |
continue; |
397 |
|
} |
398 |
|
|
399 |
4736 |
switch (d->vhd_ret) { |
400 |
|
case VHD_NAME_SEC: |
401 |
|
/* XXX: header flag for never-indexed header */ |
402 |
|
case VHD_NAME: |
403 |
2372 |
assert(d->namelen == 0); |
404 |
2372 |
if (d->out_l - d->out_u < 2) { |
405 |
4 |
d->error = H2SE_REQ_SIZE; |
406 |
4 |
break; |
407 |
|
} |
408 |
2368 |
d->out[d->out_u++] = ':'; |
409 |
2368 |
d->out[d->out_u++] = ' '; |
410 |
2368 |
d->namelen = d->out_u; |
411 |
2368 |
break; |
412 |
|
|
413 |
|
case VHD_VALUE_SEC: |
414 |
|
/* XXX: header flag for never-indexed header */ |
415 |
|
case VHD_VALUE: |
416 |
2360 |
assert(d->namelen > 0); |
417 |
2360 |
if (d->out_l - d->out_u < 1) { |
418 |
0 |
d->error = H2SE_REQ_SIZE; |
419 |
0 |
break; |
420 |
|
} |
421 |
2360 |
d->error = h2h_addhdr(hp, d); |
422 |
2360 |
if (d->error) |
423 |
132 |
break; |
424 |
2228 |
d->out[d->out_u++] = '\0'; /* Zero guard */ |
425 |
2228 |
d->out += d->out_u; |
426 |
2228 |
d->out_l -= d->out_u; |
427 |
2228 |
d->limit -= d->out_u; |
428 |
2228 |
d->out_u = 0; |
429 |
2228 |
d->namelen = 0; |
430 |
2228 |
break; |
431 |
|
|
432 |
|
case VHD_BUF: |
433 |
4 |
d->error = H2SE_REQ_SIZE; |
434 |
4 |
break; |
435 |
|
|
436 |
|
default: |
437 |
0 |
WRONG("Unhandled return value"); |
438 |
0 |
break; |
439 |
|
} |
440 |
|
|
441 |
4736 |
if (H2_ERROR_MATCH(d->error, H2SE_ENHANCE_YOUR_CALM)) { |
442 |
16 |
d->out = WS_Reservation(d->ws); |
443 |
16 |
d->out_l = e - d->out; |
444 |
16 |
d->limit -= d->out_u; |
445 |
16 |
d->out_u = 0; |
446 |
16 |
assert(d->out_l > 0); |
447 |
4736 |
} else if (d->error) |
448 |
124 |
break; |
449 |
|
} |
450 |
|
|
451 |
784 |
if (d->limit < 0) { |
452 |
|
/* Fatal error, the client exceeded both http_req_size |
453 |
|
* and h2_max_header_list_size. */ |
454 |
4 |
H2S_Lock_VSLb(h2, SLT_SessError, "Header list too large"); |
455 |
4 |
return (H2CE_ENHANCE_YOUR_CALM); |
456 |
|
} |
457 |
|
|
458 |
780 |
if (H2_ERROR_MATCH(d->error, H2SE_ENHANCE_YOUR_CALM)) { |
459 |
|
/* Stream error, delay reporting until h2h_decode_hdr_fini so |
460 |
|
* that we can process the complete header block. */ |
461 |
28 |
return (NULL); |
462 |
|
} |
463 |
|
|
464 |
752 |
return (d->error); |
465 |
784 |
} |