blob: 70e4b2cb01be7df5ac7a1c95b708f40b53a1ced4 [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
Willy Tarreau4c7e4b72020-05-27 12:58:42 +020013#include <haproxy/api.h>
Willy Tarreau6be78492020-06-05 00:00:29 +020014#include <haproxy/cfgparse.h>
Willy Tarreaudfd3de82020-06-04 23:46:14 +020015#include <haproxy/global.h>
Willy Tarreau5413a872020-06-02 19:33:08 +020016#include <haproxy/h1.h>
Willy Tarreauc6fe8842020-06-04 09:00:02 +020017#include <haproxy/h1_htx.h>
Willy Tarreaucd72d8c2020-06-02 19:11:26 +020018#include <haproxy/http.h>
Amaury Denoyelle852d78c2021-07-07 10:49:27 +020019#include <haproxy/http_htx.h>
Willy Tarreau16f958c2020-06-03 08:44:35 +020020#include <haproxy/htx.h>
Willy Tarreau36979d92020-06-05 17:27:29 +020021#include <haproxy/tools.h>
Christopher Faulet4f0f88a2019-08-10 11:17:44 +020022
Christopher Faulet4f0f88a2019-08-10 11:17:44 +020023/* Estimate the size of the HTX headers after the parsing, including the EOH. */
24static size_t h1_eval_htx_hdrs_size(const struct http_hdr *hdrs)
25{
26 size_t sz = 0;
27 int i;
28
29 for (i = 0; hdrs[i].n.len; i++)
30 sz += sizeof(struct htx_blk) + hdrs[i].n.len + hdrs[i].v.len;
31 sz += sizeof(struct htx_blk) + 1;
32 return sz;
33}
34
35/* Estimate the size of the HTX request after the parsing. */
36static size_t h1_eval_htx_size(const struct ist p1, const struct ist p2, const struct ist p3,
37 const struct http_hdr *hdrs)
38{
39 size_t sz;
40
41 /* size of the HTX start-line */
42 sz = sizeof(struct htx_blk) + sizeof(struct htx_sl) + p1.len + p2.len + p3.len;
43 sz += h1_eval_htx_hdrs_size(hdrs);
44 return sz;
45}
46
Christopher Faulet4f0f88a2019-08-10 11:17:44 +020047/* Check the validity of the request version. If the version is valid, it
48 * returns 1. Otherwise, it returns 0.
49 */
50static int h1_process_req_vsn(struct h1m *h1m, union h1_sl *sl)
51{
52 /* RFC7230#2.6 has enforced the format of the HTTP version string to be
53 * exactly one digit "." one digit. This check may be disabled using
54 * option accept-invalid-http-request.
55 */
56 if (h1m->err_pos == -2) { /* PR_O2_REQBUG_OK not set */
57 if (sl->rq.v.len != 8)
58 return 0;
59
60 if (*(sl->rq.v.ptr + 4) != '/' ||
61 !isdigit((unsigned char)*(sl->rq.v.ptr + 5)) ||
62 *(sl->rq.v.ptr + 6) != '.' ||
63 !isdigit((unsigned char)*(sl->rq.v.ptr + 7)))
64 return 0;
65 }
66 else if (!sl->rq.v.len) {
67 /* try to convert HTTP/0.9 requests to HTTP/1.0 */
68
69 /* RFC 1945 allows only GET for HTTP/0.9 requests */
70 if (sl->rq.meth != HTTP_METH_GET)
71 return 0;
72
73 /* HTTP/0.9 requests *must* have a request URI, per RFC 1945 */
74 if (!sl->rq.u.len)
75 return 0;
76
77 /* Add HTTP version */
78 sl->rq.v = ist("HTTP/1.0");
79 return 1;
80 }
81
82 if ((sl->rq.v.len == 8) &&
83 ((*(sl->rq.v.ptr + 5) > '1') ||
84 ((*(sl->rq.v.ptr + 5) == '1') && (*(sl->rq.v.ptr + 7) >= '1'))))
85 h1m->flags |= H1_MF_VER_11;
86 return 1;
87}
88
89/* Check the validity of the response version. If the version is valid, it
90 * returns 1. Otherwise, it returns 0.
91 */
92static int h1_process_res_vsn(struct h1m *h1m, union h1_sl *sl)
93{
94 /* RFC7230#2.6 has enforced the format of the HTTP version string to be
95 * exactly one digit "." one digit. This check may be disabled using
96 * option accept-invalid-http-request.
97 */
98 if (h1m->err_pos == -2) { /* PR_O2_REQBUG_OK not set */
99 if (sl->st.v.len != 8)
100 return 0;
101
102 if (*(sl->st.v.ptr + 4) != '/' ||
103 !isdigit((unsigned char)*(sl->st.v.ptr + 5)) ||
104 *(sl->st.v.ptr + 6) != '.' ||
105 !isdigit((unsigned char)*(sl->st.v.ptr + 7)))
106 return 0;
107 }
108
109 if ((sl->st.v.len == 8) &&
110 ((*(sl->st.v.ptr + 5) > '1') ||
111 ((*(sl->st.v.ptr + 5) == '1') && (*(sl->st.v.ptr + 7) >= '1'))))
112 h1m->flags |= H1_MF_VER_11;
113
114 return 1;
115}
116
117/* Convert H1M flags to HTX start-line flags. */
118static unsigned int h1m_htx_sl_flags(struct h1m *h1m)
119{
120 unsigned int flags = HTX_SL_F_NONE;
121
122 if (h1m->flags & H1_MF_RESP)
123 flags |= HTX_SL_F_IS_RESP;
124 if (h1m->flags & H1_MF_VER_11)
125 flags |= HTX_SL_F_VER_11;
126 if (h1m->flags & H1_MF_XFER_ENC)
127 flags |= HTX_SL_F_XFER_ENC;
128 if (h1m->flags & H1_MF_XFER_LEN) {
129 flags |= HTX_SL_F_XFER_LEN;
130 if (h1m->flags & H1_MF_CHNK)
131 flags |= HTX_SL_F_CHNK;
132 else if (h1m->flags & H1_MF_CLEN) {
133 flags |= HTX_SL_F_CLEN;
134 if (h1m->body_len == 0)
135 flags |= HTX_SL_F_BODYLESS;
136 }
137 else
138 flags |= HTX_SL_F_BODYLESS;
139 }
Christopher Faulet576c3582021-01-08 15:53:01 +0100140 if (h1m->flags & H1_MF_CONN_UPG)
141 flags |= HTX_SL_F_CONN_UPG;
Christopher Faulet4f0f88a2019-08-10 11:17:44 +0200142 return flags;
143}
144
145/* Postprocess the parsed headers for a request and convert them into an htx
146 * message. It returns the number of bytes parsed if > 0, or 0 if it couldn't
147 * proceed. Parsing errors are reported by setting the htx flag
148 * HTX_FL_PARSING_ERROR and filling h1m->err_pos and h1m->err_state fields.
149 */
150static int h1_postparse_req_hdrs(struct h1m *h1m, union h1_sl *h1sl, struct htx *htx,
151 struct http_hdr *hdrs, size_t max)
152{
153 struct htx_sl *sl;
154 struct ist meth, uri, vsn;
155 unsigned int flags;
Christopher Faulet4f0f88a2019-08-10 11:17:44 +0200156
157 /* <h1sl> is always defined for a request */
158 meth = h1sl->rq.m;
159 uri = h1sl->rq.u;
160 vsn = h1sl->rq.v;
161
162 /* Be sure the message, once converted into HTX, will not exceed the max
163 * size allowed.
164 */
165 if (h1_eval_htx_size(meth, uri, vsn, hdrs) > max) {
166 if (htx_is_empty(htx))
167 goto error;
168 h1m_init_res(h1m);
169 h1m->flags |= (H1_MF_NO_PHDR|H1_MF_CLEAN_CONN_HDR);
170 return 0;
171 }
172
173 /* By default, request have always a known length */
174 h1m->flags |= H1_MF_XFER_LEN;
175
176 if (h1sl->rq.meth == HTTP_METH_CONNECT) {
Christopher Faulet5be651d2021-01-22 15:28:03 +0100177 h1m->flags &= ~(H1_MF_CLEN|H1_MF_CHNK);
178 h1m->curr_len = h1m->body_len = 0;
Christopher Faulet4f0f88a2019-08-10 11:17:44 +0200179 }
180
Christopher Faulet4f0f88a2019-08-10 11:17:44 +0200181 flags = h1m_htx_sl_flags(h1m);
182 sl = htx_add_stline(htx, HTX_BLK_REQ_SL, flags, meth, uri, vsn);
183 if (!sl || !htx_add_all_headers(htx, hdrs))
184 goto error;
185 sl->info.req.meth = h1sl->rq.meth;
186
Christopher Fauletfe451fb2019-10-08 15:01:34 +0200187 /* Check if the uri contains an authority. Also check if it contains an
188 * explicit scheme and if it is "http" or "https". */
189 if (h1sl->rq.meth == HTTP_METH_CONNECT)
190 sl->flags |= HTX_SL_F_HAS_AUTHORITY;
191 else if (uri.len && uri.ptr[0] != '/' && uri.ptr[0] != '*') {
192 sl->flags |= (HTX_SL_F_HAS_AUTHORITY|HTX_SL_F_HAS_SCHM);
Christopher Faulet4f0f88a2019-08-10 11:17:44 +0200193 if (uri.len > 4 && (uri.ptr[0] | 0x20) == 'h')
194 sl->flags |= ((uri.ptr[4] == ':') ? HTX_SL_F_SCHM_HTTP : HTX_SL_F_SCHM_HTTPS);
Amaury Denoyelle852d78c2021-07-07 10:49:27 +0200195
196 /* absolute-form target URI present, proceed to scheme-based
197 * normalization */
198 http_scheme_based_normalize(htx);
Christopher Faulet4f0f88a2019-08-10 11:17:44 +0200199 }
Christopher Faulet4f0f88a2019-08-10 11:17:44 +0200200
201 /* If body length cannot be determined, set htx->extra to
202 * ULLONG_MAX. This value is impossible in other cases.
203 */
204 htx->extra = ((h1m->flags & H1_MF_XFER_LEN) ? h1m->curr_len : ULLONG_MAX);
205
206 end:
207 return 1;
208 error:
209 h1m->err_pos = h1m->next;
210 h1m->err_state = h1m->state;
211 htx->flags |= HTX_FL_PARSING_ERROR;
212 return 0;
213}
214
215/* Postprocess the parsed headers for a response and convert them into an htx
216 * message. It returns the number of bytes parsed if > 0, or 0 if it couldn't
217 * proceed. Parsing errors are reported by setting the htx flag
218 * HTX_FL_PARSING_ERROR and filling h1m->err_pos and h1m->err_state fields.
219 */
220static int h1_postparse_res_hdrs(struct h1m *h1m, union h1_sl *h1sl, struct htx *htx,
221 struct http_hdr *hdrs, size_t max)
222{
223 struct htx_sl *sl;
224 struct ist vsn, status, reason;
225 unsigned int flags;
Christopher Faulet4f0f88a2019-08-10 11:17:44 +0200226 uint16_t code = 0;
227
228 if (h1sl) {
229 /* For HTTP responses, the start-line was parsed */
230 code = h1sl->st.status;
231 vsn = h1sl->st.v;
232 status = h1sl->st.c;
233 reason = h1sl->st.r;
234 }
235 else {
236 /* For FCGI responses, there is no start(-line but the "Status"
237 * header must be parsed, if found.
238 */
239 int hdr;
240
241 vsn = ((h1m->flags & H1_MF_VER_11) ? ist("HTTP/1.1") : ist("HTTP/1.0"));
242 for (hdr = 0; hdrs[hdr].n.len; hdr++) {
243 if (isteqi(hdrs[hdr].n, ist("status"))) {
244 code = http_parse_status_val(hdrs[hdr].v, &status, &reason);
245 }
246 else if (isteqi(hdrs[hdr].n, ist("location"))) {
247 code = 302;
248 status = ist("302");
249 reason = ist("Moved Temporarily");
250 }
251 }
252 if (!code) {
253 code = 200;
254 status = ist("200");
255 reason = ist("OK");
256 }
257 /* FIXME: Check the codes 1xx ? */
258 }
259
260 /* Be sure the message, once converted into HTX, will not exceed the max
261 * size allowed.
262 */
263 if (h1_eval_htx_size(vsn, status, reason, hdrs) > max) {
264 if (htx_is_empty(htx))
265 goto error;
266 h1m_init_res(h1m);
267 h1m->flags |= (H1_MF_NO_PHDR|H1_MF_CLEAN_CONN_HDR);
268 return 0;
269 }
270
Christopher Fauletc75668e2020-12-07 18:10:32 +0100271 if (((h1m->flags & H1_MF_METH_CONNECT) && code >= 200 && code < 300) || code == 101) {
Christopher Faulet5be651d2021-01-22 15:28:03 +0100272 h1m->flags &= ~(H1_MF_CLEN|H1_MF_CHNK);
273 h1m->flags |= H1_MF_XFER_LEN;
274 h1m->curr_len = h1m->body_len = 0;
Christopher Faulet4f0f88a2019-08-10 11:17:44 +0200275 }
276 else if ((h1m->flags & H1_MF_METH_HEAD) || (code >= 100 && code < 200) ||
277 (code == 204) || (code == 304)) {
278 /* Responses known to have no body. */
279 h1m->flags &= ~(H1_MF_CLEN|H1_MF_CHNK);
280 h1m->flags |= H1_MF_XFER_LEN;
281 h1m->curr_len = h1m->body_len = 0;
282 }
283 else if (h1m->flags & (H1_MF_CLEN|H1_MF_CHNK)) {
284 /* Responses with a known body length. */
285 h1m->flags |= H1_MF_XFER_LEN;
286 }
Christopher Faulet4f0f88a2019-08-10 11:17:44 +0200287
Christopher Faulet4f0f88a2019-08-10 11:17:44 +0200288 flags = h1m_htx_sl_flags(h1m);
289 sl = htx_add_stline(htx, HTX_BLK_RES_SL, flags, vsn, status, reason);
290 if (!sl || !htx_add_all_headers(htx, hdrs))
291 goto error;
292 sl->info.res.status = code;
293
Christopher Faulet4f0f88a2019-08-10 11:17:44 +0200294 /* If body length cannot be determined, set htx->extra to
295 * ULLONG_MAX. This value is impossible in other cases.
296 */
297 htx->extra = ((h1m->flags & H1_MF_XFER_LEN) ? h1m->curr_len : ULLONG_MAX);
298
299 end:
300 return 1;
301 error:
302 h1m->err_pos = h1m->next;
303 h1m->err_state = h1m->state;
304 htx->flags |= HTX_FL_PARSING_ERROR;
305 return 0;
306}
307
308/* Parse HTTP/1 headers. It returns the number of bytes parsed if > 0, or 0 if
309 * it couldn't proceed. Parsing errors are reported by setting the htx flag
310 * HTX_FL_PARSING_ERROR and filling h1m->err_pos and h1m->err_state fields. This
311 * functions is responsible to update the parser state <h1m> and the start-line
312 * <h1sl> if not NULL.
313 * For the requests, <h1sl> must always be provided. For responses, <h1sl> may
314 * be NULL and <h1m> flags HTTP_METH_CONNECT of HTTP_METH_HEAD may be set.
315 */
Christopher Fauletde471a42021-02-01 16:37:28 +0100316size_t h1_parse_msg_hdrs(struct h1m *h1m, union h1_sl *h1sl, struct htx *dsthtx,
317 struct buffer *srcbuf, size_t ofs, size_t max)
Christopher Faulet4f0f88a2019-08-10 11:17:44 +0200318{
319 struct http_hdr hdrs[global.tune.max_http_hdr];
320 int ret = 0;
321
322 if (!max || !b_data(srcbuf))
323 goto end;
324
325 /* Realing input buffer if necessary */
326 if (b_head(srcbuf) + b_data(srcbuf) > b_wrap(srcbuf))
Christopher Faulet00d7cde2021-02-04 11:01:51 +0100327 b_slow_realign_ofs(srcbuf, trash.area, 0);
Christopher Faulet4f0f88a2019-08-10 11:17:44 +0200328
329 if (!h1sl) {
330 /* If there no start-line, be sure to only parse the headers */
331 h1m->flags |= H1_MF_HDRS_ONLY;
332 }
333 ret = h1_headers_to_hdr_list(b_peek(srcbuf, ofs), b_tail(srcbuf),
334 hdrs, sizeof(hdrs)/sizeof(hdrs[0]), h1m, h1sl);
335 if (ret <= 0) {
336 /* Incomplete or invalid message. If the input buffer only
337 * contains headers and is full, which is detected by it being
338 * full and the offset to be zero, it's an error because
339 * headers are too large to be handled by the parser. */
340 if (ret < 0 || (!ret && !ofs && !buf_room_for_htx_data(srcbuf)))
341 goto error;
342 goto end;
343 }
344
345 /* messages headers fully parsed, do some checks to prepare the body
346 * parsing.
347 */
348
349 if (!(h1m->flags & H1_MF_RESP)) {
350 if (!h1_process_req_vsn(h1m, h1sl)) {
351 h1m->err_pos = h1sl->rq.v.ptr - b_head(srcbuf);
352 h1m->err_state = h1m->state;
353 goto vsn_error;
354 }
355 if (!h1_postparse_req_hdrs(h1m, h1sl, dsthtx, hdrs, max))
356 ret = 0;
357 }
358 else {
359 if (h1sl && !h1_process_res_vsn(h1m, h1sl)) {
360 h1m->err_pos = h1sl->st.v.ptr - b_head(srcbuf);
361 h1m->err_state = h1m->state;
362 goto vsn_error;
363 }
364 if (!h1_postparse_res_hdrs(h1m, h1sl, dsthtx, hdrs, max))
365 ret = 0;
366 }
367
Christopher Faulet76014fd2019-12-10 11:47:22 +0100368 /* Switch messages without any payload to DONE state */
369 if (((h1m->flags & H1_MF_CLEN) && h1m->body_len == 0) ||
Christopher Fauletd1ac2b92020-12-02 19:12:22 +0100370 ((h1m->flags & (H1_MF_XFER_LEN|H1_MF_CLEN|H1_MF_CHNK)) == H1_MF_XFER_LEN)) {
Christopher Faulet76014fd2019-12-10 11:47:22 +0100371 h1m->state = H1_MSG_DONE;
Christopher Fauletd1ac2b92020-12-02 19:12:22 +0100372 dsthtx->flags |= HTX_FL_EOM;
373 }
Christopher Faulet76014fd2019-12-10 11:47:22 +0100374
Christopher Faulet4f0f88a2019-08-10 11:17:44 +0200375 end:
376 return ret;
377 error:
378 h1m->err_pos = h1m->next;
379 h1m->err_state = h1m->state;
380 vsn_error:
381 dsthtx->flags |= HTX_FL_PARSING_ERROR;
382 return 0;
383
384}
385
Christopher Fauletcc3124c2019-08-12 22:42:21 +0200386/* Copy data from <srbuf> into an DATA block in <dsthtx>. If possible, a
387 * zero-copy is performed. It returns the number of bytes copied.
388 */
Christopher Fauletde471a42021-02-01 16:37:28 +0100389static size_t h1_copy_msg_data(struct htx **dsthtx, struct buffer *srcbuf, size_t ofs,
Christopher Fauletf7c20442021-02-02 19:40:07 +0100390 size_t count, size_t max, struct buffer *htxbuf)
Christopher Fauletcc3124c2019-08-12 22:42:21 +0200391{
Christopher Fauletaf542632019-10-01 21:52:49 +0200392 struct htx *tmp_htx = *dsthtx;
Christopher Fauletf7c20442021-02-02 19:40:07 +0100393 size_t block1, block2, ret = 0;
394
395 /* Be prepared to create at least one HTX block by reserving its size
396 * and adjust <count> accordingly.
397 */
398 max -= sizeof(struct htx_blk);
399 if (count > max)
400 count = max;
Christopher Fauletaf542632019-10-01 21:52:49 +0200401
Christopher Fauletcc3124c2019-08-12 22:42:21 +0200402 /* very often with large files we'll face the following
403 * situation :
404 * - htx is empty and points to <htxbuf>
Christopher Fauletf7c20442021-02-02 19:40:07 +0100405 * - count == srcbuf->data
Christopher Fauletcc3124c2019-08-12 22:42:21 +0200406 * - srcbuf->head == sizeof(struct htx)
407 * => we can swap the buffers and place an htx header into
408 * the target buffer instead
409 */
Christopher Fauletaf542632019-10-01 21:52:49 +0200410 if (unlikely(htx_is_empty(tmp_htx) && count == b_data(srcbuf) &&
Christopher Fauletcc3124c2019-08-12 22:42:21 +0200411 !ofs && b_head_ofs(srcbuf) == sizeof(struct htx))) {
412 void *raw_area = srcbuf->area;
413 void *htx_area = htxbuf->area;
414 struct htx_blk *blk;
415
416 srcbuf->area = htx_area;
417 htxbuf->area = raw_area;
Christopher Fauletaf542632019-10-01 21:52:49 +0200418 tmp_htx = (struct htx *)htxbuf->area;
419 tmp_htx->size = htxbuf->size - sizeof(*tmp_htx);
420 htx_reset(tmp_htx);
Christopher Fauletcc3124c2019-08-12 22:42:21 +0200421 b_set_data(htxbuf, b_size(htxbuf));
422
Christopher Fauletaf542632019-10-01 21:52:49 +0200423 blk = htx_add_blk(tmp_htx, HTX_BLK_DATA, count);
Christopher Fauletcc3124c2019-08-12 22:42:21 +0200424 blk->info += count;
Christopher Fauletaf542632019-10-01 21:52:49 +0200425
426 *dsthtx = tmp_htx;
Christopher Fauletcc3124c2019-08-12 22:42:21 +0200427 /* nothing else to do, the old buffer now contains an
428 * empty pre-initialized HTX header
429 */
430 return count;
431 }
432
Christopher Fauletf7c20442021-02-02 19:40:07 +0100433 /* * First block is the copy of contiguous data starting at offset <ofs>
434 * with <count> as max. <max> is updated accordingly
435 *
436 * * Second block is the remaining (count - block1) if <max> is large
437 * enough. Another HTX block is reserved.
438 */
439 block1 = b_contig_data(srcbuf, ofs);
440 block2 = 0;
441 if (block1 > count)
442 block1 = count;
443 max -= block1;
444
445 if (max > sizeof(struct htx_blk)) {
446 block2 = count - block1;
447 max -= sizeof(struct htx_blk);
448 if (block2 > max)
449 block2 = max;
450 }
451
452 ret = htx_add_data(tmp_htx, ist2(b_peek(srcbuf, ofs), block1));
453 if (ret == block1 && block2)
454 ret += htx_add_data(tmp_htx, ist2(b_orig(srcbuf), block2));
455 end:
456 return ret;
Christopher Fauletcc3124c2019-08-12 22:42:21 +0200457}
458
Christopher Faulet7a835f32021-05-21 11:31:35 +0200459static const char hextable[] = {
460 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
461 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,-1,-1,-1,-1,-1,-1,
462 -1,10,11,12,13,14,15,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
463 -1,10,11,12,13,14,15,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
464 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
465 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
466 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,
467 -1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1
468};
469
Christopher Faulet0d4c9242021-05-21 10:56:24 +0200470/* Generic function to parse the current HTTP chunk. It may be used to parsed
Ilya Shipitsin213bb992021-06-12 15:55:27 +0500471 * any kind of chunks, including incomplete HTTP chunks or split chunks
Christopher Faulet0d4c9242021-05-21 10:56:24 +0200472 * because the buffer wraps. This version tries to performed zero-copy on large
473 * chunks if possible.
Christopher Faulet140691b2021-02-03 11:51:24 +0100474 */
Christopher Faulet0d4c9242021-05-21 10:56:24 +0200475static size_t h1_parse_chunk(struct h1m *h1m, struct htx **dsthtx,
476 struct buffer *srcbuf, size_t ofs, size_t *max,
477 struct buffer *htxbuf)
Christopher Faulet140691b2021-02-03 11:51:24 +0100478{
479 uint64_t chksz;
Christopher Faulet0d4c9242021-05-21 10:56:24 +0200480 size_t sz, used, lmax, total = 0;
Christopher Faulet140691b2021-02-03 11:51:24 +0100481 int ret = 0;
482
Christopher Faulet0d4c9242021-05-21 10:56:24 +0200483 lmax = *max;
Christopher Faulet140691b2021-02-03 11:51:24 +0100484 switch (h1m->state) {
485 case H1_MSG_DATA:
486 new_chunk:
487 used = htx_used_space(*dsthtx);
488
Christopher Faulet0d4c9242021-05-21 10:56:24 +0200489 if (b_data(srcbuf) == ofs || !lmax)
Christopher Faulet140691b2021-02-03 11:51:24 +0100490 break;
491
492 sz = b_data(srcbuf) - ofs;
493 if (unlikely(sz > h1m->curr_len))
494 sz = h1m->curr_len;
Christopher Faulet0d4c9242021-05-21 10:56:24 +0200495 sz = h1_copy_msg_data(dsthtx, srcbuf, ofs, sz, lmax, htxbuf);
496 lmax -= htx_used_space(*dsthtx) - used;
Christopher Faulet140691b2021-02-03 11:51:24 +0100497 ofs += sz;
498 total += sz;
499 h1m->curr_len -= sz;
500 if (h1m->curr_len)
501 break;
502
503 h1m->state = H1_MSG_CHUNK_CRLF;
504 /*fall through */
505
506 case H1_MSG_CHUNK_CRLF:
507 ret = h1_skip_chunk_crlf(srcbuf, ofs, b_data(srcbuf));
508 if (ret <= 0)
509 break;
510 ofs += ret;
511 total += ret;
Christopher Faulet0d4c9242021-05-21 10:56:24 +0200512
513 /* Don't parse next chunk to try to handle contiguous chunks if possible */
Christopher Faulet140691b2021-02-03 11:51:24 +0100514 h1m->state = H1_MSG_CHUNK_SIZE;
Christopher Faulet0d4c9242021-05-21 10:56:24 +0200515 break;
Christopher Faulet140691b2021-02-03 11:51:24 +0100516
517 case H1_MSG_CHUNK_SIZE:
518 ret = h1_parse_chunk_size(srcbuf, ofs, b_data(srcbuf), &chksz);
519 if (ret <= 0)
520 break;
521 h1m->state = ((!chksz) ? H1_MSG_TRAILERS : H1_MSG_DATA);
522 h1m->curr_len = chksz;
523 h1m->body_len += chksz;
524 ofs += ret;
525 total += ret;
526
527 if (h1m->curr_len) {
528 h1m->state = H1_MSG_DATA;
529 goto new_chunk;
530 }
531 h1m->state = H1_MSG_TRAILERS;
532 break;
533
534 default:
535 /* unexpected */
536 ret = -1;
537 break;
538 }
539
540 if (ret < 0) {
541 (*dsthtx)->flags |= HTX_FL_PARSING_ERROR;
542 h1m->err_state = h1m->state;
543 h1m->err_pos = ofs;
544 total = 0;
545 }
546
547 /* Don't forget to update htx->extra */
548 (*dsthtx)->extra = h1m->curr_len;
Christopher Faulet0d4c9242021-05-21 10:56:24 +0200549 *max = lmax;
550 return total;
551}
552
Christopher Fauletbdcefe52021-05-21 11:05:12 +0200553/* Parses full contiguous HTTP chunks. This version is optimized for small
554 * chunks and does not performed zero-copy. It must be called in
Ilya Shipitsin213bb992021-06-12 15:55:27 +0500555 * H1_MSG_CHUNK_SIZE state. Be careful if you change something in this
Christopher Fauletbdcefe52021-05-21 11:05:12 +0200556 * function. It is really sensitive, any change may have an impact on
557 * performance.
558 */
559static size_t h1_parse_full_contig_chunks(struct h1m *h1m, struct htx **dsthtx,
560 struct buffer *srcbuf, size_t ofs, size_t *max,
561 struct buffer *htxbuf)
562{
563 char *start, *end, *dptr;
564 ssize_t dpos, ridx, save;
565 size_t lmax, total = 0;
566 uint64_t chksz;
567 struct htx_ret htxret;
568
569 /* source info :
570 * start : pointer at <ofs> position
571 * end : pointer marking the end of data to parse
572 * ridx : the reverse index (negative) marking the parser position (end[ridx])
573 */
574 ridx = -b_contig_data(srcbuf, ofs);
575 if (!ridx)
576 goto out;
577 start = b_peek(srcbuf, ofs);
578 end = start - ridx;
579
580 /* Reserve the maximum possible size for the data */
581 htxret = htx_reserve_max_data(*dsthtx);
582 if (!htxret.blk)
583 goto out;
584
585 /* destination info :
586 * dptr : pointer on the beginning of the data
587 * dpos : current position where to copy data
588 */
589 dptr = htx_get_blk_ptr(*dsthtx, htxret.blk);
590 dpos = htxret.ret;
591
592 /* Empty DATA block is not possible, thus if <dpos> is the beginning of
593 * the block, it means it is a new block. We can remove the block size
594 * from <max>. Then we must adjust it if it exceeds the free size in the
595 * block.
596 */
597 lmax = *max;
598 if (!dpos)
599 lmax -= sizeof(struct htx_blk);
600 if (lmax > htx_get_blksz(htxret.blk) - dpos)
601 lmax = htx_get_blksz(htxret.blk) - dpos;
602
603 while (1) {
604 /* The chunk size is in the following form, though we are only
605 * interested in the size and CRLF :
606 * 1*HEXDIGIT *WSP *[ ';' extensions ] CRLF
607 */
608 chksz = 0;
609 save = ridx; /* Save the parser position to rewind if necessary */
610 while (1) {
611 int c;
612
613 if (!ridx)
614 goto end_parsing;
615
616 /* Convert current character */
Christopher Faulet7a835f32021-05-21 11:31:35 +0200617 c = hextable[(unsigned char)end[ridx]];
Christopher Fauletbdcefe52021-05-21 11:05:12 +0200618
619 /* not a hex digit anymore */
Christopher Fauletbf76df12021-06-11 13:39:06 +0200620 if (c & 0xF0)
Christopher Fauletbdcefe52021-05-21 11:05:12 +0200621 break;
622
623 /* Update current chunk size */
624 chksz = (chksz << 4) + c;
625
626 if (unlikely(chksz & 0xF0000000000000)) {
627 /* Don't get more than 13 hexa-digit (2^52 - 1)
628 * to never fed possibly bogus values from
629 * languages that use floats for their integers
630 */
631 goto parsing_error;
632 }
633 ++ridx;
634 }
635
636 if (unlikely(chksz > lmax))
637 goto end_parsing;
638
639 if (unlikely(ridx == save)) {
640 /* empty size not allowed */
641 goto parsing_error;
642 }
643
644 /* Skip spaces */
645 while (HTTP_IS_SPHT(end[ridx])) {
646 if (!++ridx)
647 goto end_parsing;
648 }
649
650 /* Up to there, we know that at least one byte is present. Check
651 * for the end of chunk size.
652 */
653 while (1) {
654 if (likely(end[ridx] == '\r')) {
655 /* Parse CRLF */
656 if (!++ridx)
657 goto end_parsing;
658 if (unlikely(end[ridx] != '\n')) {
659 /* CR must be followed by LF */
660 goto parsing_error;
661 }
662
663 /* done */
664 ++ridx;
665 break;
666 }
667 else if (end[ridx] == '\n') {
668 /* Parse LF only, nothing more to do */
669 ++ridx;
670 break;
671 }
672 else if (likely(end[ridx] == ';')) {
673 /* chunk extension, ends at next CRLF */
674 if (!++ridx)
675 goto end_parsing;
676 while (!HTTP_IS_CRLF(end[ridx])) {
677 if (!++ridx)
678 goto end_parsing;
679 }
680 /* we have a CRLF now, loop above */
681 continue;
682 }
683 else {
684 /* all other characters are unexpected */
685 goto parsing_error;
686 }
687 }
688
689 /* Exit if it is the last chunk */
690 if (unlikely(!chksz)) {
691 h1m->state = H1_MSG_TRAILERS;
692 save = ridx;
693 goto end_parsing;
694 }
695
696 /* Now check if the whole chunk is here (including the CRLF at
Ilya Shipitsin213bb992021-06-12 15:55:27 +0500697 * the end), otherwise we switch in H1_MSG_DATA state.
Christopher Fauletbdcefe52021-05-21 11:05:12 +0200698 */
699 if (chksz + 2 > -ridx) {
700 h1m->curr_len = chksz;
701 h1m->body_len += chksz;
702 h1m->state = H1_MSG_DATA;
703 (*dsthtx)->extra = h1m->curr_len;
704 save = ridx;
705 goto end_parsing;
706 }
707
708 memcpy(dptr + dpos, end + ridx, chksz);
709 h1m->body_len += chksz;
710 lmax -= chksz;
711 dpos += chksz;
712 ridx += chksz;
713
714 /* Parse CRLF or LF (always present) */
715 if (likely(end[ridx] == '\r'))
716 ++ridx;
717 if (end[ridx] != '\n') {
718 h1m->state = H1_MSG_CHUNK_CRLF;
719 goto parsing_error;
720 }
721 ++ridx;
722 }
723
724 end_parsing:
725 ridx = save;
726
727 /* Adjust the HTX block size or remove the block if nothing was copied
728 * (Empty HTX data block are not supported).
729 */
730 if (!dpos)
731 htx_remove_blk(*dsthtx, htxret.blk);
732 else
733 htx_change_blk_value_len(*dsthtx, htxret.blk, dpos);
734 total = end + ridx - start;
735 *max = lmax;
736
737 out:
738 return total;
739
740 parsing_error:
741 (*dsthtx)->flags |= HTX_FL_PARSING_ERROR;
742 h1m->err_state = h1m->state;
743 h1m->err_pos = ofs + end + ridx - start;
744 return 0;
745}
746
747/* Parse HTTP chunks. This function relies on an optimized function to parse
748 * contiguous chunks if possible. Otherwise, when a chunk is incomplete or when
749 * the underlying buffer is wrapping, a generic function is used.
750 */
Christopher Faulet0d4c9242021-05-21 10:56:24 +0200751static size_t h1_parse_msg_chunks(struct h1m *h1m, struct htx **dsthtx,
752 struct buffer *srcbuf, size_t ofs, size_t max,
753 struct buffer *htxbuf)
754{
755 size_t ret, total = 0;
756
757 while (ofs < b_data(srcbuf)) {
758 ret = 0;
759
Christopher Fauletbdcefe52021-05-21 11:05:12 +0200760 /* First parse full contiguous chunks. It is only possible if we
761 * are waiting for the next chunk size.
762 */
763 if (h1m->state == H1_MSG_CHUNK_SIZE) {
764 ret = h1_parse_full_contig_chunks(h1m, dsthtx, srcbuf, ofs, &max, htxbuf);
765 /* exit on error */
766 if (!ret && (*dsthtx)->flags & HTX_FL_PARSING_ERROR) {
767 total = 0;
768 break;
769 }
770 /* or let a chance to parse remaining data */
771 total += ret;
772 ofs += ret;
773 ret = 0;
774 }
775
776 /* If some data remains, try to parse it using the generic
Ilya Shipitsin213bb992021-06-12 15:55:27 +0500777 * function handling incomplete chunks and split chunks
Christopher Fauletbdcefe52021-05-21 11:05:12 +0200778 * because of a wrapping buffer.
779 */
780 if (h1m->state < H1_MSG_TRAILERS && ofs < b_data(srcbuf)) {
Christopher Faulet0d4c9242021-05-21 10:56:24 +0200781 ret = h1_parse_chunk(h1m, dsthtx, srcbuf, ofs, &max, htxbuf);
782 total += ret;
783 ofs += ret;
784 }
785
Christopher Fauletbdcefe52021-05-21 11:05:12 +0200786 /* nothing more was parsed or parsing was stopped on incomplete
787 * chunk, we can exit, handling parsing error if necessary.
Christopher Faulet0d4c9242021-05-21 10:56:24 +0200788 */
Christopher Fauletbdcefe52021-05-21 11:05:12 +0200789 if (!ret || h1m->state != H1_MSG_CHUNK_SIZE) {
Christopher Faulet0d4c9242021-05-21 10:56:24 +0200790 if ((*dsthtx)->flags & HTX_FL_PARSING_ERROR)
791 total = 0;
792 break;
793 }
794 }
795
Christopher Faulet140691b2021-02-03 11:51:24 +0100796 return total;
797}
798
Christopher Faulet4f0f88a2019-08-10 11:17:44 +0200799/* Parse HTTP/1 body. It returns the number of bytes parsed if > 0, or 0 if it
800 * couldn't proceed. Parsing errors are reported by setting the htx flags
801 * HTX_FL_PARSING_ERROR and filling h1m->err_pos and h1m->err_state fields. This
802 * functions is responsible to update the parser state <h1m>.
803 */
Christopher Fauletde471a42021-02-01 16:37:28 +0100804size_t h1_parse_msg_data(struct h1m *h1m, struct htx **dsthtx,
805 struct buffer *srcbuf, size_t ofs, size_t max,
806 struct buffer *htxbuf)
Christopher Faulet4f0f88a2019-08-10 11:17:44 +0200807{
Christopher Fauletde471a42021-02-01 16:37:28 +0100808 size_t sz, total = 0;
Christopher Faulet4f0f88a2019-08-10 11:17:44 +0200809
Christopher Fauletf7c20442021-02-02 19:40:07 +0100810 if (b_data(srcbuf) == ofs || !max)
Christopher Faulet140691b2021-02-03 11:51:24 +0100811 return 0;
Christopher Fauletf7c20442021-02-02 19:40:07 +0100812
Christopher Faulet4f0f88a2019-08-10 11:17:44 +0200813 if (h1m->flags & H1_MF_CLEN) {
814 /* content-length: read only h2m->body_len */
Christopher Fauletf7c20442021-02-02 19:40:07 +0100815 sz = b_data(srcbuf) - ofs;
816 if (unlikely(sz > h1m->curr_len))
Christopher Fauletde471a42021-02-01 16:37:28 +0100817 sz = h1m->curr_len;
Christopher Fauletf7c20442021-02-02 19:40:07 +0100818 sz = h1_copy_msg_data(dsthtx, srcbuf, ofs, sz, max, htxbuf);
819 h1m->curr_len -= sz;
820 (*dsthtx)->extra = h1m->curr_len;
821 total += sz;
Christopher Fauletd1ac2b92020-12-02 19:12:22 +0100822 if (!h1m->curr_len) {
Christopher Faulet4f0f88a2019-08-10 11:17:44 +0200823 h1m->state = H1_MSG_DONE;
Christopher Fauletd1ac2b92020-12-02 19:12:22 +0100824 (*dsthtx)->flags |= HTX_FL_EOM;
825 }
Christopher Faulet4f0f88a2019-08-10 11:17:44 +0200826 }
827 else if (h1m->flags & H1_MF_CHNK) {
828 /* te:chunked : parse chunks */
Christopher Faulet140691b2021-02-03 11:51:24 +0100829 total += h1_parse_msg_chunks(h1m, dsthtx, srcbuf, ofs, max, htxbuf);
Christopher Faulet4f0f88a2019-08-10 11:17:44 +0200830 }
831 else if (h1m->flags & H1_MF_XFER_LEN) {
832 /* XFER_LEN is set but not CLEN nor CHNK, it means there is no
833 * body. Switch the message in DONE state
834 */
Christopher Faulet4f0f88a2019-08-10 11:17:44 +0200835 h1m->state = H1_MSG_DONE;
Christopher Fauletd1ac2b92020-12-02 19:12:22 +0100836 (*dsthtx)->flags |= HTX_FL_EOM;
Christopher Faulet4f0f88a2019-08-10 11:17:44 +0200837 }
838 else {
839 /* no content length, read till SHUTW */
Christopher Fauletf7c20442021-02-02 19:40:07 +0100840 sz = b_data(srcbuf) - ofs;
841 sz = h1_copy_msg_data(dsthtx, srcbuf, ofs, sz, max, htxbuf);
842 total += sz;
Christopher Faulet4f0f88a2019-08-10 11:17:44 +0200843 }
844
Christopher Faulet4f0f88a2019-08-10 11:17:44 +0200845 return total;
846}
847
848/* Parse HTTP/1 trailers. It returns the number of bytes parsed if > 0, or 0 if
849 * it couldn't proceed. Parsing errors are reported by setting the htx flags
850 * HTX_FL_PARSING_ERROR and filling h1m->err_pos and h1m->err_state fields. This
851 * functions is responsible to update the parser state <h1m>.
852 */
Christopher Fauletde471a42021-02-01 16:37:28 +0100853size_t h1_parse_msg_tlrs(struct h1m *h1m, struct htx *dsthtx,
854 struct buffer *srcbuf, size_t ofs, size_t max)
Christopher Faulet4f0f88a2019-08-10 11:17:44 +0200855{
856 struct http_hdr hdrs[global.tune.max_http_hdr];
857 struct h1m tlr_h1m;
858 int ret = 0;
859
860 if (!max || !b_data(srcbuf))
861 goto end;
862
863 /* Realing input buffer if necessary */
864 if (b_peek(srcbuf, ofs) > b_tail(srcbuf))
Christopher Faulet00d7cde2021-02-04 11:01:51 +0100865 b_slow_realign_ofs(srcbuf, trash.area, 0);
Christopher Faulet4f0f88a2019-08-10 11:17:44 +0200866
867 tlr_h1m.flags = (H1_MF_NO_PHDR|H1_MF_HDRS_ONLY);
868 ret = h1_headers_to_hdr_list(b_peek(srcbuf, ofs), b_tail(srcbuf),
869 hdrs, sizeof(hdrs)/sizeof(hdrs[0]), &tlr_h1m, NULL);
870 if (ret <= 0) {
871 /* Incomplete or invalid trailers. If the input buffer only
872 * contains trailers and is full, which is detected by it being
873 * full and the offset to be zero, it's an error because
874 * trailers are too large to be handled by the parser. */
875 if (ret < 0 || (!ret && !ofs && !buf_room_for_htx_data(srcbuf)))
876 goto error;
877 goto end;
878 }
879
880 /* messages trailers fully parsed. */
881 if (h1_eval_htx_hdrs_size(hdrs) > max) {
882 if (htx_is_empty(dsthtx))
883 goto error;
884 ret = 0;
885 goto end;
886 }
887
888 if (!htx_add_all_trailers(dsthtx, hdrs))
889 goto error;
890
Christopher Faulet76014fd2019-12-10 11:47:22 +0100891 h1m->state = H1_MSG_DONE;
Christopher Fauletd1ac2b92020-12-02 19:12:22 +0100892 dsthtx->flags |= HTX_FL_EOM;
Christopher Faulet76014fd2019-12-10 11:47:22 +0100893
Christopher Faulet4f0f88a2019-08-10 11:17:44 +0200894 end:
895 return ret;
896 error:
897 h1m->err_state = h1m->state;
898 h1m->err_pos = h1m->next;
899 dsthtx->flags |= HTX_FL_PARSING_ERROR;
900 return 0;
901}
902
Christopher Faulet53a899b2019-10-08 16:38:42 +0200903/* Appends the H1 representation of the request line <sl> to the chunk <chk>. It
904 * returns 1 if data are successfully appended, otherwise it returns 0.
905 */
906int h1_format_htx_reqline(const struct htx_sl *sl, struct buffer *chk)
907{
908 struct ist uri;
909 size_t sz = chk->data;
910
Christopher Fauletfb38c912021-04-26 09:38:55 +0200911 uri = h1_get_uri(sl);
Christopher Faulet53a899b2019-10-08 16:38:42 +0200912 if (!chunk_memcat(chk, HTX_SL_REQ_MPTR(sl), HTX_SL_REQ_MLEN(sl)) ||
913 !chunk_memcat(chk, " ", 1) ||
914 !chunk_memcat(chk, uri.ptr, uri.len) ||
915 !chunk_memcat(chk, " ", 1))
916 goto full;
917
918 if (sl->flags & HTX_SL_F_VER_11) {
919 if (!chunk_memcat(chk, "HTTP/1.1", 8))
920 goto full;
921 }
922 else {
923 if (!chunk_memcat(chk, HTX_SL_REQ_VPTR(sl), HTX_SL_REQ_VLEN(sl)))
924 goto full;
925 }
926
927 if (!chunk_memcat(chk, "\r\n", 2))
928 goto full;
929
930 return 1;
931
932 full:
933 chk->data = sz;
934 return 0;
935}
936
937/* Appends the H1 representation of the status line <sl> to the chunk <chk>. It
938 * returns 1 if data are successfully appended, otherwise it returns 0.
939 */
940int h1_format_htx_stline(const struct htx_sl *sl, struct buffer *chk)
941{
942 size_t sz = chk->data;
943
944 if (HTX_SL_LEN(sl) + 4 > b_room(chk))
945 return 0;
946
947 if (sl->flags & HTX_SL_F_VER_11) {
948 if (!chunk_memcat(chk, "HTTP/1.1", 8))
949 goto full;
950 }
951 else {
952 if (!chunk_memcat(chk, HTX_SL_RES_VPTR(sl), HTX_SL_RES_VLEN(sl)))
953 goto full;
954 }
955 if (!chunk_memcat(chk, " ", 1) ||
956 !chunk_memcat(chk, HTX_SL_RES_CPTR(sl), HTX_SL_RES_CLEN(sl)) ||
957 !chunk_memcat(chk, " ", 1) ||
958 !chunk_memcat(chk, HTX_SL_RES_RPTR(sl), HTX_SL_RES_RLEN(sl)) ||
959 !chunk_memcat(chk, "\r\n", 2))
960 goto full;
961
962 return 1;
963
964 full:
965 chk->data = sz;
966 return 0;
967}
968
Ilya Shipitsin47d17182020-06-21 21:42:57 +0500969/* Appends the H1 representation of the header <n> with the value <v> to the
Christopher Faulet53a899b2019-10-08 16:38:42 +0200970 * chunk <chk>. It returns 1 if data are successfully appended, otherwise it
971 * returns 0.
972 */
973int h1_format_htx_hdr(const struct ist n, const struct ist v, struct buffer *chk)
974{
975 size_t sz = chk->data;
976
977 if (n.len + v.len + 4 > b_room(chk))
978 return 0;
979
980 if (!chunk_memcat(chk, n.ptr, n.len) ||
981 !chunk_memcat(chk, ": ", 2) ||
982 !chunk_memcat(chk, v.ptr, v.len) ||
983 !chunk_memcat(chk, "\r\n", 2))
984 goto full;
985
986 return 1;
987
988 full:
989 chk->data = sz;
990 return 0;
991}
992
993/* Appends the H1 representation of the data <data> to the chunk <chk>. If
994 * <chunked> is non-zero, it emits HTTP/1 chunk-encoded data. It returns 1 if
995 * data are successfully appended, otherwise it returns 0.
996 */
997int h1_format_htx_data(const struct ist data, struct buffer *chk, int chunked)
998{
999 size_t sz = chk->data;
1000
1001 if (chunked) {
1002 uint32_t chksz;
1003 char tmp[10];
1004 char *beg, *end;
1005
1006 chksz = data.len;
1007
1008 beg = end = tmp+10;
1009 *--beg = '\n';
1010 *--beg = '\r';
1011 do {
1012 *--beg = hextab[chksz & 0xF];
1013 } while (chksz >>= 4);
1014
1015 if (!chunk_memcat(chk, beg, end - beg) ||
1016 !chunk_memcat(chk, data.ptr, data.len) ||
1017 !chunk_memcat(chk, "\r\n", 2))
1018 goto full;
1019 }
1020 else {
1021 if (!chunk_memcat(chk, data.ptr, data.len))
1022 return 0;
1023 }
1024
1025 return 1;
1026
1027 full:
1028 chk->data = sz;
1029 return 0;
1030}
1031
Christopher Faulet4f0f88a2019-08-10 11:17:44 +02001032/*
1033 * Local variables:
1034 * c-indent-level: 8
1035 * c-basic-offset: 8
1036 * End:
1037 */