blob: 86b4a76173166070878a71e7ae317ba713bcc3be [file] [log] [blame]
Christopher Faulet4f0f88a2019-08-10 11:17:44 +02001/*
2 * Functions to manipulate H1 messages using the internal representation.
3 *
4 * Copyright (C) 2019 HAProxy Technologies, Christopher Faulet <cfaulet@haproxy.com>
5 *
6 * This program is free software; you can redistribute it and/or
7 * modify it under the terms of the GNU General Public License
8 * as published by the Free Software Foundation; either version
9 * 2 of the License, or (at your option) any later version.
10 *
11 */
12
13#include <common/config.h>
14#include <common/debug.h>
15#include <common/cfgparse.h>
16#include <common/h1.h>
17#include <common/http.h>
18#include <common/htx.h>
19
20#include <proto/h1_htx.h>
21
22/* Estimate the size of the HTX headers after the parsing, including the EOH. */
23static size_t h1_eval_htx_hdrs_size(const struct http_hdr *hdrs)
24{
25 size_t sz = 0;
26 int i;
27
28 for (i = 0; hdrs[i].n.len; i++)
29 sz += sizeof(struct htx_blk) + hdrs[i].n.len + hdrs[i].v.len;
30 sz += sizeof(struct htx_blk) + 1;
31 return sz;
32}
33
34/* Estimate the size of the HTX request after the parsing. */
35static size_t h1_eval_htx_size(const struct ist p1, const struct ist p2, const struct ist p3,
36 const struct http_hdr *hdrs)
37{
38 size_t sz;
39
40 /* size of the HTX start-line */
41 sz = sizeof(struct htx_blk) + sizeof(struct htx_sl) + p1.len + p2.len + p3.len;
42 sz += h1_eval_htx_hdrs_size(hdrs);
43 return sz;
44}
45
46/* Switch the message to tunnel mode. On the request, it must only be called for
47 * a CONNECT method. On the response, this function must only be called on
48 * successfull replies to CONNECT requests or on protocol switching.
49 */
50static void h1_set_tunnel_mode(struct h1m *h1m)
51{
52 h1m->flags &= ~(H1_MF_XFER_LEN|H1_MF_CLEN|H1_MF_CHNK);
53 h1m->state = H1_MSG_TUNNEL;
54}
55
56/* Check the validity of the request version. If the version is valid, it
57 * returns 1. Otherwise, it returns 0.
58 */
59static int h1_process_req_vsn(struct h1m *h1m, union h1_sl *sl)
60{
61 /* RFC7230#2.6 has enforced the format of the HTTP version string to be
62 * exactly one digit "." one digit. This check may be disabled using
63 * option accept-invalid-http-request.
64 */
65 if (h1m->err_pos == -2) { /* PR_O2_REQBUG_OK not set */
66 if (sl->rq.v.len != 8)
67 return 0;
68
69 if (*(sl->rq.v.ptr + 4) != '/' ||
70 !isdigit((unsigned char)*(sl->rq.v.ptr + 5)) ||
71 *(sl->rq.v.ptr + 6) != '.' ||
72 !isdigit((unsigned char)*(sl->rq.v.ptr + 7)))
73 return 0;
74 }
75 else if (!sl->rq.v.len) {
76 /* try to convert HTTP/0.9 requests to HTTP/1.0 */
77
78 /* RFC 1945 allows only GET for HTTP/0.9 requests */
79 if (sl->rq.meth != HTTP_METH_GET)
80 return 0;
81
82 /* HTTP/0.9 requests *must* have a request URI, per RFC 1945 */
83 if (!sl->rq.u.len)
84 return 0;
85
86 /* Add HTTP version */
87 sl->rq.v = ist("HTTP/1.0");
88 return 1;
89 }
90
91 if ((sl->rq.v.len == 8) &&
92 ((*(sl->rq.v.ptr + 5) > '1') ||
93 ((*(sl->rq.v.ptr + 5) == '1') && (*(sl->rq.v.ptr + 7) >= '1'))))
94 h1m->flags |= H1_MF_VER_11;
95 return 1;
96}
97
98/* Check the validity of the response version. If the version is valid, it
99 * returns 1. Otherwise, it returns 0.
100 */
101static int h1_process_res_vsn(struct h1m *h1m, union h1_sl *sl)
102{
103 /* RFC7230#2.6 has enforced the format of the HTTP version string to be
104 * exactly one digit "." one digit. This check may be disabled using
105 * option accept-invalid-http-request.
106 */
107 if (h1m->err_pos == -2) { /* PR_O2_REQBUG_OK not set */
108 if (sl->st.v.len != 8)
109 return 0;
110
111 if (*(sl->st.v.ptr + 4) != '/' ||
112 !isdigit((unsigned char)*(sl->st.v.ptr + 5)) ||
113 *(sl->st.v.ptr + 6) != '.' ||
114 !isdigit((unsigned char)*(sl->st.v.ptr + 7)))
115 return 0;
116 }
117
118 if ((sl->st.v.len == 8) &&
119 ((*(sl->st.v.ptr + 5) > '1') ||
120 ((*(sl->st.v.ptr + 5) == '1') && (*(sl->st.v.ptr + 7) >= '1'))))
121 h1m->flags |= H1_MF_VER_11;
122
123 return 1;
124}
125
126/* Convert H1M flags to HTX start-line flags. */
127static unsigned int h1m_htx_sl_flags(struct h1m *h1m)
128{
129 unsigned int flags = HTX_SL_F_NONE;
130
131 if (h1m->flags & H1_MF_RESP)
132 flags |= HTX_SL_F_IS_RESP;
133 if (h1m->flags & H1_MF_VER_11)
134 flags |= HTX_SL_F_VER_11;
135 if (h1m->flags & H1_MF_XFER_ENC)
136 flags |= HTX_SL_F_XFER_ENC;
137 if (h1m->flags & H1_MF_XFER_LEN) {
138 flags |= HTX_SL_F_XFER_LEN;
139 if (h1m->flags & H1_MF_CHNK)
140 flags |= HTX_SL_F_CHNK;
141 else if (h1m->flags & H1_MF_CLEN) {
142 flags |= HTX_SL_F_CLEN;
143 if (h1m->body_len == 0)
144 flags |= HTX_SL_F_BODYLESS;
145 }
146 else
147 flags |= HTX_SL_F_BODYLESS;
148 }
149 return flags;
150}
151
152/* Postprocess the parsed headers for a request and convert them into an htx
153 * message. It returns the number of bytes parsed if > 0, or 0 if it couldn't
154 * proceed. Parsing errors are reported by setting the htx flag
155 * HTX_FL_PARSING_ERROR and filling h1m->err_pos and h1m->err_state fields.
156 */
157static int h1_postparse_req_hdrs(struct h1m *h1m, union h1_sl *h1sl, struct htx *htx,
158 struct http_hdr *hdrs, size_t max)
159{
160 struct htx_sl *sl;
161 struct ist meth, uri, vsn;
162 unsigned int flags;
163 size_t used;
164
165 /* <h1sl> is always defined for a request */
166 meth = h1sl->rq.m;
167 uri = h1sl->rq.u;
168 vsn = h1sl->rq.v;
169
170 /* Be sure the message, once converted into HTX, will not exceed the max
171 * size allowed.
172 */
173 if (h1_eval_htx_size(meth, uri, vsn, hdrs) > max) {
174 if (htx_is_empty(htx))
175 goto error;
176 h1m_init_res(h1m);
177 h1m->flags |= (H1_MF_NO_PHDR|H1_MF_CLEAN_CONN_HDR);
178 return 0;
179 }
180
181 /* By default, request have always a known length */
182 h1m->flags |= H1_MF_XFER_LEN;
183
184 if (h1sl->rq.meth == HTTP_METH_CONNECT) {
185 /* Switch CONNECT requests to tunnel mode */
186 h1_set_tunnel_mode(h1m);
187 }
188
189 used = htx_used_space(htx);
190 flags = h1m_htx_sl_flags(h1m);
191 sl = htx_add_stline(htx, HTX_BLK_REQ_SL, flags, meth, uri, vsn);
192 if (!sl || !htx_add_all_headers(htx, hdrs))
193 goto error;
194 sl->info.req.meth = h1sl->rq.meth;
195
196 /* Check if the uri contains an explicit scheme and if it is
197 * "http" or "https". */
198 if (uri.len && uri.ptr[0] != '/') {
199 sl->flags |= HTX_SL_F_HAS_SCHM;
200 if (uri.len > 4 && (uri.ptr[0] | 0x20) == 'h')
201 sl->flags |= ((uri.ptr[4] == ':') ? HTX_SL_F_SCHM_HTTP : HTX_SL_F_SCHM_HTTPS);
202 }
203 /* Set bytes used in the HTX mesage for the headers now */
204 sl->hdrs_bytes = htx_used_space(htx) - used;
205
206 /* If body length cannot be determined, set htx->extra to
207 * ULLONG_MAX. This value is impossible in other cases.
208 */
209 htx->extra = ((h1m->flags & H1_MF_XFER_LEN) ? h1m->curr_len : ULLONG_MAX);
210
211 end:
212 return 1;
213 error:
214 h1m->err_pos = h1m->next;
215 h1m->err_state = h1m->state;
216 htx->flags |= HTX_FL_PARSING_ERROR;
217 return 0;
218}
219
220/* Postprocess the parsed headers for a response and convert them into an htx
221 * message. It returns the number of bytes parsed if > 0, or 0 if it couldn't
222 * proceed. Parsing errors are reported by setting the htx flag
223 * HTX_FL_PARSING_ERROR and filling h1m->err_pos and h1m->err_state fields.
224 */
225static int h1_postparse_res_hdrs(struct h1m *h1m, union h1_sl *h1sl, struct htx *htx,
226 struct http_hdr *hdrs, size_t max)
227{
228 struct htx_sl *sl;
229 struct ist vsn, status, reason;
230 unsigned int flags;
231 size_t used;
232 uint16_t code = 0;
233
234 if (h1sl) {
235 /* For HTTP responses, the start-line was parsed */
236 code = h1sl->st.status;
237 vsn = h1sl->st.v;
238 status = h1sl->st.c;
239 reason = h1sl->st.r;
240 }
241 else {
242 /* For FCGI responses, there is no start(-line but the "Status"
243 * header must be parsed, if found.
244 */
245 int hdr;
246
247 vsn = ((h1m->flags & H1_MF_VER_11) ? ist("HTTP/1.1") : ist("HTTP/1.0"));
248 for (hdr = 0; hdrs[hdr].n.len; hdr++) {
249 if (isteqi(hdrs[hdr].n, ist("status"))) {
250 code = http_parse_status_val(hdrs[hdr].v, &status, &reason);
251 }
252 else if (isteqi(hdrs[hdr].n, ist("location"))) {
253 code = 302;
254 status = ist("302");
255 reason = ist("Moved Temporarily");
256 }
257 }
258 if (!code) {
259 code = 200;
260 status = ist("200");
261 reason = ist("OK");
262 }
263 /* FIXME: Check the codes 1xx ? */
264 }
265
266 /* Be sure the message, once converted into HTX, will not exceed the max
267 * size allowed.
268 */
269 if (h1_eval_htx_size(vsn, status, reason, hdrs) > max) {
270 if (htx_is_empty(htx))
271 goto error;
272 h1m_init_res(h1m);
273 h1m->flags |= (H1_MF_NO_PHDR|H1_MF_CLEAN_CONN_HDR);
274 return 0;
275 }
276
277 if (((h1m->flags & H1_MF_METH_CONNECT) && code == 200) || code == 101) {
278 /* Switch successfull replies to CONNECT requests and
279 * protocol switching to tunnel mode. */
280 h1_set_tunnel_mode(h1m);
281 }
282 else if ((h1m->flags & H1_MF_METH_HEAD) || (code >= 100 && code < 200) ||
283 (code == 204) || (code == 304)) {
284 /* Responses known to have no body. */
285 h1m->flags &= ~(H1_MF_CLEN|H1_MF_CHNK);
286 h1m->flags |= H1_MF_XFER_LEN;
287 h1m->curr_len = h1m->body_len = 0;
288 }
289 else if (h1m->flags & (H1_MF_CLEN|H1_MF_CHNK)) {
290 /* Responses with a known body length. */
291 h1m->flags |= H1_MF_XFER_LEN;
292 }
293 else {
294 /* Responses with an unknown body length */
295 h1m->state = H1_MSG_TUNNEL;
296 }
297
298 used = htx_used_space(htx);
299 flags = h1m_htx_sl_flags(h1m);
300 sl = htx_add_stline(htx, HTX_BLK_RES_SL, flags, vsn, status, reason);
301 if (!sl || !htx_add_all_headers(htx, hdrs))
302 goto error;
303 sl->info.res.status = code;
304
305 /* Set bytes used in the HTX mesage for the headers now */
306 sl->hdrs_bytes = htx_used_space(htx) - used;
307
308 /* If body length cannot be determined, set htx->extra to
309 * ULLONG_MAX. This value is impossible in other cases.
310 */
311 htx->extra = ((h1m->flags & H1_MF_XFER_LEN) ? h1m->curr_len : ULLONG_MAX);
312
313 end:
314 return 1;
315 error:
316 h1m->err_pos = h1m->next;
317 h1m->err_state = h1m->state;
318 htx->flags |= HTX_FL_PARSING_ERROR;
319 return 0;
320}
321
322/* Parse HTTP/1 headers. It returns the number of bytes parsed if > 0, or 0 if
323 * it couldn't proceed. Parsing errors are reported by setting the htx flag
324 * HTX_FL_PARSING_ERROR and filling h1m->err_pos and h1m->err_state fields. This
325 * functions is responsible to update the parser state <h1m> and the start-line
326 * <h1sl> if not NULL.
327 * For the requests, <h1sl> must always be provided. For responses, <h1sl> may
328 * be NULL and <h1m> flags HTTP_METH_CONNECT of HTTP_METH_HEAD may be set.
329 */
330int h1_parse_msg_hdrs(struct h1m *h1m, union h1_sl *h1sl, struct htx *dsthtx,
331 struct buffer *srcbuf, size_t ofs, size_t max)
332{
333 struct http_hdr hdrs[global.tune.max_http_hdr];
334 int ret = 0;
335
336 if (!max || !b_data(srcbuf))
337 goto end;
338
339 /* Realing input buffer if necessary */
340 if (b_head(srcbuf) + b_data(srcbuf) > b_wrap(srcbuf))
341 b_slow_realign(srcbuf, trash.area, 0);
342
343 if (!h1sl) {
344 /* If there no start-line, be sure to only parse the headers */
345 h1m->flags |= H1_MF_HDRS_ONLY;
346 }
347 ret = h1_headers_to_hdr_list(b_peek(srcbuf, ofs), b_tail(srcbuf),
348 hdrs, sizeof(hdrs)/sizeof(hdrs[0]), h1m, h1sl);
349 if (ret <= 0) {
350 /* Incomplete or invalid message. If the input buffer only
351 * contains headers and is full, which is detected by it being
352 * full and the offset to be zero, it's an error because
353 * headers are too large to be handled by the parser. */
354 if (ret < 0 || (!ret && !ofs && !buf_room_for_htx_data(srcbuf)))
355 goto error;
356 goto end;
357 }
358
359 /* messages headers fully parsed, do some checks to prepare the body
360 * parsing.
361 */
362
363 if (!(h1m->flags & H1_MF_RESP)) {
364 if (!h1_process_req_vsn(h1m, h1sl)) {
365 h1m->err_pos = h1sl->rq.v.ptr - b_head(srcbuf);
366 h1m->err_state = h1m->state;
367 goto vsn_error;
368 }
369 if (!h1_postparse_req_hdrs(h1m, h1sl, dsthtx, hdrs, max))
370 ret = 0;
371 }
372 else {
373 if (h1sl && !h1_process_res_vsn(h1m, h1sl)) {
374 h1m->err_pos = h1sl->st.v.ptr - b_head(srcbuf);
375 h1m->err_state = h1m->state;
376 goto vsn_error;
377 }
378 if (!h1_postparse_res_hdrs(h1m, h1sl, dsthtx, hdrs, max))
379 ret = 0;
380 }
381
382 end:
383 return ret;
384 error:
385 h1m->err_pos = h1m->next;
386 h1m->err_state = h1m->state;
387 vsn_error:
388 dsthtx->flags |= HTX_FL_PARSING_ERROR;
389 return 0;
390
391}
392
Christopher Fauletcc3124c2019-08-12 22:42:21 +0200393/* Copy data from <srbuf> into an DATA block in <dsthtx>. If possible, a
394 * zero-copy is performed. It returns the number of bytes copied.
395 */
396static int h1_copy_msg_data(struct htx *dsthtx, struct buffer *srcbuf, size_t ofs,
397 size_t count, struct buffer *htxbuf)
398{
399 /* very often with large files we'll face the following
400 * situation :
401 * - htx is empty and points to <htxbuf>
402 * - ret == srcbuf->data
403 * - srcbuf->head == sizeof(struct htx)
404 * => we can swap the buffers and place an htx header into
405 * the target buffer instead
406 */
407 if (unlikely(htx_is_empty(dsthtx) && count == b_data(srcbuf) &&
408 !ofs && b_head_ofs(srcbuf) == sizeof(struct htx))) {
409 void *raw_area = srcbuf->area;
410 void *htx_area = htxbuf->area;
411 struct htx_blk *blk;
412
413 srcbuf->area = htx_area;
414 htxbuf->area = raw_area;
415 dsthtx = (struct htx *)htxbuf->area;
416 dsthtx->size = htxbuf->size - sizeof(*dsthtx);
417 htx_reset(dsthtx);
418 b_set_data(htxbuf, b_size(htxbuf));
419
420 blk = htx_add_blk(dsthtx, HTX_BLK_DATA, count);
421 blk->info += count;
422 /* nothing else to do, the old buffer now contains an
423 * empty pre-initialized HTX header
424 */
425 return count;
426 }
427
428 return htx_add_data(dsthtx, ist2(b_peek(srcbuf, ofs), count));
429}
430
Christopher Faulet4f0f88a2019-08-10 11:17:44 +0200431/* Parse HTTP/1 body. It returns the number of bytes parsed if > 0, or 0 if it
432 * couldn't proceed. Parsing errors are reported by setting the htx flags
433 * HTX_FL_PARSING_ERROR and filling h1m->err_pos and h1m->err_state fields. This
434 * functions is responsible to update the parser state <h1m>.
435 */
436int h1_parse_msg_data(struct h1m *h1m, struct htx *dsthtx,
437 struct buffer *srcbuf, size_t ofs, size_t max,
438 struct buffer *htxbuf)
439{
440 size_t total = 0;
441 int32_t ret = 0;
442
443 if (h1m->flags & H1_MF_CLEN) {
444 /* content-length: read only h2m->body_len */
445 ret = htx_get_max_blksz(dsthtx, max);
446 if ((uint64_t)ret > h1m->curr_len)
447 ret = h1m->curr_len;
448 if (ret > b_contig_data(srcbuf, ofs))
449 ret = b_contig_data(srcbuf, ofs);
450 if (ret) {
Christopher Faulet4f0f88a2019-08-10 11:17:44 +0200451 int32_t try = ret;
452
Christopher Fauletcc3124c2019-08-12 22:42:21 +0200453 ret = h1_copy_msg_data(dsthtx, srcbuf, ofs, try, htxbuf);
Christopher Faulet4f0f88a2019-08-10 11:17:44 +0200454 h1m->curr_len -= ret;
455 max -= sizeof(struct htx_blk) + ret;
456 ofs += ret;
457 total += ret;
458 if (ret < try)
459 goto end;
460 }
461
462 if (!h1m->curr_len) {
463 if (max < sizeof(struct htx_blk) + 1 || !htx_add_endof(dsthtx, HTX_BLK_EOM))
464 goto end;
465 h1m->state = H1_MSG_DONE;
466 }
467 }
468 else if (h1m->flags & H1_MF_CHNK) {
469 /* te:chunked : parse chunks */
470 new_chunk:
471 if (h1m->state == H1_MSG_CHUNK_CRLF) {
472 ret = h1_skip_chunk_crlf(srcbuf, ofs, b_data(srcbuf));
473 if (ret <= 0)
474 goto end;
475 h1m->state = H1_MSG_CHUNK_SIZE;
476 ofs += ret;
477 total += ret;
478 }
479 if (h1m->state == H1_MSG_CHUNK_SIZE) {
480 unsigned int chksz;
481
482 ret = h1_parse_chunk_size(srcbuf, ofs, b_data(srcbuf), &chksz);
483 if (ret <= 0)
484 goto end;
485 h1m->state = ((!chksz) ? H1_MSG_TRAILERS : H1_MSG_DATA);
486 h1m->curr_len = chksz;
487 h1m->body_len += chksz;
488 ofs += ret;
489 total += ret;
490 if (!h1m->curr_len)
491 goto end;
492 }
493 if (h1m->state == H1_MSG_DATA) {
494 ret = htx_get_max_blksz(dsthtx, max);
495 if ((uint64_t)ret > h1m->curr_len)
496 ret = h1m->curr_len;
497 if (ret > b_contig_data(srcbuf, ofs))
498 ret = b_contig_data(srcbuf, ofs);
499 if (ret) {
500 int32_t try = ret;
501
Christopher Fauletcc3124c2019-08-12 22:42:21 +0200502 ret = h1_copy_msg_data(dsthtx, srcbuf, ofs, try, htxbuf);
Christopher Faulet4f0f88a2019-08-10 11:17:44 +0200503 h1m->curr_len -= ret;
504 max -= sizeof(struct htx_blk) + ret;
505 ofs += ret;
506 total += ret;
507 if (ret < try)
508 goto end;
509 }
510 if (!h1m->curr_len) {
511 h1m->state = H1_MSG_CHUNK_CRLF;
512 goto new_chunk;
513 }
514 goto end;
515 }
516 }
517 else if (h1m->flags & H1_MF_XFER_LEN) {
518 /* XFER_LEN is set but not CLEN nor CHNK, it means there is no
519 * body. Switch the message in DONE state
520 */
521 if (max < sizeof(struct htx_blk) + 1 || !htx_add_endof(dsthtx, HTX_BLK_EOM))
522 goto end;
523 h1m->state = H1_MSG_DONE;
524 }
525 else {
526 /* no content length, read till SHUTW */
527 ret = htx_get_max_blksz(dsthtx, max);
528 if (ret > b_contig_data(srcbuf, ofs))
529 ret = b_contig_data(srcbuf, ofs);
530 if (ret)
Christopher Fauletcc3124c2019-08-12 22:42:21 +0200531 total += h1_copy_msg_data(dsthtx, srcbuf, ofs, ret, htxbuf);
Christopher Faulet4f0f88a2019-08-10 11:17:44 +0200532 }
533
534 end:
535 if (ret < 0) {
536 dsthtx->flags |= HTX_FL_PARSING_ERROR;
537 h1m->err_state = h1m->state;
538 h1m->err_pos = ofs;
539 total = 0;
540 }
541
542 /* update htx->extra, only when the body length is known */
543 if (h1m->flags & H1_MF_XFER_LEN)
544 dsthtx->extra = h1m->curr_len;
545 return total;
546}
547
548/* Parse HTTP/1 trailers. It returns the number of bytes parsed if > 0, or 0 if
549 * it couldn't proceed. Parsing errors are reported by setting the htx flags
550 * HTX_FL_PARSING_ERROR and filling h1m->err_pos and h1m->err_state fields. This
551 * functions is responsible to update the parser state <h1m>.
552 */
553int h1_parse_msg_tlrs(struct h1m *h1m, struct htx *dsthtx,
554 struct buffer *srcbuf, size_t ofs, size_t max)
555{
556 struct http_hdr hdrs[global.tune.max_http_hdr];
557 struct h1m tlr_h1m;
558 int ret = 0;
559
560 if (!max || !b_data(srcbuf))
561 goto end;
562
563 /* Realing input buffer if necessary */
564 if (b_peek(srcbuf, ofs) > b_tail(srcbuf))
565 b_slow_realign(srcbuf, trash.area, 0);
566
567 tlr_h1m.flags = (H1_MF_NO_PHDR|H1_MF_HDRS_ONLY);
568 ret = h1_headers_to_hdr_list(b_peek(srcbuf, ofs), b_tail(srcbuf),
569 hdrs, sizeof(hdrs)/sizeof(hdrs[0]), &tlr_h1m, NULL);
570 if (ret <= 0) {
571 /* Incomplete or invalid trailers. If the input buffer only
572 * contains trailers and is full, which is detected by it being
573 * full and the offset to be zero, it's an error because
574 * trailers are too large to be handled by the parser. */
575 if (ret < 0 || (!ret && !ofs && !buf_room_for_htx_data(srcbuf)))
576 goto error;
577 goto end;
578 }
579
580 /* messages trailers fully parsed. */
581 if (h1_eval_htx_hdrs_size(hdrs) > max) {
582 if (htx_is_empty(dsthtx))
583 goto error;
584 ret = 0;
585 goto end;
586 }
587
588 if (!htx_add_all_trailers(dsthtx, hdrs))
589 goto error;
590
591 end:
592 return ret;
593 error:
594 h1m->err_state = h1m->state;
595 h1m->err_pos = h1m->next;
596 dsthtx->flags |= HTX_FL_PARSING_ERROR;
597 return 0;
598}
599
600/*
601 * Local variables:
602 * c-indent-level: 8
603 * c-basic-offset: 8
604 * End:
605 */