| | 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 |
20960 |
h2h_assert_ready(const struct h2h_decode *d) |
43 |
|
{ |
44 |
|
|
45 |
20960 |
CHECK_OBJ_NOTNULL(d, H2H_DECODE_MAGIC); |
46 |
20960 |
AN(d->out); |
47 |
20960 |
assert(d->namelen >= 2); /* 2 chars from the ": " that we added */ |
48 |
20960 |
assert(d->namelen <= d->out_u); |
49 |
20960 |
assert(d->out[d->namelen - 2] == ':'); |
50 |
20960 |
assert(d->out[d->namelen - 1] == ' '); |
51 |
20960 |
} |
52 |
|
|
53 |
|
// rfc9113,l,2493,2528 |
54 |
|
static h2_error |
55 |
20960 |
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 |
20960 |
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 |
20960 |
l = vmin_t(int, Tlen(nm) + 2 + Tlen(val), 20); |
75 |
20960 |
state = FLD_NAME_FIRST; |
76 |
159120 |
Tforeach(p, nm) { |
77 |
138320 |
switch(state) { |
78 |
|
case FLD_NAME_FIRST: |
79 |
20960 |
state = FLD_NAME; |
80 |
20960 |
if (*p == ':') |
81 |
16320 |
break; |
82 |
|
/* FALLTHROUGH */ |
83 |
|
case FLD_NAME: |
84 |
122000 |
if (isupper(*p)) { |
85 |
80 |
VSLb(vsl, SLT_BogoHeader, |
86 |
|
"Illegal field header name (upper-case): %.*s", |
87 |
40 |
l, nm.b); |
88 |
40 |
return (H2SE_PROTOCOL_ERROR); |
89 |
|
} |
90 |
121960 |
if (!vct_istchar(*p) || *p == ':') { |
91 |
240 |
VSLb(vsl, SLT_BogoHeader, |
92 |
|
"Illegal field header name (non-token): %.*s", |
93 |
120 |
l, nm.b); |
94 |
120 |
return (H2SE_PROTOCOL_ERROR); |
95 |
|
} |
96 |
121840 |
break; |
97 |
|
default: |
98 |
0 |
WRONG("http2 field name validation state"); |
99 |
0 |
} |
100 |
138160 |
} |
101 |
|
|
102 |
20800 |
state = FLD_VALUE_FIRST; |
103 |
1265560 |
Tforeach(p, val) { |
104 |
1245160 |
switch(state) { |
105 |
|
case FLD_VALUE_FIRST: |
106 |
20520 |
if (vct_issp(*p)) { |
107 |
720 |
VSLb(vsl, SLT_BogoHeader, |
108 |
360 |
"Illegal field value start %.*s", l, nm.b); |
109 |
360 |
return (H2SE_PROTOCOL_ERROR); |
110 |
|
} |
111 |
20160 |
state = FLD_VALUE; |
112 |
|
/* FALLTHROUGH */ |
113 |
|
case FLD_VALUE: |
114 |
1244800 |
if (!vct_ishdrval(*p)) { |
115 |
80 |
VSLb(vsl, SLT_BogoHeader, |
116 |
40 |
"Illegal field value %.*s", l, nm.b); |
117 |
40 |
return (H2SE_PROTOCOL_ERROR); |
118 |
|
} |
119 |
1244760 |
break; |
120 |
|
default: |
121 |
0 |
WRONG("http2 field value validation state"); |
122 |
0 |
} |
123 |
1244760 |
} |
124 |
20400 |
if (state == FLD_VALUE && vct_issp(val.e[-1])) { |
125 |
160 |
VSLb(vsl, SLT_BogoHeader, |
126 |
80 |
"Illegal field value (end) %.*s", l, nm.b); |
127 |
80 |
return (H2SE_PROTOCOL_ERROR); |
128 |
|
} |
129 |
20320 |
return (0); |
130 |
20960 |
} |
131 |
|
|
132 |
|
static h2_error |
133 |
20960 |
h2h_addhdr(struct http *hp, struct h2h_decode *d) |
134 |
|
{ |
135 |
|
/* XXX: This might belong in cache/cache_http.c */ |
136 |
|
txt hdr, nm, val; |
137 |
|
int disallow_empty; |
138 |
|
const char *p; |
139 |
|
unsigned n, has_dup; |
140 |
|
h2_error err; |
141 |
|
|
142 |
20960 |
CHECK_OBJ_NOTNULL(hp, HTTP_MAGIC); |
143 |
20960 |
h2h_assert_ready(d); |
144 |
|
|
145 |
|
/* Assume hdr is by default a regular header from what we decoded. */ |
146 |
20960 |
hdr.b = d->out; |
147 |
20960 |
hdr.e = hdr.b + d->out_u; |
148 |
20960 |
n = hp->nhd; |
149 |
|
|
150 |
|
/* nm and val are separated by ": " */ |
151 |
20960 |
nm.b = hdr.b; |
152 |
20960 |
nm.e = nm.b + d->namelen - 2; |
153 |
20960 |
val.b = nm.e + 2; |
154 |
20960 |
val.e = hdr.e; |
155 |
|
|
156 |
20960 |
err = h2h_checkhdr(hp->vsl, nm, val); |
157 |
20960 |
if (err != NULL) |
158 |
640 |
return (err); |
159 |
|
|
160 |
20320 |
disallow_empty = 0; |
161 |
20320 |
has_dup = 0; |
162 |
|
|
163 |
20320 |
if (Tlen(hdr) > cache_param->http_req_hdr_len) { |
164 |
40 |
VSLb(hp->vsl, SLT_BogoHeader, "Header too large: %.20s", hdr.b); |
165 |
40 |
return (H2SE_ENHANCE_YOUR_CALM); |
166 |
|
} |
167 |
|
|
168 |
|
/* Match H/2 pseudo headers */ |
169 |
|
/* XXX: Should probably have some include tbl for pseudo-headers */ |
170 |
20280 |
if (!Tstrcmp(nm, ":method")) { |
171 |
5000 |
hdr.b = val.b; |
172 |
5000 |
n = HTTP_HDR_METHOD; |
173 |
5000 |
disallow_empty = 1; |
174 |
|
|
175 |
|
/* Check HTTP token */ |
176 |
21200 |
Tforeach(p, hdr) { |
177 |
16200 |
if (!vct_istchar(*p)) |
178 |
0 |
return (H2SE_PROTOCOL_ERROR); |
179 |
16200 |
} |
180 |
20280 |
} else if (!Tstrcmp(nm, ":path")) { |
181 |
5240 |
hdr.b = val.b; |
182 |
5240 |
n = HTTP_HDR_URL; |
183 |
5240 |
disallow_empty = 1; |
184 |
|
|
185 |
|
// rfc9113,l,2693,2705 |
186 |
5240 |
if (Tlen(val) > 0 && val.b[0] != '/' && Tstrcmp(val, "*")) { |
187 |
160 |
VSLb(hp->vsl, SLT_BogoHeader, |
188 |
|
"Illegal :path pseudo-header %.*s", |
189 |
80 |
(int)Tlen(val), val.b); |
190 |
80 |
return (H2SE_PROTOCOL_ERROR); |
191 |
|
} |
192 |
|
|
193 |
|
/* Path cannot contain LWS or CTL */ |
194 |
17320 |
Tforeach(p, hdr) { |
195 |
12160 |
if (vct_islws(*p) || vct_isctl(*p)) |
196 |
0 |
return (H2SE_PROTOCOL_ERROR); |
197 |
12160 |
} |
198 |
15200 |
} else if (!Tstrcmp(nm, ":scheme")) { |
199 |
|
/* XXX: What to do about this one? (typically |
200 |
|
"http" or "https"). For now set it as a normal |
201 |
|
header, stripping the first ':'. */ |
202 |
5040 |
hdr.b++; |
203 |
5040 |
has_dup = d->has_scheme; |
204 |
5040 |
d->has_scheme = 1; |
205 |
5040 |
disallow_empty = 1; |
206 |
|
|
207 |
|
/* Check HTTP token */ |
208 |
24920 |
Tforeach(p, val) { |
209 |
19880 |
if (!vct_istchar(*p)) |
210 |
0 |
return (H2SE_PROTOCOL_ERROR); |
211 |
19880 |
} |
212 |
10040 |
} else if (!Tstrcmp(nm, ":authority")) { |
213 |
|
/* NB: we inject "host" in place of "rity" for |
214 |
|
* the ":authority" pseudo-header. |
215 |
|
*/ |
216 |
680 |
memcpy(d->out + 6, "host", 4); |
217 |
680 |
hdr.b += 6; |
218 |
680 |
nm = Tstr(":authority"); /* preserve original */ |
219 |
680 |
has_dup = d->has_authority; |
220 |
680 |
d->has_authority = 1; |
221 |
5000 |
} else if (nm.b[0] == ':') { |
222 |
160 |
VSLb(hp->vsl, SLT_BogoHeader, |
223 |
|
"Unknown pseudo-header: %.*s", |
224 |
80 |
vmin_t(int, Tlen(hdr), 20), hdr.b); |
225 |
80 |
return (H2SE_PROTOCOL_ERROR); // rfc7540,l,2990,2992 |
226 |
|
} |
227 |
|
|
228 |
20120 |
if (disallow_empty && Tlen(val) == 0) { |
229 |
480 |
VSLb(hp->vsl, SLT_BogoHeader, |
230 |
|
"Empty pseudo-header %.*s", |
231 |
240 |
(int)Tlen(nm), nm.b); |
232 |
240 |
return (H2SE_PROTOCOL_ERROR); |
233 |
|
} |
234 |
|
|
235 |
19880 |
if (n >= HTTP_HDR_FIRST) { |
236 |
|
/* Check for space in struct http */ |
237 |
9880 |
if (n >= hp->shd) { |
238 |
80 |
VSLb(hp->vsl, SLT_LostHeader, |
239 |
|
"Too many headers: %.*s", |
240 |
40 |
vmin_t(int, Tlen(hdr), 20), hdr.b); |
241 |
40 |
return (H2SE_ENHANCE_YOUR_CALM); |
242 |
|
} |
243 |
9840 |
hp->nhd++; |
244 |
9840 |
AZ(hp->hd[n].b); |
245 |
9840 |
} |
246 |
|
|
247 |
19840 |
if (has_dup || hp->hd[n].b != NULL) { |
248 |
200 |
assert(nm.b[0] == ':'); |
249 |
400 |
VSLb(hp->vsl, SLT_BogoHeader, |
250 |
|
"Duplicate pseudo-header %.*s", |
251 |
200 |
(int)Tlen(nm), nm.b); |
252 |
200 |
return (H2SE_PROTOCOL_ERROR); // rfc7540,l,3158,3162 |
253 |
|
} |
254 |
|
|
255 |
19640 |
hp->hd[n] = hdr; |
256 |
19640 |
return (0); |
257 |
20960 |
} |
258 |
|
|
259 |
|
static void |
260 |
6400 |
h2h_decode_init(const struct h2_sess *h2, struct ws *ws) |
261 |
|
{ |
262 |
|
struct h2h_decode *d; |
263 |
|
|
264 |
6400 |
CHECK_OBJ_NOTNULL(h2, H2_SESS_MAGIC); |
265 |
6400 |
CHECK_OBJ_NOTNULL(ws, WS_MAGIC); |
266 |
|
|
267 |
6400 |
AN(h2->decode); |
268 |
6400 |
d = h2->decode; |
269 |
6400 |
INIT_OBJ(d, H2H_DECODE_MAGIC); |
270 |
6400 |
VHD_Init(d->vhd); |
271 |
6400 |
d->out_l = WS_ReserveSize(ws, cache_param->http_req_size); |
272 |
|
/* |
273 |
|
* Can't do any work without any buffer |
274 |
|
* space. Require non-zero size. |
275 |
|
*/ |
276 |
6400 |
XXXAN(d->out_l); |
277 |
6400 |
d->out = WS_Reservation(ws); |
278 |
|
|
279 |
6400 |
if (cache_param->h2_max_header_list_size == 0) |
280 |
6400 |
d->limit = |
281 |
6400 |
(long)(h2->local_settings.max_header_list_size * 1.5); |
282 |
|
else |
283 |
0 |
d->limit = cache_param->h2_max_header_list_size; |
284 |
|
|
285 |
6400 |
if (d->limit < h2->local_settings.max_header_list_size) |
286 |
0 |
d->limit = INT64_MAX; |
287 |
|
|
288 |
6400 |
d->ws = ws; |
289 |
6400 |
} |
290 |
|
|
291 |
|
void |
292 |
6400 |
h2h_decode_hdr_init(const struct h2_sess *h2) |
293 |
|
{ |
294 |
|
|
295 |
6400 |
CHECK_OBJ_NOTNULL(h2, H2_SESS_MAGIC); |
296 |
6400 |
CHECK_OBJ_NOTNULL(h2->new_req, REQ_MAGIC); |
297 |
6400 |
CHECK_OBJ_NOTNULL(h2->new_req->http, HTTP_MAGIC); |
298 |
6400 |
h2h_decode_init(h2, h2->new_req->ws); |
299 |
6400 |
} |
300 |
|
|
301 |
|
/* Possible error returns: |
302 |
|
* |
303 |
|
* H2E_COMPRESSION_ERROR: Lost compression state due to incomplete header |
304 |
|
* block. This is a connection level error. |
305 |
|
* |
306 |
|
* H2E_ENHANCE_YOUR_CALM: Ran out of workspace or http header space. This |
307 |
|
* is a stream level error. |
308 |
|
*/ |
309 |
|
h2_error |
310 |
6400 |
h2h_decode_hdr_fini(const struct h2_sess *h2) |
311 |
|
{ |
312 |
|
h2_error ret; |
313 |
|
struct h2h_decode *d; |
314 |
|
|
315 |
6400 |
CHECK_OBJ_NOTNULL(h2, H2_SESS_MAGIC); |
316 |
6400 |
d = h2->decode; |
317 |
6400 |
CHECK_OBJ_NOTNULL(h2->new_req, REQ_MAGIC); |
318 |
6400 |
CHECK_OBJ_NOTNULL(d, H2H_DECODE_MAGIC); |
319 |
6400 |
WS_ReleaseP(d->ws, d->out); |
320 |
6400 |
if (d->vhd_ret != VHD_OK) { |
321 |
|
/* HPACK header block didn't finish at an instruction |
322 |
|
boundary */ |
323 |
2800 |
VSLb(h2->new_req->http->vsl, SLT_BogoHeader, |
324 |
1400 |
"HPACK compression error/fini (%s)", VHD_Error(d->vhd_ret)); |
325 |
1400 |
ret = H2CE_COMPRESSION_ERROR; |
326 |
6400 |
} else if (d->error == NULL && !d->has_scheme) { |
327 |
160 |
H2S_Lock_VSLb(h2, SLT_Debug, "Missing :scheme"); |
328 |
160 |
ret = H2SE_MISSING_SCHEME; //rfc7540,l,3087,3090 |
329 |
160 |
} else |
330 |
4840 |
ret = d->error; |
331 |
6400 |
FINI_OBJ(d); |
332 |
6400 |
if (ret == H2SE_REQ_SIZE) { |
333 |
40 |
VSLb(h2->new_req->http->vsl, SLT_LostHeader, |
334 |
|
"Header list too large"); |
335 |
40 |
} |
336 |
6400 |
return (ret); |
337 |
|
} |
338 |
|
|
339 |
|
/* Possible error returns: |
340 |
|
* |
341 |
|
* H2E_COMPRESSION_ERROR: Lost compression state due to invalid header |
342 |
|
* block. This is a connection level error. |
343 |
|
* |
344 |
|
* H2E_PROTOCOL_ERROR: Malformed header or duplicate pseudo-header. |
345 |
|
* Violation of field name/value charsets |
346 |
|
*/ |
347 |
|
h2_error |
348 |
6960 |
h2h_decode_bytes(struct h2_sess *h2, const uint8_t *in, size_t in_l) |
349 |
|
{ |
350 |
|
struct http *hp; |
351 |
|
struct h2h_decode *d; |
352 |
6960 |
size_t in_u = 0; |
353 |
|
const char *r, *e; |
354 |
|
|
355 |
6960 |
CHECK_OBJ_NOTNULL(h2, H2_SESS_MAGIC); |
356 |
6960 |
CHECK_OBJ_NOTNULL(h2->new_req, REQ_MAGIC); |
357 |
6960 |
hp = h2->new_req->http; |
358 |
6960 |
CHECK_OBJ_NOTNULL(hp, HTTP_MAGIC); |
359 |
6960 |
d = h2->decode; |
360 |
6960 |
CHECK_OBJ_NOTNULL(d, H2H_DECODE_MAGIC); |
361 |
6960 |
CHECK_OBJ_NOTNULL(d->ws, WS_MAGIC); |
362 |
6960 |
r = WS_Reservation(d->ws); |
363 |
6960 |
AN(r); |
364 |
6960 |
e = r + WS_ReservationSize(d->ws); |
365 |
|
|
366 |
|
/* Only H2E_ENHANCE_YOUR_CALM indicates that we should continue |
367 |
|
processing. Other errors should have been returned and handled |
368 |
|
by the caller. */ |
369 |
6960 |
if (d->error != NULL) |
370 |
160 |
assert(H2_ERROR_MATCH(d->error, H2SE_ENHANCE_YOUR_CALM)); |
371 |
|
|
372 |
48840 |
while (d->limit >= 0) { |
373 |
48800 |
AN(d->out); |
374 |
48800 |
assert(d->out_u <= d->out_l); |
375 |
97600 |
d->vhd_ret = VHD_Decode(d->vhd, h2->dectbl, in, in_l, &in_u, |
376 |
48800 |
d->out, d->out_l, &d->out_u); |
377 |
|
|
378 |
48800 |
if (d->vhd_ret < 0) { |
379 |
160 |
H2S_Lock_VSLb(h2, SLT_BogoHeader, |
380 |
|
"HPACK compression error (%s)", |
381 |
80 |
VHD_Error(d->vhd_ret)); |
382 |
80 |
d->error = H2CE_COMPRESSION_ERROR; |
383 |
80 |
break; |
384 |
48720 |
} else if (d->vhd_ret == VHD_OK || d->vhd_ret == VHD_MORE) { |
385 |
5600 |
assert(in_u == in_l); |
386 |
5600 |
break; |
387 |
|
} |
388 |
|
|
389 |
43120 |
if (H2_ERROR_MATCH(d->error, H2SE_ENHANCE_YOUR_CALM)) { |
390 |
1040 |
d->limit -= d->out_u; |
391 |
1040 |
d->out_u = 0; |
392 |
1040 |
assert(d->out_u < d->out_l); |
393 |
1040 |
continue; |
394 |
|
} |
395 |
|
|
396 |
42080 |
switch (d->vhd_ret) { |
397 |
|
case VHD_NAME_SEC: |
398 |
|
/* XXX: header flag for never-indexed header */ |
399 |
|
case VHD_NAME: |
400 |
21080 |
assert(d->namelen == 0); |
401 |
21080 |
if (d->out_l - d->out_u < 2) { |
402 |
40 |
d->error = H2SE_REQ_SIZE; |
403 |
40 |
break; |
404 |
|
} |
405 |
21040 |
d->out[d->out_u++] = ':'; |
406 |
21040 |
d->out[d->out_u++] = ' '; |
407 |
21040 |
d->namelen = d->out_u; |
408 |
21040 |
break; |
409 |
|
|
410 |
|
case VHD_VALUE_SEC: |
411 |
|
/* XXX: header flag for never-indexed header */ |
412 |
|
case VHD_VALUE: |
413 |
20960 |
assert(d->namelen > 0); |
414 |
20960 |
if (d->out_l - d->out_u < 1) { |
415 |
0 |
d->error = H2SE_REQ_SIZE; |
416 |
0 |
break; |
417 |
|
} |
418 |
20960 |
d->error = h2h_addhdr(hp, d); |
419 |
20960 |
if (d->error) |
420 |
1320 |
break; |
421 |
19640 |
d->out[d->out_u++] = '\0'; /* Zero guard */ |
422 |
19640 |
d->out += d->out_u; |
423 |
19640 |
d->out_l -= d->out_u; |
424 |
19640 |
d->limit -= d->out_u; |
425 |
19640 |
d->out_u = 0; |
426 |
19640 |
d->namelen = 0; |
427 |
19640 |
break; |
428 |
|
|
429 |
|
case VHD_BUF: |
430 |
40 |
d->error = H2SE_REQ_SIZE; |
431 |
40 |
break; |
432 |
|
|
433 |
|
default: |
434 |
0 |
WRONG("Unhandled return value"); |
435 |
0 |
break; |
436 |
|
} |
437 |
|
|
438 |
42080 |
if (H2_ERROR_MATCH(d->error, H2SE_ENHANCE_YOUR_CALM)) { |
439 |
160 |
d->out = WS_Reservation(d->ws); |
440 |
160 |
d->out_l = e - d->out; |
441 |
160 |
d->limit -= d->out_u; |
442 |
160 |
d->out_u = 0; |
443 |
160 |
assert(d->out_l > 0); |
444 |
42080 |
} else if (d->error) |
445 |
1240 |
break; |
446 |
|
} |
447 |
|
|
448 |
6960 |
if (d->limit < 0) { |
449 |
|
/* Fatal error, the client exceeded both http_req_size |
450 |
|
* and h2_max_header_list_size. */ |
451 |
40 |
H2S_Lock_VSLb(h2, SLT_SessError, "Header list too large"); |
452 |
40 |
return (H2CE_ENHANCE_YOUR_CALM); |
453 |
|
} |
454 |
|
|
455 |
6920 |
if (H2_ERROR_MATCH(d->error, H2SE_ENHANCE_YOUR_CALM)) { |
456 |
|
/* Stream error, delay reporting until h2h_decode_hdr_fini so |
457 |
|
* that we can process the complete header block. */ |
458 |
280 |
return (NULL); |
459 |
|
} |
460 |
|
|
461 |
6640 |
return (d->error); |
462 |
6960 |
} |